modules/io/src/main/java/net/multiphasicapps/io/InflaterInputStream.java
// -*- Mode: Java; indent-tabs-mode: t; tab-width: 4 -*-
// ---------------------------------------------------------------------------
// SquirrelJME
// Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
// ---------------------------------------------------------------------------
// SquirrelJME is under the Mozilla Public License Version 2.0.
// See license.mkd for licensing and copyright information.
// ---------------------------------------------------------------------------
package net.multiphasicapps.io;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.NoSuchElementException;
/**
* This is used to decompress standard deflate compressed stream.
*
* Associated standards: https://www.ietf.org/rfc/rfc1951.txt.
*
* This class is not thread safe.
*
* @since 2017/02/24
*/
public class InflaterInputStream
extends DecompressionInputStream
{
/** The size of the sliding window. */
private static final int _DEFAULT_SLIDING_WINDOW_SIZE =
32768;
/** No compression. */
static final byte _TYPE_NO_COMPRESSION =
0b00;
/** Fixed huffman table compression. */
static final byte _TYPE_FIXED_HUFFMAN =
0b01;
/** Dynamic huffman table compression. */
static final byte _TYPE_DYNAMIC_HUFFMAN =
0b10;
/** An error. */
static final byte _TYPE_ERROR =
0b11;
/** The maximum number of bits in the code length tree. */
private static final byte _MAX_BITS =
15;
/** Shuffled bit values when reading values. */
private static final byte[] _SHUFFLE_BITS =
new byte[]
{
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
};
/** The deflated compressed stream to be decompressed. */
protected final InputStream in;
/** Sliding window for accessing old bytes. */
protected final SlidingByteWindow window;
/** If the output cannot be filled, bytes are written here instead. */
protected final ByteDeque overflow =
new ByteDeque();
/** When bytes are read, a checkum will be calculated for it, optional. */
protected final Checksum checksum;
/** Single byte read. */
private final byte[] _solo =
new byte[1];
/** The read-in buffer which is used to bulk read input bytes. */
private final byte[] _readin =
new byte[4];
/** The bit source for reading. */
private final BitSource _bitsource =
new __BitSource__();
/**
* Raw code lengths (allocated once), the size is the max code length
* count.
*/
private final int[] _rawcodelens =
new int[19];
/**
* Raw literal and distances (allocated once), the size is the total of
* both the maximum length count and distance count.
*/
private final int[] _rawlitdistlens =
new int[322];
/** Used to store bit length counts. */
private final int[] _blcount =
new int[InflaterInputStream._MAX_BITS + 1];
/** Used to store the next code. */
private final int[] _nextcode =
new int[InflaterInputStream._MAX_BITS + 1];
/** The number of compressed bytes. */
private long _compressedsize;
/** The number of uncompressed bytes. */
private long _uncompressedsize;
/** The code length tree. */
private Reference<HuffmanTreeInt> _codelentree;
/** The literal tree. */
private Reference<HuffmanTreeInt> _literaltree;
/** The distance tree. */
private Reference<HuffmanTreeInt> _distancetree;
/** Window reader. */
private Reference<byte[]> _readwindow;
/**
* The miniature read window, it stores a 32-bit value and is given input
* bytes to read along with being used as output. This is an int because it
* is faster to work with integer values rather than bytes. It also means
* that it is much simpler to work with.
*/
private int _miniwindow;
/** Represents the number of bits in the mini window. */
private int _minisize;
/** The output write window, this is used to shift out writes as needed. */
private int _writewindow;
/** The number of bits in the write window. */
private int _writesize;
/** EOF has been reached? */
private boolean _eof;
/** The target byte array for writes. */
private byte[] _targ;
/** The target offset for writes. */
private int _targoff;
/** The target end offset for writes. */
private int _targend;
/**
* Initializes the deflate compression stream inflater.
*
* @param __in The stream to inflate.
* @throws NullPointerException On null arguments.
* @since 2017/02/24
*/
public InflaterInputStream(InputStream __in)
throws NullPointerException
{
this(__in, InflaterInputStream._DEFAULT_SLIDING_WINDOW_SIZE);
}
/**
* Initializes the deflate compression stream inflater, an optional
* checksum calculator may be specified also.
*
* @param __in The stream to inflate.
* @param __cs The checksum.
* @throws NullPointerException On null arguments, except for {@code __cs}.
* @since 2017/02/24
*/
public InflaterInputStream(InputStream __in, Checksum __cs)
throws NullPointerException
{
this(__in, InflaterInputStream._DEFAULT_SLIDING_WINDOW_SIZE, __cs);
}
/**
* Initializes the deflate compression stream inflater with a custom
* size specified for the sliding window.
*
* @param __in The stream to inflate.
* @param __sls Custom size to the sliding window.
* @throws NullPointerException On null arguments.
* @since 2017/03/04
*/
public InflaterInputStream(InputStream __in, int __sls)
{
this(__in, __sls, null);
}
/**
* Initializes the deflate compression stream inflater with a custom
* size specified for the sliding window, an optional checksum calculator
* may be specified also.
*
* @param __in The stream to inflate.
* @param __sls Custom size to the sliding window.
* @param __checksum If not {@code null} then when bytes are read from this
* stream they will have their checksum calculated. The checksum is
* calculated on the uncompressed bytes.
* @throws NullPointerException On null arguments, except for
* {@code __checksum}.
* @since 2017/08/22
*/
public InflaterInputStream(InputStream __in, int __sls,
Checksum __checksum)
{
// Check
if (__in == null)
throw new NullPointerException("NARG");
// Set
this.in = __in;
this.window = new SlidingByteWindow(__sls);
this.checksum = __checksum;
}
/**
* {@inheritDoc}
* @since 2017/02/24
*/
@Override
public int available()
throws IOException
{
// Use the number of bytes that are able to be read quickly without
// requiring decompression
return this.overflow.available();
}
/**
* {@inheritDoc}
* @since 2017/02/24
*/
@Override
public void close()
throws IOException
{
// Close input
this.in.close();
}
/**
* {@inheritDoc}
* @since 2017/08/22
*/
@Override
public long compressedBytes()
{
return this._compressedsize;
}
/**
* {@inheritDoc}
* @since 2017/08/22
*/
@Override
public boolean detectsEOF()
{
return true;
}
/**
* {@inheritDoc}
* @since 2017/02/24
*/
@Override
public int read()
throws IOException
{
// Try reading a single byte
byte[] solo = this._solo;
for (;;)
{
int rv = this.read(solo, 0, 1);
// EOF?
if (rv < 0)
return rv;
// Try again
else if (rv == 0)
continue;
// Return that byte
else
return (solo[0] & 0xFF);
}
}
/**
* {@inheritDoc}
* @since 2018/11/11
*/
@Override
public int read(byte[] __b)
throws IOException, NullPointerException
{
return this.read(__b, 0, __b.length);
}
/**
* {@inheritDoc}
* @since 2017/02/24
*/
@Override
public int read(byte[] __b, int __o, int __l)
throws ArrayIndexOutOfBoundsException, IOException,
NullPointerException
{
// Check
if (__b == null)
throw new NullPointerException("NARG");
int bl = __b.length;
if (__o < 0 || __l < 0 || (__o + __l) > bl)
throw new ArrayIndexOutOfBoundsException("AIOB");
// If there are bytes in the overflow buffer, read them first into the
// output because they are the result of previous decompression.
ByteDeque overflow = this.overflow;
int ovn = overflow.available(),
ovr = (Math.min(ovn, __l));
int c = overflow.removeFirst(__b, __o, __l);
// More bytes can be read from the input compressed data because the
// overflow buffer has been emptied
boolean eof = this._eof;
if (!eof && c < __l)
{
// Store write information
this._targ = __b;
// Try to fit as many bytes as possible into the output
while (c < __l)
{
// Decompress
int base;
this._targoff = (base = __o + c);
this._targend = base + (__l - c);
int rv = this.__decompress();
// Ended?
if (rv < 0)
{
this._eof = true;
break;
}
// Otherwise add those bytes
c += rv;
}
}
// Calculate CRC for this output data
Checksum checksum = this.checksum;
if (checksum != null)
checksum.offer(__b, __o, c);
// Count uncompressed size
if (c > 0)
this._uncompressedsize += c;
// Return the read count or end of file if the end of the stream has
// been reached
// But never leave bytes waiting in the overflow buffer ever
return (c == 0 && eof && overflow.isEmpty() ? -1 : c);
}
/**
* {@inheritDoc}
* @since 2017/08/22
*/
@Override
public long uncompressedBytes()
{
return this._uncompressedsize;
}
/**
* Reads the input and performs decompression on the data.
*
* @return The number of stored bytes or a negative value if the stream
* has terminated.
* @throws IOException On read or decompression errors.
* @since 2017/02/25
*/
private int __decompress()
throws IOException
{
// Do nothing on EOF
if (this._eof)
return -1;
// The target offset on entry
int enteroff = this._targoff;
// Read the final bit which determines if this is the last block
int finalhit = this.__readBits(1, false);
// Read the window type
int type = this.__readBits(2, false);
switch (type)
{
// None
case InflaterInputStream._TYPE_NO_COMPRESSION:
this.__decompressNone();
break;
// Fixed huffman
case InflaterInputStream._TYPE_FIXED_HUFFMAN:
this.__decompressFixed();
break;
// Dynamic huffman
case InflaterInputStream._TYPE_DYNAMIC_HUFFMAN:
this.__decompressDynamic();
break;
// Error or unknown
case InflaterInputStream._TYPE_ERROR:
default:
/* {@squirreljme.error BD17 Unknown type or the error type
was reached. (The type code used in the stream)} */
throw new IOException(String.format("BD17 %d", type));
}
// If this was the last block to read, then return EOF if no data
// was actually read, but mark EOF otherwise
int rv = (this._targoff - enteroff);
if (finalhit != 0)
{
this._eof = true;
return (rv == 0 ? -1 : rv);
}
// Just the read count
else
return rv;
}
/**
* Decompress dynamic huffman code.
*
* @throws IOException On read or decompression errors.
* @since 2017/02/25
*/
private void __decompressDynamic()
throws IOException
{
// Read the code length parameters
int dhlit = this.__readBits(5, false) + 257;
int dhdist = this.__readBits(5, false) + 1;
int dhclen = this.__readBits(4, false) + 4;
// Read the code length tree
HuffmanTreeInt codelentree = this.__decompressDynamicLoadLenTree(dhclen);
// Read the literal and distance trees
HuffmanTreeInt literaltree = this.__obtainLiteralTree(),
distancetree = this.__obtainDistanceTree();
this.__decompressDynamicLoadLitDistTree(codelentree, dhlit, dhdist,
literaltree, distancetree);
// Decode input
for (;;)
{
// Read code
int code = literaltree.getValue(this._bitsource);
// Literal byte value
if (code >= 0 && code <= 255)
this.__write(code, 8, false);
// Stop processing
else if (code == 256)
return;
// Window based result
else if (code >= 257 && code <= 285)
this.__decompressWindow(this.__handleLength(code),
distancetree.getValue(this._bitsource));
/* {@squirreljme.error BD18 Illegal dynamic huffman code. (The
code.)} */
else
throw new IOException(String.format("BD18 %d", code));
}
}
/**
* Reads the literal and distance trees.
*
* @param __cltree The code length tree.
* @param __dhlit The literal count.
* @param __dhdist The distance count.
* @param __ltree The literal tree.
* @param __dtree The distance tree.
* @throws IOException On read errors.
* @since 2017/02/25
*/
private void __decompressDynamicLoadLitDistTree(HuffmanTreeInt __cltree,
int __dhlit, int __dhdist, HuffmanTreeInt __ltree,
HuffmanTreeInt __dtree)
throws IOException
{
// Determine the maximum bit count that is used when reading values
int total = __dhlit + __dhdist;
// Cached, erase the data because later reads may have less
int[] rawlitdistlens = this._rawlitdistlens;
Arrays.fill(rawlitdistlens, 0);
// Read every code
try
{
for (int next = 0; next < total;)
next += this.__readCodeBits(__cltree, rawlitdistlens, next);
}
/* {@squirreljme.error BD19 The compressed stream is
damaged by being too short or having an illegal tree
access.} */
catch (NoSuchElementException e)
{
throw new IOException("BD19", e);
}
// Initialize both trees
this.__thunkCodeLengthTree(__ltree, rawlitdistlens, 0, __dhlit);
this.__thunkCodeLengthTree(__dtree, rawlitdistlens, __dhlit, __dhdist);
}
/**
* Reads the code length tree.
*
* @param __dhclen The code length size.
* @throws IOException On read errors.
* @since 2017/02/25
*/
private HuffmanTreeInt __decompressDynamicLoadLenTree(int __dhclen)
throws IOException
{
// Target tree
HuffmanTreeInt codelentree = this.__obtainCodeLenTree();
/* {@squirreljme.error BD1a There may only be at most 19 used
code lengths. (The number of code lengths)} */
if (__dhclen > 19)
throw new IOException(String.format("BD1a %d", __dhclen));
// The same array is used for reading code lengths but the next time
// around it is possible that less code lengths are read, so if the
// higher elements have previously been set they will be used
int[] rawcodelens = this._rawcodelens;
Arrays.fill(rawcodelens, 0);
// Read lengths, they are just 3 bits but their placement values are
// shuffled since some sequences are more common than others
byte[] hsbits = InflaterInputStream._SHUFFLE_BITS;
for (int next = 0; next < __dhclen; next++)
rawcodelens[hsbits[next]] = this.__readBits(3, false);
// Thunk the tree and return it
return this.__thunkCodeLengthTree(codelentree, rawcodelens, 0,
rawcodelens.length);
}
/**
* Decodes decompressed data stored with the fixed huffman table and
* decompresses it.
*
* @throws IOException On read or decompression errors.
* @since 2017/02/25
*/
private void __decompressFixed()
throws IOException
{
// Read until the sequence has ended
for (;;)
{
// Read code
int code = this.__readFixedHuffman();
// Literal byte value
if (code >= 0 && code <= 255)
this.__write(code, 8, false);
// Stop processing
else if (code == 256)
return;
// Window based result
else if (code >= 257 && code <= 285)
this.__decompressWindow(this.__handleLength(code), Integer.MIN_VALUE);
/* {@squirreljme.error BD1b Illegal fixed huffman code. (The
code.)} */
else
throw new IOException(String.format("BD1b %d", code));
}
}
/**
* Decompresses uncompressed data.
*
* @throws IOException On read errors.
* @since 2017/02/25
*/
private void __decompressNone()
throws IOException
{
// Throw out bits that have been read so that the following reads are
// aligned to byte boundaries
int minisub = this._minisize & 7;
if (minisub > 0)
this.__readBits(minisub, false);
// Read length and the one's complement of it
int len = this.__readBits(16, false);
int com = this.__readBits(16, false);
// The complemented length must be equal to the complement
/* {@squirreljme.error BD1c Value mismatch reading the number of
uncompressed symbols that exist. (The length; The complement;
The complemented input length; The complemented input complement)} */
if ((len ^ 0xFFFF) != com)
throw new IOException(String.format("BD1c %04x %04x %04x %04x",
len, com, len ^ 0xFFFF, com ^ 0xFFFF));
// Read all bytes
for (int i = 0; i < len; i++)
this.__write(this.__readBits(8, false), 8, false);
}
/**
* Handles decompressing window data.
*
* @param __len The length to read, must be prehandled.
* @param __dist The distance to read.
* @throws IOException On read errors.
* @since 2017/02/25
*/
private void __decompressWindow(int __len, int __dist)
throws IOException
{
// Handle distance
__dist = this.__handleDistance(__dist);
// Get the maximum valid length, so for example if the length
// is 5 and the distance is two, then only read two bytes.
int maxlen = Math.min(__dist, __len);
// Create a byte array from the sliding window data
byte[] winb = new byte[maxlen];
try
{
this.window.get(__dist, winb, 0, maxlen);
}
// Bad window read
catch (IndexOutOfBoundsException ioobe)
{
/* {@squirreljme.error BD1d Window access out of range.
(The distance; The length)} */
throw new IOException(String.format(
"BD1d %d %d", __dist, __len), ioobe);
}
// Add those bytes to the output, handle wrapping around if the
// length is greater than the current position
for (int i = 0, v = 0; i < __len; i++)
{
// Write byte
this.__write(winb[v], 8, false);
// Wrap around
if ((++v) >= maxlen)
v = 0;
}
}
/**
* Handles fixed huffman distance.
*
* @param __code The input code.
* @return The ditsance read.
* @throws IOException On read errors.
* @since 2017/02/25
*/
private int __handleDistance(int __code)
throws IOException
{
// Read distance
if (__code == Integer.MIN_VALUE)
__code = this.__readBits(5, true);
/* {@squirreljme.error BD1e Illegal fixed distance code. (The distance
code)} */
if (__code > 29)
throw new IOException(String.format("BD1e %d", __code));
// Calculate the required distance to use
int rv = 1;
for (int i = 0; i < __code; i++)
{
// This uses a similar pattern to the length code, however the
// division is half the size (so there are groups of 2 now).
int v = ((i / 2) - 1);
if (v >= 0)
rv += (1 << v);
else
rv++;
}
// Determine the number of extra bits that make up the distance which
// is used as an additional distance value
int extrabits = ((__code / 2) - 1);
if (extrabits > 0)
rv += this.__readBits(extrabits, false);
// Return it
return rv;
}
/**
* Reads length codes from the input.
*
* @param __c Input code value.
* @throws IOException On read/write errors.
* @since 2016/03/12
*/
private int __handleLength(int __c)
throws IOException
{
// The maximum length that can ever be used is 258, it has no bits also
if (__c == 285)
return 258;
// Get the base code
int base = __c - 257;
/* {@squirreljme.error BD1f Illegal length code. (The length code)} */
if (base < 0)
throw new IOException(String.format("BD1f %d", __c));
// Calculate the required length to use
int rv = 3;
for (int i = 0; i < base; i++)
{
// Determine how many groups of 4 the code is long. Since zero
// appears as items then subtract 1 to make it longer. However
// after the first 8 it goes up in a standard pattern.
int v = ((i / 4) - 1);
if (v > 0)
rv += (1 << v);
else
rv++;
}
// Add extra bits which are used to modify the amount of data read
int extrabits = (base / 4) - 1;
if (extrabits > 0)
rv += (extrabits = this.__readBits(extrabits, false));
// Return the length
return rv;
}
/**
* Obtains the code length tree.
*
* @return The code length tree.
* @since 2017/02/27
*/
private HuffmanTreeInt __obtainCodeLenTree()
{
Reference<HuffmanTreeInt> ref = this._codelentree;
HuffmanTreeInt rv;
if (ref == null || null == (rv = ref.get()))
this._codelentree = new WeakReference<>(
(rv = new HuffmanTreeInt()));
// Clear before return
rv.clear();
return rv;
}
/**
* Obtains the distance tree.
*
* @return The distance tree.
* @since 2017/02/27
*/
private HuffmanTreeInt __obtainDistanceTree()
{
Reference<HuffmanTreeInt> ref = this._distancetree;
HuffmanTreeInt rv;
if (ref == null || null == (rv = ref.get()))
this._distancetree = new WeakReference<>(
(rv = new HuffmanTreeInt()));
// Clear before return
rv.clear();
return rv;
}
/**
* Obtains the literal tree.
*
* @return The literal tree.
* @since 2017/02/27
*/
private HuffmanTreeInt __obtainLiteralTree()
{
Reference<HuffmanTreeInt> ref = this._literaltree;
HuffmanTreeInt rv;
if (ref == null || null == (rv = ref.get()))
this._literaltree = new WeakReference<>(
(rv = new HuffmanTreeInt()));
// Clear before return
rv.clear();
return rv;
}
/**
* Obtains the read window.
*
* @return The read window.
* @since 2017/02/26
*/
private byte[] __obtainReadWindow()
{
Reference<byte[]> ref = this._readwindow;
byte[] rv;
if (ref == null || null == (rv = ref.get()))
this._readwindow = new WeakReference<>(
(rv = new byte[128]));
return rv;
}
/**
* Reads bits from the input stream.
*
* @param __n The number of bits to read.
* @param __msb If {@code true} the most significant bits are first
* @return The read data.
* @throws IOException On read errors.
* @since 2017/02/25
*/
int __readBits(int __n, boolean __msb)
throws IOException
{
// Nothing to read
if (__n == 0)
return 0;
// Get the mini window information
int miniwindow = this._miniwindow,
minisize = this._minisize;
// Not enough bits to read the value
while (minisize < __n)
{
// The number of bytes to be read
int bc = (__n - minisize) / 8;
if (bc == 0)
bc = 1;
// Read input bytes
byte[] readin = this._readin;
int rc = this.in.read(readin, 0, bc);
/* {@squirreljme.error BD1g Reached EOF while reading bytes to
decompress. (Bits in the queue; Requested number of bits)} */
if (rc < 0)
throw new IOException(String.format("BD1g %d %d", minisize,
__n));
// Shift in the read bytes to the higher positions
for (int i = 0; i < rc; i++)
{
miniwindow |= ((readin[i] & 0xFF) << minisize);
minisize += 8;
}
// Count the number of compressed bytes
this._compressedsize += rc;
}
// Mask in the value, which is always at the lower bits
int rv = miniwindow & ((1 << __n) - 1);
// Shift down the mini window for the next read
// Make sure the shift down is unsigned so that zeroes are in the
// higher bits for the filling OR operation.
miniwindow >>>= __n;
minisize -= __n;
// Store for next run
this._miniwindow = miniwindow;
this._minisize = minisize;
// Want MSB to be first, need to swap all the bits so the lowest ones
// are at the highest positions
// Luckily such a method already exists and it could potentially be
// inlined by the JVM or converted to native code if such an
// instruction exists.
if (__msb)
return Integer.reverse(rv) >>> (32 - __n);
// Return read result
return rv;
}
/**
* Reads code bits using the given huffman tree and into the specified
* array.
*
* @param __codes The huffman tree which contains the length codes which
* the values being read are encoded with.
* @param __out The output array.
* @param __next The next value to read.
* @throws IOException On read or decompression errors.
* @throws NullPointerException On null arguments.
* @since 2016/03/28
*/
private int __readCodeBits(HuffmanTreeInt __codes, int[] __out,
int __next)
throws IOException, NullPointerException
{
// Check
if (__codes == null || __out == null)
throw new NullPointerException("NARG");
// Read in code based on an input huffman tree
int basenext = __next;
int code = __codes.getValue(this._bitsource);
// Literal length, the input is used
if (code >= 0 && code < 16)
__out[__next++] = code;
// Repeating
else
{
// Repeat this value and for this many lengths
int repval;
int repfor;
// Repeat the previous length 3-6 times
if (code == 16)
{
/* {@squirreljme.error BD1h A repeat code was specified,
however this is the first entry. (The last length index)} */
int lastlendx = __next - 1;
if (lastlendx < 0)
throw new IOException(String.format("BD1h %d",
lastlendx));
// Read the last
repval = __out[lastlendx];
// Read the repeat count
repfor = 3 + this.__readBits(2, false);
}
// Repeat zero for 3-10 times
else if (code == 17)
{
// Use zero
repval = 0;
// Read 3 bits
repfor = 3 + this.__readBits(3, false);
}
// Repeat zero for 11-138 times
else if (code == 18)
{
// Use zero
repval = 0;
// Read 7 bits
repfor = 11 + this.__readBits(7, false);
}
/* {@squirreljme.error BD1i Illegal code. (The code)} */
else
throw new IOException(String.format("BD1i %d", code));
// Could fail
try
{
// Place in repeated values
for (int i = 0; i < repfor; i++)
__out[__next++] = repval;
}
// Out of bounds entry
catch (IndexOutOfBoundsException ioobe)
{
/* {@squirreljme.error BD1j Out of bounds index read.} */
throw new IOException("BD1j", ioobe);
}
}
// Skip count
return __next - basenext;
}
/**
* Reads a fixed huffman code for use by the {@code _TYPE_FIXED_HUFFMAN}
* state. This method does not traverse a huffman tree so to speak, but it
* instead uses many if statements. Initially every consideration was made
* but now instead it uses ranges once it keeps deep enough into the tree.
* This method is faster and provides a built-in huffman tree while not
* taking up too many bytes in the byte code.
*
* @return The read value.
* @throws IOException On read errors.
* @since 2016/03/10
*/
private int __readFixedHuffman()
throws IOException
{
// The long if statement block
if (this.__readBits(1, true) != 0)
if (this.__readBits(1, true) != 0)
if (this.__readBits(1, true) != 0)
return 192 + this.__readBits(6, true);
else
if (this.__readBits(1, true) != 0)
return 160 + this.__readBits(5, true);
else
if (this.__readBits(1, true) != 0)
return 144 + this.__readBits(4, true);
else
return 280 + this.__readBits(3, true);
else
return 80 + this.__readBits(6, true);
else
if (this.__readBits(1, true) != 0)
return 16 + this.__readBits(6, true);
else
if (this.__readBits(1, true) != 0)
if (this.__readBits(1, true) != 0)
return 0 + this.__readBits(4, true);
else
return 272 + this.__readBits(3, true);
else
return 256 + this.__readBits(4, true);
}
/**
* Creates a huffman tree from the given code lengths. These generate
* symbols which are used to determine how the dynamic huffman data is to
* be decoded.
*
* @param __tree The tree to output.
* @param __lens The input code lengths.
* @param __o The starting offset.
* @param __l The number of lengths to decode.
* @return A huffman tree from the code length input.
* @throws NullPointerException On null arguments.
* @since 2016/03/28
*/
private HuffmanTreeInt __thunkCodeLengthTree(HuffmanTreeInt __tree,
int[] __lens, int __o, int __l)
throws NullPointerException
{
// Check
if (__lens == null)
throw new NullPointerException("NARG");
// Initialize both arrays with zero
int[] bl_count = this._blcount;
int[] next_code = this._nextcode;
Arrays.fill(bl_count, 0);
Arrays.fill(next_code, 0);
// Determine the bitlength count for all of the inputs
for (int i = 0, p = __o; i < __l; i++, p++)
bl_count[__lens[p]]++;
bl_count[0] = 0;
// Find the numerical value of the smallest code for each code
// length.
int code = 0;
for (int bits = 1; bits <= InflaterInputStream._MAX_BITS; bits++)
{
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
}
// Assign values to all codes
__tree.clear();
for (int q = 0, p = __o; q < __l; q++, p++)
{
// Get length
int len = __lens[p];
// Add code length to the huffman tree
if (len != 0)
__tree.add(q, (next_code[len])++, (1 << len) - 1);
}
// Return it
return __tree;
}
/**
* Writes the specified value to the output.
*
* @param __v The value to write.
* @param __bits The number of bits to write.
* @param __msb Most significant bits first?
* @throws IOException On write errors.
* @since 2017/02/25
*/
private void __write(int __v, int __bits, boolean __msb)
throws IOException
{
// Calculate the mask
int mask = (1 << __bits) - 1;
// Write LSB value, need to swap bits if writing MSB
__v &= mask;
if (__msb)
__v = Integer.reverse(__v) >>> (32 - __bits);
// Get the current write window
int writewindow = this._writewindow,
writesize = this._writesize;
// Add bits to write
writewindow |= (__v & mask) << writesize;
writesize += __bits;
// Enough bytes to write to the output?
if (writesize >= 8)
{
// Write to a byte array first
byte[] targ = this._targ;
int targoff = this._targoff,
targend = this._targend;
// But if writes overflow then add to the overflow buffer
ByteDeque overflow = this.overflow;
// Any bytes written are appended to the sliding window
SlidingByteWindow window = this.window;
// Write bytes
do
{
// Read input byte
byte b = (byte)writewindow;
writewindow >>>= 8;
writesize -= 8;
// Can fit in the output buffer
if (targoff < targend)
targ[targoff++] = b;
// Overflows
else
overflow.offerLast(b);
// Append to the window
window.append(b);
} while (writesize >= 8);
// Store new position
this._targoff = targoff;
}
// Store the write window info
this._writewindow = writewindow;
this._writesize = writesize;
}
/**
* Bit source for huffman reads.
*
* @since 2017/02/25
*/
private final class __BitSource__
implements BitSource
{
/**
* {@inheritDoc}
* @since 2017/02/25
*/
@Override
public boolean nextBit()
throws IOException
{
return 0 != InflaterInputStream.this.__readBits(1, true);
}
}
}