package com.emc.vipr.transform.compression;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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.TransformException;
import com.emc.vipr.transform.TransformConstants.CompressionMode;
import com.emc.vipr.transform.TransformFactory;
public class CompressionTransformFactory extends
TransformFactory<CompressionOutputTransform, CompressionInputTransform> {
private static final Logger logger = LoggerFactory.getLogger(CompressionTransformFactory.class);
public CompressionMode compressMode = TransformConstants.DEFAULT_COMPRESSION_MODE;
public int compressionLevel = TransformConstants.DEFAULT_COMPRESSION_LEVEL;
public CompressionTransformFactory() {
setPriority(1000);
}
public CompressionMode getCompressMode() {
return compressMode;
}
public void setCompressMode(CompressionMode compressMode) {
this.compressMode = compressMode;
}
public int getCompressionLevel() {
return compressionLevel;
}
public void setCompressionLevel(int compressionLevel) {
this.compressionLevel = compressionLevel;
}
@Override
public CompressionOutputTransform getOutputTransform(
OutputStream streamToEncodeTo, Map<String, String> metadataToEncode) throws IOException {
switch(compressMode) {
case Deflate:
return new DeflateOutputTransform(streamToEncodeTo, metadataToEncode, compressionLevel);
case LZMA:
return new LZMAOutputTransform(streamToEncodeTo, metadataToEncode, compressionLevel);
default:
throw new IllegalArgumentException("Unsupported compression method " + compressMode);
}
}
@Override
public CompressionOutputTransform getOutputTransform(
InputStream streamToEncode, Map<String, String> metadataToEncode)
throws IOException, TransformException {
switch(compressMode) {
case Deflate:
return new DeflateOutputTransform(streamToEncode, metadataToEncode, compressionLevel);
case LZMA:
return new LZMAOutputTransform(streamToEncode, metadataToEncode, compressionLevel);
default:
throw new IllegalArgumentException("Unsupported compression method " + compressMode);
}
}
@Override
public CompressionInputTransform getInputTransform(String transformConfig, InputStream streamToDecode, Map<String, String> metadata) throws IOException {
String[] transformTuple = splitTransformConfig(transformConfig);
if(!TransformConstants.COMPRESSION_CLASS.equals(transformTuple[0])) {
throw new IllegalArgumentException("Unsupported transform class: " + transformTuple[0]);
}
// Decode mode
String[] configParams = transformTuple[1].split("/");
if(configParams.length < 1) {
throw new IllegalArgumentException("Could not decode configuration: " + configParams);
}
// First arg is mode. Others are compression config and informational only.
CompressionMode mode = CompressionMode.valueOf(configParams[0]);
switch(mode) {
case Deflate:
return new DeflateInputTransform(streamToDecode, metadata);
case LZMA:
return new LZMAInputTransform(streamToDecode, metadata);
default:
throw new IllegalArgumentException("Unknown compression method " + mode);
}
}
@Override
public String getTransformClass() {
return TransformConstants.COMPRESSION_CLASS;
}
/**
* Checks whether this class can decode the given transformation configuration.
* @param transformClass the transformation class to check, e.g. "COMP"
* @param config the configuration for the transformation, e.g. "LZMA/9"
* @param metadata the additional metadata from the object in case additional fields
* need to be checked.
* @return true if this factory can decode the given object stream.
*/
public boolean canDecode(String transformClass, String config, Map<String,String> metadata) {
// null?
if(config == null) {
logger.warn("Configuration string null");
return false;
}
// Decode mode
String[] configParams = config.split("/");
if(configParams.length < 1) {
logger.warn("Could not decode config string {}", config);
return false;
}
// First arg is mode. Others are compression config and informational only.
try {
CompressionMode.valueOf(configParams[0]);
} catch(IllegalArgumentException e) {
logger.warn("Invalid compression mode {}", configParams[0]);
return false;
}
return getTransformClass().equals(transformClass);
}
/**
* Map LZMA compression parameters into the standard 0-9 compression levels.
*/
public static LzmaProfile LZMA_COMPRESSION_PROFILE[] = {
new LzmaProfile(16*1024, 5, Encoder.EMatchFinderTypeBT2), // 0
new LzmaProfile(64*1024, 64, Encoder.EMatchFinderTypeBT2), // 1
new LzmaProfile(512*1024, 128, Encoder.EMatchFinderTypeBT2), // 2
new LzmaProfile(1024*1024, 128, Encoder.EMatchFinderTypeBT2), // 3
new LzmaProfile(8*1024*1024, 128, Encoder.EMatchFinderTypeBT2), // 4
new LzmaProfile(16*1024*1024, 128, Encoder.EMatchFinderTypeBT2), // 5
new LzmaProfile(24*1024*1024, 192, Encoder.EMatchFinderTypeBT2), // 6
new LzmaProfile(32*1024*1024, 224, Encoder.EMatchFinderTypeBT4), // 7
new LzmaProfile(48*1024*1024, 256, Encoder.EMatchFinderTypeBT4), // 8
new LzmaProfile(64*1024*1024, 273, Encoder.EMatchFinderTypeBT4) // 9
};
public static long memoryRequiredForLzma(int compressionLevel) {
return memoryRequiredForLzma(LZMA_COMPRESSION_PROFILE[compressionLevel]);
}
public static long memoryRequiredForLzma(LzmaProfile profile) {
return (long)(profile.dictionarySize * 11.5);
}
public static class LzmaProfile {
int dictionarySize;
int fastBytes;
int matchFinder;
int lc;
int lp;
int pb;
public LzmaProfile(int dictionarySize, int fastBytes, int matchFinder) {
this(dictionarySize, fastBytes, matchFinder, 3, 0, 2);
}
public LzmaProfile(int dictionarySize, int fastBytes, int matchFinder, int lc, int lp, int pb) {
this.dictionarySize = dictionarySize;
this.fastBytes = fastBytes;
this.matchFinder = matchFinder;
this.lc = lc;
this.lp = lp;
this.pb = pb;
}
}
}