blob: 17139b7263087704b56373f4b356801216eb82ae [file] [log] [blame]
/*
*
* 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.
*
*/
package org.apache.flex.swf.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.flex.utils.DAByteArrayOutputStream;
import SevenZip.Compression.LZMA.Encoder;
public class LZMACompressor
{
public LZMACompressor()
{
// init the encoder to it is ready for work
encoder = new Encoder();
// Note: algorithm doesn't do anything at this time.
/// value 1 is supposed to be "max"
if (!encoder.SetAlgorithm(1))
assert false;
// Dictionary size
// This is the default value from the 7Zip example (1 << 21)
// It is not obvious that making it bigger will give better results. Be aware that the
// implementation seems to allocate 2X ints, so 8Xbytes.
int dictionarySize = 1 << 21;
if (!encoder.SetDictionarySize(dictionarySize))
assert false;
// set number of fast bytes - [5, 273], default: 128\n" +
if (!encoder.SetNumFastBytes(128))
assert false;
// -mf{MF_ID}: set Match Finder: [bt2, bt4], default: bt4\n" +
if (!encoder.SetMatchFinder(1))
assert false;
//" -lc{N}: set number of literal context bits - [0, 8], default: 3\n" +
//" -lp{N}: set number of literal pos bits - [0, 4], default: 0\n" +
//" -pb{N}: set number of pos bits - [0, 4], default: 2\n" +
int lc = 3;
int lp = 0;
int pb = 2;
if (!encoder.SetLcLpPb(lc, lp, pb))
assert false;
// set marker mode to true, so that we write out
// an explicit "end of stream" marker, as per the
// SFW LZMA spec
encoder.SetEndMarkerMode(true);
}
// We will compress all of our data into this data structure,
// so that we can easily stream it out later
private DAByteArrayOutputStream byteArrayOutputStream = null;
private final Encoder encoder;
/**
* This simple wrapper make an IOutputBitStream look like in InputSteam.
* LZMA library needs an Input stream, but our SWF infrastructure is already
* using IOutputBitStream.
*/
public static class StreamAdapter extends InputStream
{
public StreamAdapter(IOutputBitStream outputBitStream)
{
// Fetch the raw bytes from outputBitStream, and
// save them so we can serve them up later
bytes = outputBitStream.getBytes();
totalByteCount = outputBitStream.size();
}
private final byte[] bytes;
private final long totalByteCount;
private int position = 0;
/**
* Implemeint InputStream.read()
*/
@Override
public int read() throws IOException
{
int ret = -1;
if (position >= totalByteCount)
ret = -1;
else
{
ret = bytes[position++];
if (ret < 0)
ret += 256; // Java signed byte -> unsigned
}
return ret;
}
/**
* @return the number of bytes in the input stream
*/
long getCount()
{
return totalByteCount;
}
}
/**
* Compresses all of the data in outputBitStream into
* this.byteArrayOutputStream Must be called before any of the write
* methods.
*/
public void compress(IOutputBitStream outputBitStream) throws IOException
{
assert byteArrayOutputStream == null;
byteArrayOutputStream = new DAByteArrayOutputStream();
StreamAdapter is = new StreamAdapter(outputBitStream);
encoder.Code(is, byteArrayOutputStream, -1, -1, null);
}
/**
* Write the LZMA compression properties to the output. These are part the
* the SWF header
*
* @param outputStream The output stream.
*/
public void writeLZMAProperties(OutputStream outputStream) throws IOException
{
encoder.WriteCoderProperties(outputStream);
}
/**
* Write the actual compressed payload, and the EOS bytes at the end
*/
public void writeDataAndEnd(OutputStream outputStream) throws IOException
{
byte[] data = byteArrayOutputStream.getDirectByteArray();
outputStream.write(data, 0, data.length);
outputStream.flush();
}
/**
* @return the length of the LZMA data, and the final EOS marker
*/
public long getLengthOfCompressedPayload()
{
assert byteArrayOutputStream != null;
return byteArrayOutputStream.size();
}
}