package com.colloquial.arithcode;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
/** <P>A filter output stream which uses a statistical model and
* arithmetic coding for compression of bytes read from an underlying
* arithmetic encoder. This encoder may be constructed from an output
* stream or bit output. Given a model and a stream, this class
* operates in the same way as
* <code>java.util.zip.GZIPOutputStream</code>.
*
* @author <a href="http://www.colloquial.com/carp/">Bob Carpenter</a>
* @version 1.1
* @see ArithCodeInputStream
* @see ArithCodeModel
* @since 1.0
*/
public class ArithCodeOutputStream extends OutputStream {
/** Construct an output stream that writes to the specified output
* events with the given arithmetic encoder with the given statistical model.
* @param encoder Arithmetic encoder to use for coding output.
* @param model Statistical model of byte stream.
* @since 1.1
*/
public ArithCodeOutputStream(ArithEncoder encoder, ArithCodeModel model) {
_encoder = encoder;
_model = model;
}
/** Construct an output stream that writes to the specified bit output
* using arithmetic coding with the given statistical model.
* @param bitOut Bit output to write coded bits to.
* @param model Statistical model of byte stream.
* @since 1.1
*/
public ArithCodeOutputStream(BitOutput bitOut, ArithCodeModel model) {
this(new ArithEncoder(bitOut), model);
}
/** Construct an output stream that writes to the specified buffered output
* stream using arithmetic coding with the given statistical model.
* @param model Statistical model of byte stream.
* @param out Buffered output stream to write coded bits to.
* @since 1.1
*/
public ArithCodeOutputStream(BufferedOutputStream out, ArithCodeModel model) {
this(new ArithEncoder(out), model);
}
/** Construct an output stream that writes to the specified output
* stream using arithmetic coding with the given statistical model.
* @param output Output stream to write coded bits to.
* @param model Statistical model of byte stream.
*/
public ArithCodeOutputStream(OutputStream out, ArithCodeModel model) {
this(new BufferedOutputStream(out), model);
}
/** Close this output stream.
* @throws IOException If there is an exception in the underlying encoder.
*/
public void close() throws IOException {
encode(ArithCodeModel.EOF); // must code EOF to allow decoding to halt
_encoder.close();
}
/** Flushes underlying stream.
* @throws IOException If there is an exception flushing the underlying stream.
*/
public void flush() throws IOException {
_encoder.flush();
}
/** Writes array of bytes to the output stream.
* @param bs Array of bytes to write.
* @throws IOException If there is an exception in writing to the underlying encoder.
*/
public void write(byte[] bs) throws IOException {
write(bs,0,bs.length);
}
/** Writes section of array of bytes to the output stream.
* @param bs Array of bytes to write.
* @param off Index from which to start writing.
* @param len Number of bytes to write.
* @throws IOException If there is an exception in writing to the underlying encoder.
*/
public void write(byte[] bs, int off, int len) throws IOException {
while (off < len) write(bs[off++]);
}
/** Writes the eight low-order bits of argument to the output stream
* as a byte.
* @param i Bits to write.
* @throws IOException If there is an exception in writing to the underlying encoder.
*/
public void write(int i) throws IOException {
if( i < 0 ) i += 256;
assert 0 <= i && i < 256;
encode(i);
}
/** The model on which the output stream is based.
*/
private final ArithCodeModel _model;
/** The arithmetic encoder used to write coded bytes.
*/
private final ArithEncoder _encoder;
/** Interval used for coding ranges.
*/
private final int[] _interval = new int[3];
/** Writes encoded symbol after necessary escapes to the underlying
* encoder.
* @param symbol Symbol to encode.
* @throws IOException If the underlying encoder throws an IOException.
*/
private void encode(int symbol) throws IOException {
while (_model.escaped(symbol)) {
_model.interval(ArithCodeModel.ESCAPE,_interval); // have already done complete walk to compute escape
_encoder.encode(_interval);
}
_model.interval(symbol,_interval); // have already done walk to element to compute escape
_encoder.encode(_interval);
}
}