tikhomirov@530: /*
tikhomirov@530:  * Copyright (c) 2013 TMate Software Ltd
tikhomirov@530:  *  
tikhomirov@530:  * This program is free software; you can redistribute it and/or modify
tikhomirov@530:  * it under the terms of the GNU General Public License as published by
tikhomirov@530:  * the Free Software Foundation; version 2 of the License.
tikhomirov@530:  *
tikhomirov@530:  * This program is distributed in the hope that it will be useful,
tikhomirov@530:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
tikhomirov@530:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
tikhomirov@530:  * GNU General Public License for more details.
tikhomirov@530:  *
tikhomirov@530:  * For information on how to redistribute this software under
tikhomirov@530:  * the terms of a license other than GNU General Public License
tikhomirov@530:  * contact TMate Software at support@hg4j.com
tikhomirov@530:  */
tikhomirov@530: package org.tmatesoft.hg.internal;
tikhomirov@530: 
tikhomirov@530: import java.io.IOException;
tikhomirov@530: import java.util.zip.Deflater;
tikhomirov@534: 
tikhomirov@534: import org.tmatesoft.hg.core.SessionContext;
tikhomirov@534: import org.tmatesoft.hg.util.LogFacility.Severity;
tikhomirov@530: 
tikhomirov@530: /**
tikhomirov@530:  * 
tikhomirov@530:  * @author Artem Tikhomirov
tikhomirov@530:  * @author TMate Software Ltd.
tikhomirov@530:  */
tikhomirov@530: public class RevlogCompressor {
tikhomirov@534: 	private final SessionContext ctx;
tikhomirov@530: 	private final Deflater zip;
tikhomirov@534: 	private DataSerializer.DataSource sourceData;
tikhomirov@534: 	private int compressedLen;
tikhomirov@530: 	
tikhomirov@534: 	public RevlogCompressor(SessionContext sessionCtx) {
tikhomirov@534: 		ctx = sessionCtx;
tikhomirov@530: 		zip = new Deflater();
tikhomirov@530: 	}
tikhomirov@530: 
tikhomirov@534: 	public void reset(DataSerializer.DataSource source) {
tikhomirov@530: 		sourceData = source;
tikhomirov@534: 		compressedLen = -1;
tikhomirov@530: 	}
tikhomirov@530: 	
tikhomirov@534: 	// out stream is not closed!
tikhomirov@534: 	public int writeCompressedData(DataSerializer out) throws IOException {
tikhomirov@530: 		zip.reset();
tikhomirov@534: 		DeflaterDataSerializer dds = new DeflaterDataSerializer(out, zip, sourceData.serializeLength());
tikhomirov@534: 		sourceData.serialize(dds);
tikhomirov@534: 		dds.finish();
tikhomirov@530: 		return zip.getTotalOut();
tikhomirov@530: 	}
tikhomirov@530: 
tikhomirov@534: 	public int getCompressedLength() {
tikhomirov@534: 		if (compressedLen != -1) {
tikhomirov@534: 			return compressedLen;
tikhomirov@530: 		}
tikhomirov@534: 		Counter counter = new Counter();
tikhomirov@534: 		try {
tikhomirov@534: 			compressedLen = writeCompressedData(counter);
tikhomirov@534: 			assert counter.totalWritten == compressedLen;
tikhomirov@534: 	        return compressedLen;
tikhomirov@534: 		} catch (IOException ex) {
tikhomirov@534: 			// can't happen provided we write to our stream that does nothing but byte counting
tikhomirov@534: 			ctx.getLog().dump(getClass(), Severity.Error, ex, "Failed estimating compressed length of revlog data");
tikhomirov@534: 			return counter.totalWritten; // use best known value so far
tikhomirov@534: 		}
tikhomirov@534: 	}
tikhomirov@534: 
tikhomirov@534: 	private static class Counter extends DataSerializer {
tikhomirov@534: 		public int totalWritten = 0;
tikhomirov@534: 
tikhomirov@534: 		public void writeByte(byte... values) throws IOException {
tikhomirov@534: 			totalWritten += values.length;
tikhomirov@534: 		}
tikhomirov@534: 
tikhomirov@534: 		public void writeInt(int... values) throws IOException {
tikhomirov@534: 			totalWritten += 4 * values.length;
tikhomirov@534: 		}
tikhomirov@534: 
tikhomirov@534: 		public void write(byte[] data, int offset, int length) throws IOException {
tikhomirov@534: 			totalWritten += length;
tikhomirov@534: 		}
tikhomirov@530: 	}
tikhomirov@530: }