package com.colloquial.arithcode; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.FileInputStream; import java.io.FilterInputStream; import java.io.*; /** <P>An input stream which uses a statistical model and arithmetic * coding for decompression of encoded bytes read from an underlying * input stream. Given a statistical model of a byte sequence, it * operates in the same way as * <code>java.util.zip.GZIPInputStream</code>. * * @author <a href="http://www.colloquial.com/carp/">Bob Carpenter</a> * @version 1.1 * @see ArithCodeOutputStream * @see ArithCodeModel * @since 1.0 */ public final class ArithCodeInputStream extends InputStream { /** Construct an arithmetic coded input stream from a specified * arithmetic decoder and a statistical model. * @param decoder Arithmetic decoder from which to read input events. * @param model Statistical model for arithmetic coding. * @throws IOException If there is an I/O exception in the underlying input stream. */ public ArithCodeInputStream(ArithDecoder decoder, ArithCodeModel model) throws IOException { _decoder = decoder; _model = model; } /** Construct an arithmetic coded input stream from a specified * bit input and a statistical model. * @param bitIn Bit input from which to read bits. * @param model Statistical model for arithmetic coding. * @throws IOException If there is an I/O exception in the underlying input stream. */ public ArithCodeInputStream(BitInput in, ArithCodeModel model) throws IOException { this(new ArithDecoder(in), model); } /** Construct an arithmetic coded input stream from a specified * buffered input stream and a statistical model. * @param in Buffered input stream from which to read coded bits. * @param model Statistical model for arithmetic coding. * @throws IOException If there is an I/O exception in the underlying input stream. */ public ArithCodeInputStream(BufferedInputStream in, ArithCodeModel model) throws IOException { this(new BitInput(in),model); } /** Construct an arithmetic coded input stream from a specified * input stream and a statistical model. * @param in Input stream from which to read coded bits. * @param model Statistical model for arithmetic coding. * @throws IOException If there is an I/O exception in the underlying input stream. */ public ArithCodeInputStream(InputStream in, ArithCodeModel model) throws IOException { this(new BufferedInputStream(in), model); } /** Closes this input stream. * @throws IOException If there is an exception closing the underlying input stream. */ public void close() throws IOException { _decoder.close(); } /** Not supported. */ public void mark(int readLimit) { } /** Returns <code>false</code> because marking is not supported. * @return <code>false</code>. */ public boolean markSupported() { return false; } /** Read an array of bytes into the specified byte array, returning * number of bytes read. * @param bs Byte array into which to read the bytes. * @return Number of bytes read. * @throws IOException If there is an I/O exception reading from the underlying stream. */ public int read(byte[] bs) throws IOException { return read(bs,0,bs.length); } /** Read the specified number of bytes into the array, beginning from the position * specified by the offset. Return the total number of bytes read. Will be less than * array length if the end of stream was encountered. * @param bs Byte array into which to read the bytes. * @param off Offset into byte array from which to begin writing output. * @param len Maximum number of bytes to read. * @return Number of bytes read. * @throws IOException If there is an I/O exception reading from the underlying stream. */ public int read(byte[] bs, int off, int len) throws IOException { for (int i = off; i < len; ++i) { int nextByte = read(); if (nextByte == -1) return (i - off); // eof, return length read bs[i] = (byte)nextByte; } return len > 0 ? len : 0; } /* Reads the next byte from the input stream. Returns -1 if end-of-stream * is encountered; otherwise result is given in the low order 8 bits of * the return value. * @return The next byte from the input stream or -1 if end of stream is encountered. * @throws IOException If there is an I/O exception reading from the underlying stream. */ public int read() throws IOException { int result = decodeNextByte(); if( result == -1 ) return -1; assert 0 <= result && result < 256 : result; return result; } /** Not supported. Throws an <code>IOException</code> if called. * @throws IOException whenever called. */ public void reset() throws IOException { throw new IOException("reset not supported in AdaptiveUnigramInputStream"); } /** Skips the given number of bytes from the input. * @param n Number of bytes to skip. * @return Number of bytes skipped. * @throws IOException If there is an I/O exception reading from the underlying stream. */ public long skip(long n) throws IOException { for (long i = 0; i < n; ++i) if (read() == -1) return i; return n; } /** The statistical model model on which the input stream is based. */ private final ArithCodeModel _model; /** The arithmetic decoder used to read bytes. */ private final ArithDecoder _decoder; /** The buffered next byte to write. If it's equal to -1, * the end of stream has been reached, otherwise next byte * is the low order bits. */ private boolean eof_p = false; /** Interval used for coding ranges. */ private final int[] _interval = new int[3]; /** Buffers the next byte into <code>_nextByte</code>. */ private int decodeNextByte() throws IOException { if (eof_p || _decoder.endOfStream()) { eof_p = true; return ArithCodeModel.EOF; } while (true) { int _nextByte = _model.pointToSymbol(_decoder.getCurrentSymbolCount(_model.totalCount())); _model.interval(_nextByte,_interval); _decoder.removeSymbolFromStream(_interval); if (_nextByte != ArithCodeModel.ESCAPE) return _nextByte; } } }