package com.emc.vipr.transform.compression; import java.io.IOException; import java.io.InputStream; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashMap; import java.util.Map; import java.util.zip.Deflater; import java.util.zip.DeflaterInputStream; import com.emc.vipr.transform.TransformConstants; import com.emc.vipr.transform.encryption.KeyUtils; import com.emc.vipr.transform.util.CountingInputStream; public class DeflateInputFilter extends InputStream implements CompressionStream { private CountingInputStream uncompressedCounter; private CountingInputStream compressedCounter; private DigestInputStream digester; private boolean closed; private byte[] uncompressedDigest; public DeflateInputFilter(InputStream in, int level) throws IOException { Deflater def = new Deflater(level); uncompressedCounter = new CountingInputStream(in); try { digester = new DigestInputStream(uncompressedCounter, MessageDigest.getInstance("SHA1")); } catch (NoSuchAlgorithmException e) { throw new IOException("Unable to initialize digest", e); } DeflaterInputStream deflateFilter = new DeflaterInputStream(digester, def); compressedCounter = new CountingInputStream(deflateFilter); } @Override public int read() throws IOException { return compressedCounter.read(); } @Override public int read(byte[] b, int off, int len) throws IOException { return compressedCounter.read(b, off, len); } @Override public int read(byte[] b) throws IOException { return compressedCounter.read(b); } @Override public void close() throws IOException { if(closed) return; closed = true; compressedCounter.close(); // Store digest (can only call this once). uncompressedDigest = digester.getMessageDigest().digest(); } @Override public int available() throws IOException { return compressedCounter.available(); } @Override public Map<String, String> getStreamMetadata() { if(!closed) { throw new IllegalStateException("Stream must be closed before getting metadata"); } Map<String,String> metadata = new HashMap<String, String>(); long compSize = compressedCounter.getByteCount(); long uncompSize = uncompressedCounter.getByteCount(); String compRatioString = String.format("%.1f%%", 100.0 - (compSize*100.0/uncompSize)); metadata.put(TransformConstants.META_COMPRESSION_UNCOMP_SIZE, ""+uncompSize); metadata.put(TransformConstants.META_COMPRESSION_COMP_SIZE, ""+compSize); metadata.put(TransformConstants.META_COMPRESSION_COMP_RATIO, ""+compRatioString); metadata.put(TransformConstants.META_COMPRESSION_UNCOMP_SHA1, KeyUtils.toHexPadded(uncompressedDigest)); return metadata; } }