com.neovisionaries.ws.client.Huffman Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nv-websocket-client Show documentation
Show all versions of nv-websocket-client Show documentation
WebSocket client implementation in Java.
/*
* Copyright (C) 2015 Neo Visionaries Inc.
*
* Licensed 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 com.neovisionaries.ws.client;
/**
* Huffman coding for DEFLATE format (RFC 1951).
*/
class Huffman
{
private final int mMinCodeLen;
private final int mMaxCodeLen;
private final int[] mMaxCodeValsFromCodeLen;
private final int[] mSymsFromCodeVal;
public Huffman(int[] codeLensFromSym)
{
// Remember the minimum and maximum code lengths.
mMinCodeLen = Math.max(Misc.min(codeLensFromSym), 1);
mMaxCodeLen = Misc.max(codeLensFromSym);
// Count the number of entries for each code length.
int[] countsFromCodeLen = createCountsFromCodeLen(codeLensFromSym, mMaxCodeLen);
// Create an array holding the maximum code values for each code length.
Object[] out = new Object[2];
mMaxCodeValsFromCodeLen = createMaxCodeValsFromCodeLen(countsFromCodeLen, mMaxCodeLen, out);
// Create a table to convert code values int symbols.
int[] codeValsFromCodeLen = (int[])out[0];
int maxCodeVal = ((Integer)out[1]).intValue();
mSymsFromCodeVal = createSymsFromCodeVal(codeLensFromSym, codeValsFromCodeLen, maxCodeVal);
}
/**
* Create an array whose elements have the given initial value.
*/
private static int[] createIntArray(int size, int initialValue)
{
int[] array = new int[size];
for (int i = 0; i < size; ++i)
{
array[i] = initialValue;
}
return array;
}
private static int[] createCountsFromCodeLen(int[] codeLensFromSym, int maxCodeLen)
{
int[] countsFromCodeLen = new int[maxCodeLen + 1];
// Count the number of entries for each code length.
// This corresponds to the step 1 in 3.2.2. of RFC 1951.
for (int symbol = 0; symbol < codeLensFromSym.length; ++symbol)
{
int codeLength = codeLensFromSym[symbol];
++countsFromCodeLen[codeLength];
}
return countsFromCodeLen;
}
private static int[] createMaxCodeValsFromCodeLen(int[] countsFromCodeLen, int maxCodeLen, Object[] out)
{
// Initialize an array that holds the maximum code values
// for each code length. '-1' indicates that there is no
// code value for the code length.
int[] maxCodeValsFromCodeLen = createIntArray(maxCodeLen + 1, -1);
// Compute the smallest code value for each code length.
// This corresponds to the step 2 in 3.2.2. of RFC 1951.
int minCodeVal = 0;
int maxCodeVal = 0;
countsFromCodeLen[0] = 0;
int[] codeValsFromCodeLen = new int[maxCodeLen + 1];
for (int codeLen = 1; codeLen < countsFromCodeLen.length; ++codeLen)
{
// Compute the minimum code value for each code length.
int prevCount = countsFromCodeLen[codeLen - 1];
minCodeVal = (minCodeVal + prevCount) << 1;
codeValsFromCodeLen[codeLen] = minCodeVal;
// Compute the maximum code value for each code length.
maxCodeVal = minCodeVal + countsFromCodeLen[codeLen] - 1;
maxCodeValsFromCodeLen[codeLen] = maxCodeVal;
}
out[0] = codeValsFromCodeLen;
out[1] = Integer.valueOf(maxCodeVal);
return maxCodeValsFromCodeLen;
}
private static int[] createSymsFromCodeVal(int[] codeLensFromSym, int[] codeValsFromCodeLen, int maxCodeVal)
{
int[] symsFromCodeVal = new int[maxCodeVal + 1];
// Set up a table to convert code values into symbols.
// This corresponds to the step 3 in 3.2.2. of RFC 1951.
for (int sym = 0; sym < codeLensFromSym.length; ++sym)
{
int codeLen = codeLensFromSym[sym];
if (codeLen == 0)
{
continue;
}
int codeVal = codeValsFromCodeLen[codeLen]++;
symsFromCodeVal[codeVal] = sym;
}
return symsFromCodeVal;
}
public int readSym(ByteArray data, int[] bitIndex) throws FormatException
{
for (int codeLen = mMinCodeLen; codeLen <= mMaxCodeLen; ++codeLen)
{
// Get the maximum one from among the code values
// whose code length is 'codeLen'.
int maxCodeVal = mMaxCodeValsFromCodeLen[codeLen];
if (maxCodeVal < 0)
{
// There is no code value whose code length is 'codeLen'.
continue;
}
// Read a code value from the input assuming its code length is 'codeLen'.
int codeVal = data.getHuffmanBits(bitIndex[0], codeLen);
if (maxCodeVal < codeVal)
{
// The read code value is bigger than the maximum code value
// among the code values whose code length is 'codeLen'.
//
// Considering the latter rule of the two added for DEFLATE format
// (3.2.2. Use of Huffman coding in the "deflate" format),
//
// * All codes of a given bit length have lexicographically
// consecutive values, in the same order as the symbols
// they represent;
//
// * Shorter codes lexicographically precede longer codes.
//
// We can expect that the code length of the code value we are
// parsing is longer than the current 'codeLen'.
continue;
}
// Convert the code value into a symbol value.
int sym = mSymsFromCodeVal[codeVal];
// Consume the bits of the code value.
bitIndex[0] += codeLen;
return sym;
}
// Bad code at the bit index.
String message = String.format(
"[%s] Bad code at the bit index '%d'.",
getClass().getSimpleName(), bitIndex[0]);
throw new FormatException(message);
}
}