Package com.emc.vipr.transform.compression

Source Code of com.emc.vipr.transform.compression.LZMACompressionFilter

package com.emc.vipr.transform.compression;

import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import SevenZip.Compression.LZMA.Encoder;

import com.emc.vipr.transform.TransformConstants;
import com.emc.vipr.transform.compression.CompressionTransformFactory.LzmaProfile;
import com.emc.vipr.transform.encryption.KeyUtils;
import com.emc.vipr.transform.util.CountingInputStream;

/**
* Compression filter used in "pull" mode to compress data for the
* @link {@link CompressionOutputTransform}.
*/
public class LZMACompressionFilter extends InputStream implements CompressionStream, Runnable {
    private static Logger log = LoggerFactory.getLogger(LZMACompressionFilter.class);
   
    private boolean closed = false;
    private Thread compressionThread;
    private PipedInputStream inputPipe;
    private PipedOutputStream outputPipe;
    private Encoder lzma;
    private CountingInputStream uncompressedSize;
    private CountingInputStream compressedSize;
    private DigestInputStream uncompressedDigest;
    private byte[] digest;
    private Exception compressionFailure;

    public LZMACompressionFilter(InputStream in, int compressionLevel) throws IOException {
        this(in, CompressionTransformFactory.LZMA_COMPRESSION_PROFILE[compressionLevel]);
    }
   
    public LZMACompressionFilter(InputStream in, LzmaProfile compressionProfile) throws IOException {
        closed = false;
        digest = new byte[0];
       
        // The LZMA Encoder reads from an input stream and writes to an output stream and
        // thus does not make a good filter.  We need to create a pipe and and use an
        // auxiliary thread to compress the data.
        //
        // Filter chain:
        // user stream -> CountingInputStream(uncompressedSize) -> DigestInputStream ->
        // Encoder -> PipedOutputStream -> PipedInputStream ->
        // CountingInputStream(compressedSize)
        uncompressedSize = new CountingInputStream(in);
        try {
            uncompressedDigest = new DigestInputStream(uncompressedSize,
                    MessageDigest.getInstance("SHA1"));
        } catch (NoSuchAlgorithmException e) {
           throw new IOException("Could not create LZMACompessionFilter", e);
        }
        inputPipe = new PipedInputStream();
        outputPipe = new PipedOutputStream(inputPipe);
       
        compressedSize = new CountingInputStream(inputPipe);
        lzma = new Encoder();
        lzma.SetDictionarySize(compressionProfile.dictionarySize);
        lzma.SetNumFastBytes(compressionProfile.fastBytes);
        lzma.SetMatchFinder(compressionProfile.matchFinder);
        lzma.SetLcLpPb(compressionProfile.lc, compressionProfile.lp, compressionProfile.pb);
        lzma.SetEndMarkerMode(true);
       
        // Write the compression settings to the stream (this is read during
        // decompression to configure the decoder)
        lzma.WriteCoderProperties(outputPipe);
       
        compressionThread = new Thread(LZMAOutputStream.LZ_COMP_TG, this);
       
        compressionThread.start();
    }

    @Override
    public int read() throws IOException {
        readCheck();
        return compressedSize.read();
    }
   
    @Override
    public int read(byte[] b) throws IOException {
        readCheck();
        return compressedSize.read(b);
    }
   
    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        readCheck();
        return compressedSize.read(b, off, len);
    }
   
    @Override
    public int available() throws IOException {
        readCheck();
        return compressedSize.available();
    }
   
    @Override
    public long skip(long n) throws IOException {
        readCheck();
        return compressedSize.skip(n);
    }
   
    @Override
    public void close() throws IOException {
        if(closed) return;
        closed = true;
       
        compressedSize.close();
       
        // Wait for encoder to finish
        try {
            compressionThread.join();
        } catch (InterruptedException e) {
            throw new IOException("Error waiting for compression thread to exit", e);
        }

        digest = uncompressedDigest.getMessageDigest().digest();
       
        // Free the encoder
        lzma = null;
    }

    @Override
    public void run() {
        // Start compressing data
        try {
            lzma.Code(uncompressedDigest, outputPipe, -1, -1, null);
        } catch(Exception e) {
            compressionFailure(e);
        }
       
        // Compression done.  Close output side of pipe before thread dies.
        try {
            outputPipe.close();
        } catch (IOException e) {
            compressionFailure(e);
        }

    }
   
    private synchronized void compressionFailure(Exception e) {
        compressionFailure = e;
        log.error("Error compressing data", e);
    }
   
    private synchronized void readCheck() throws IOException {
        if(compressionFailure != null) {
            throw new IOException("Error during stream compression", compressionFailure);
        }
        if(closed) {
            throw new IOException("Stream closed");
        }
    }

    @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 = compressedSize.getByteCount();
        long uncompSize = uncompressedSize.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(digest));
       
        return metadata;
    }

}
TOP

Related Classes of com.emc.vipr.transform.compression.LZMACompressionFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.