All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.carne.nio.compression.common.HuffmanDecoder Maven / Gradle / Ivy

/*
 * Copyright (c) 2016 Holger de Carne and contributors, All Rights Reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser Public License
 * along with this program.  If not, see .
 */
package de.carne.nio.compression.common;

import java.io.IOException;
import java.nio.channels.ReadableByteChannel;

import de.carne.nio.compression.InvalidDataException;
import de.carne.nio.compression.util.Assert;

/**
 * Huffman symbol decoding support.
 */
public final class HuffmanDecoder {

	private static final int LENGTHS_TABLE_BITS = 9;

	private final int limits[];
	private final int positions[];
	private final int symbols[];
	byte lengths[];

	/**
	 * Construct {@code HuffmanDecoder}.
	 *
	 * @param maxBits Maximum length of a symbol.
	 * @param maxSymbols Maximum number of symbols.
	 */
	public HuffmanDecoder(int maxBits, int maxSymbols) {
		Assert.isValid(maxBits > 0, "maxBits", maxBits);
		Assert.isValid(maxSymbols > 0, "maxSymbols", maxSymbols);

		this.limits = new int[maxBits + 1];
		this.positions = new int[maxBits + 1];
		this.symbols = new int[maxSymbols];
		this.lengths = new byte[1 << LENGTHS_TABLE_BITS];
	}

	/**
	 * Set code lengths.
	 *
	 * @param codeLengths The code lengths to set.
	 * @throws IOException if inconsistent data is encountered.
	 */
	public void setCodeLengths(byte[] codeLengths) throws IOException {
		int maxBits = this.limits.length - 1;
		int maxSymbols = this.symbols.length;
		int lengthCounts[] = new int[maxBits + 1];
		int positions2[] = new int[maxBits + 1];

		for (int symbol = 0; symbol < maxSymbols; symbol++) {
			int length = codeLengths[symbol] & 0xff;

			if (length > maxBits) {
				throw new InvalidDataException(length);
			}
			lengthCounts[length]++;
			this.symbols[symbol] = -1;
		}
		lengthCounts[0] = this.positions[0] = this.limits[0] = 0;

		int startPosition = 0;
		int index = 0;
		final int maxValue = (1 << maxBits);

		for (int value = 1; value <= maxBits; value++) {
			startPosition += lengthCounts[value] << (maxBits - value);
			if (startPosition > maxValue) {
				throw new InvalidDataException();
			}
			this.limits[value] = (value == maxBits ? maxValue : startPosition);
			this.positions[value] = this.positions[value - 1] + lengthCounts[value - 1];
			positions2[value] = this.positions[value];
			if (value <= LENGTHS_TABLE_BITS) {
				final int limit = (this.limits[value] >> (maxBits - LENGTHS_TABLE_BITS));

				while (index < limit) {
					this.lengths[index] = (byte) value;
					index++;
				}
			}
		}
		for (int symbol = 0; symbol < maxSymbols; symbol++) {
			int length = codeLengths[symbol] & 0xff;

			if (length != 0) {
				this.symbols[positions2[length]++] = symbol;
			}
		}
	}

	/**
	 * Decode next symbol.
	 *
	 * @param src The channel to read the symbol from.
	 * @param bitDecoder The BitDecoder to use for bit decoding.
	 * @param bufferIndex The bit buffer to use for bit decoding.
	 * @return The read symbol.
	 * @throws IOException If an I/O error occurred.
	 */
	public int decodeSymbol(ReadableByteChannel src, BitDecoder bitDecoder, int bufferIndex) throws IOException {
		int maxBits = this.positions.length - 1;
		int value = bitDecoder.peekBits(src, maxBits, bufferIndex);
		int symbolLength;

		if (value < this.limits[LENGTHS_TABLE_BITS]) {
			symbolLength = this.lengths[value >> (maxBits - LENGTHS_TABLE_BITS)] & 0xff;
		} else {
			symbolLength = LENGTHS_TABLE_BITS + 1;
			while (value >= this.limits[symbolLength]) {
				symbolLength++;
			}
		}
		bitDecoder.decodeBits(src, symbolLength, bufferIndex);

		int index = this.positions[symbolLength]
				+ ((value - this.limits[symbolLength - 1]) >> (maxBits - symbolLength));

		return (index < this.symbols.length ? this.symbols[index] : -1);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy