All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.milton.zsync.MetaFileReader Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/* MetafileReader.java

MetafileReader: Metafile reader class
Copyright (C) 2011 Tomáš Hlavnička 

This file is a part of Jazsync.

Jazsync is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

Jazsync is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with Jazsync; if not, write to the

Free Software Foundation, Inc.,
59 Temple Place, Suite 330,
Boston, MA  02111-1307
USA
 */
package io.milton.zsync;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import io.milton.zsync.HeaderMaker.Headers;
import io.milton.zsync.MetaFileMaker.MetaData;
import java.io.FileNotFoundException;


/**
 * Class used to read metafile
 * @author Tomáš Hlavnička
 */
public class MetaFileReader {

	private ChainingHash hashtable;
	private int fileOffset;
	private final int blockNum;
	
	/** Variables for header information from .zsync metafile */
	//------------------------------
	private Headers headers;


	public MetaFileReader(File metafile) {
		readMetaFile(metafile);
		blockNum = (int) Math.ceil((double) headers.length / (double) headers.blocksize);
		readChecksums(metafile) ;	
	}

	public MetaFileReader(MetaData metaData) {
		this.headers = metaData.getHeaders();		
		blockNum = (int) Math.ceil((double) headers.length / (double) headers.blocksize);
		fillHashTable(metaData.getChecksums());
		
	}

	/**
	 * Parsing method for metafile headers, saving each value into separate variable.
	 * @param s String containing metafile
	 * @return Boolean value notifying whether header ended or not (true = end of header)
	 */
	private boolean parseHeader(String s) {
		String subs;
		int colonIndex;
		if (s.equals("")) {
			//timto prazdnym radkem skoncil header, muzeme prestat cist
			return true;
		}
		colonIndex = s.indexOf(":");
		subs = s.substring(0, colonIndex);
		if (subs.equalsIgnoreCase("zsync")) {
			headers.version = s.substring(colonIndex + 2);
			//zkontrolujeme kompatibilitu
			if (headers.version.equals("0.0.4") || headers.version.equals("0.0.2")) {
				throw new RuntimeException("This version is not compatible with zsync streams in versions up to 0.0.4");
			}
		} else if (subs.equalsIgnoreCase("Blocksize")) {
			headers.blocksize = Integer.parseInt(s.substring(colonIndex + 2));
		} else if (subs.equalsIgnoreCase("Length")) {
			headers.length = Long.parseLong(s.substring(colonIndex + 2));
		} else if (subs.equalsIgnoreCase("Hash-Lengths")) {
			int comma = s.indexOf(",");
			int seqNum = Integer.parseInt(s.substring((colonIndex + 2), comma));
			headers.setSeqNum(seqNum);
			int nextComma = s.indexOf(",", comma + 1);
			headers.setRsumBytes( Integer.parseInt(s.substring(comma + 1, nextComma)) );
			headers.setChecksumBytes( Integer.parseInt(s.substring(nextComma + 1)) );
			if ((headers.getSeqNum() < 1 || headers.getSeqNum() > 2)
					|| (headers.getRsumButes() < 1 || headers.getRsumButes() > 4)
					|| (headers.getChecksumBytes() < 3 || headers.getChecksumBytes() > 16)) {
				throw new RuntimeException("Nonsensical hash lengths line " + s.substring(colonIndex + 2));
			}

		} else if (subs.equalsIgnoreCase("URL")) {
			headers.url = s.substring(colonIndex + 2);
		} else if (subs.equalsIgnoreCase("Z-URL")) {
			//not implemented yet
		} else if (subs.equalsIgnoreCase("SHA-1")) {
			headers.sha1 = s.substring(colonIndex + 2);
		} else if (subs.equalsIgnoreCase("Z-Map2")) {
			//not implemented yet
		}
		return false;
	}

	/**
	 * Method reads metafile from file and reads
	 * it line by line, sending line String to parser.
	 */
	private void readMetaFile(File metafile) {
		headers = new Headers(); 
		try {
			BufferedReader in = new BufferedReader(new FileReader(metafile));
			String s;
			while ((s = in.readLine()) != null) {
				if (parseHeader(s)) {
					break;
				}
			}
			in.close();
		} catch(FileNotFoundException e) {
			throw new RuntimeException("File not found: " + metafile.getAbsolutePath(), e);
		} catch (IOException e) {
			throw new RuntimeException("IO problem in metafile header reading", e);
		}
	}

	/**
	 * Method that reads metafile from file and stores its content into byte array
	 * and saves offset where headers end and blocksums starts.
	 */
	private void readChecksums(File metafile) {
		long length = metafile.length();
		if (metafile.length() > Integer.MAX_VALUE) {
			throw new RuntimeException("Metafile is too large");
		}
		byte[] bytes = new byte[(int) length];

		try {
			InputStream is = new FileInputStream(metafile);
			int offset = 0;
			int n = 0;
			while (offset < bytes.length && (n = is.read(bytes, offset, bytes.length - offset)) >= 0) {
				offset += n;
			}

			// Presvedcime se, ze jsme precetli cely soubor
			if (offset < bytes.length) {
				throw new IOException("Could not completely read file " + metafile.getName());
			} 

			is.close();
		} catch (IOException e) {
			throw new RuntimeException("IO problem in metafile reading", e);
		}
		// urci offset, kde konci hlavicka a zacinaji kontrolni soucty
		fileOffset = 0;
		for (int i = 2; i < bytes.length; i++) {
			if (bytes[i - 2] == 10 && bytes[i - 1] == 10) {
				fileOffset = i;
				break;
			}
		}
		fillHashTable(bytes);
	}

	private void fillHashTable(List list) {
		int i = 16;
		//spocteme velikost hashtable podle poctu bloku dat
		while ((2 << (i - 1)) > blockNum && i > 4) {
			i--;
		}
		//vytvorime hashtable o velikosti 2^i (max. 2^16, min. 2^4)
		hashtable = new ChainingHash(2 << (i - 1));
		for( ChecksumPair pair : list ) {
			hashtable.insert(pair);
		}
	}
	
	/**
	 * Fills a chaining hash table with ChecksumPairs
	 * @param checksums Byte array with bytes of whole metafile
	 */
	private void fillHashTable(byte[] checksums) {
		int i = 16;
		//spocteme velikost hashtable podle poctu bloku dat
		while ((2 << (i - 1)) > blockNum && i > 4) {
			i--;
		}
		//vytvorime hashtable o velikosti 2^i (max. 2^16, min. 2^4)
		hashtable = new ChainingHash(2 << (i - 1));
		ChecksumPair p = null;
		//Link item;
		int offset = 0;
		int weakSum = 0;
		int seq = 0;
		int off = fileOffset;

		byte[] weak = new byte[4];
		byte[] strongSum = new byte[headers.getChecksumBytes()];

		while (seq < blockNum) {

			for (int w = 0; w < headers.getRsumButes(); w++) {
				weak[w] = checksums[off];
				off++;
			}

			for (int s = 0; s < strongSum.length; s++) {
				strongSum[s] = checksums[off];
				off++;
			}

			weakSum = 0;
			weakSum += (weak[2] & 0x000000FF) << 24;
			weakSum += (weak[3] & 0x000000FF) << 16;
			weakSum += (weak[0] & 0x000000FF) << 8;
			weakSum += (weak[1] & 0x000000FF);

			p = new ChecksumPair(weakSum, strongSum.clone(), offset, headers.blocksize, seq);
			offset += headers.blocksize;
			seq++;
			//item = new Link(p);
			hashtable.insert(p);
		}
	}

	/**
	 * Returns hash table cotaining block checksums
	 * @return Hash table
	 */
	public ChainingHash getHashtable() {
		return hashtable;
	}

	/**
	 * Returns number of blocks in complete file
	 * @return Number of blocks
	 */
	public int getBlockCount() {
		return blockNum;
	}

	/**
	 * Returns size of block
	 * @return Size of the data block
	 */
	public int getBlocksize() {
		return headers.blocksize;
	}

	/**
	 * Length of used strong sum
	 * @return Length of strong sum
	 */
	public int getChecksumBytes() {
		return headers.getChecksumBytes();
	}

	/**
	 * Returns length of complete file
	 * @return Length of the file
	 */
	public long getLength() {
		return headers.length;
	}


	/**
	 * Length of used weak sum
	 * @return Length of weak sum
	 */
	public int getRsumBytes() {
		return headers.getRsumButes();
	}

	/**
	 * Number of consequence blocks
	 * @return Number of consequence blocks
	 */
	public int getSeqNum() {
		return headers.getSeqNum();
	}

	/**
	 * Returns SHA1sum of complete file
	 * @return String containing SHA1 sum of complete file
	 */
	public String getSha1() {
		return headers.sha1;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy