// package net.sf.zipme; /** * This class allows us to retrieve a specified amount of bits from * the input buffer, as well as copy big byte blocks. * It uses an int buffer to store up to 31 bits for direct * manipulation. This guarantees that we can get at least 16 bits, * but we only need at most 15, so this is all safe. * There are some optimizations in this class, for example, you must * never peek more then 8 bits more than needed, and you must first * peek bits before you may drop them. This is not a general purpose * class but optimized for the behaviour of the Inflater. * @author John Leuner, Jochen Hoenicke */ class StreamManipulator { private byte[] window; private int window_start=0; private int window_end=0; private int buffer=0; private int bits_in_buffer=0; /** * Get the next n bits but don't increase input pointer. n must be * less or equal 16 and if you if this call succeeds, you must drop * at least n-8 bits in the next call. * @return the value of the bits, or -1 if not enough bits available. */ public final int peekBits( int n){ if (bits_in_buffer < n) { if (window_start == window_end) return -1; buffer|=(window[window_start++] & 0xff | (window[window_start++] & 0xff) << 8) << bits_in_buffer; bits_in_buffer+=16; } return buffer & ((1 << n) - 1); } public final void dropBits( int n){ buffer>>>=n; bits_in_buffer-=n; } /** * Gets the next n bits and increases input pointer. This is equivalent * to peekBits followed by dropBits, except for correct error handling. * @return the value of the bits, or -1 if not enough bits available. */ public final int getBits( int n){ int bits=peekBits(n); if (bits >= 0) dropBits(n); return bits; } /** * Gets the number of bits available in the bit buffer. This must be * only called when a previous peekBits() returned -1. * @return the number of bits available. */ public final int getAvailableBits(){ return bits_in_buffer; } /** * Gets the number of bytes available. * @return the number of bytes available. */ public final int getAvailableBytes(){ return window_end - window_start + (bits_in_buffer >> 3); } /** * Skips to the next byte boundary. */ public void skipToByteBoundary(){ buffer>>=(bits_in_buffer & 7); bits_in_buffer&=~7; } public final boolean needsInput(){ return window_start == window_end; } public int copyBytes( byte[] output, int offset, int length){ if (length < 0) throw new IllegalArgumentException("length negative"); if ((bits_in_buffer & 7) != 0) throw new IllegalStateException("Bit buffer is not aligned!"); int count=0; while (bits_in_buffer > 0 && length > 0) { output[offset++]=(byte)buffer; buffer>>>=8; bits_in_buffer-=8; length--; count++; } if (length == 0) return count; int avail=window_end - window_start; if (length > avail) length=avail; System.arraycopy(window,window_start,output,offset,length); window_start+=length; if (((window_start - window_end) & 1) != 0) { buffer=(window[window_start++] & 0xff); bits_in_buffer=8; } return count + length; } public StreamManipulator(){ } public void reset(){ window_start=window_end=buffer=bits_in_buffer=0; } public void setInput( byte[] buf, int off, int len){ if (window_start < window_end) throw new IllegalStateException("Old input was not completely processed"); int end=off + len; if (0 > off || off > end || end > buf.length) throw new ArrayIndexOutOfBoundsException(); if ((len & 1) != 0) { buffer|=(buf[off++] & 0xff) << bits_in_buffer; bits_in_buffer+=8; } window=buf; window_start=off; window_end=end; } }