/* * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, * and the EPL 1.0 (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.dev.util; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.PriorityQueue; /** * A stream that supports Golomb and Huffman coding. */ public class BitStream { private BitStream() { // a utility class } /** * A bit input stream. */ public static class In { private final InputStream in; private int current = 0x10000; public In(InputStream in) { this.in = in; } /** * Read a value that is stored as a Golomb code. * * @param divisor the divisor * @return the value */ public int readGolomb(int divisor) { int q = 0; while (readBit() == 1) { q++; } int bit = 31 - Integer.numberOfLeadingZeros(divisor - 1); int r = 0; if (bit >= 0) { int cutOff = (2 << bit) - divisor; for (; bit > 0; bit--) { r = (r << 1) + readBit(); } if (r >= cutOff) { r = (r << 1) + readBit() - cutOff; } } return q * divisor + r; } /** * Read a bit. * * @return the bit (0 or 1) */ public int readBit() { if (current >= 0x10000) { try { current = 0x100 | in.read(); if (current < 0) { return -1; } } catch (IOException e) { throw new IllegalStateException(e); } } int bit = (current >>> 7) & 1; current <<= 1; return bit; } /** * Close the stream. This will also close the underlying stream. */ public void close() { try { in.close(); } catch (IOException e) { throw new IllegalStateException(e); } } } /** * A bit output stream. */ public static class Out { private final OutputStream out; private int current = 1; public Out(OutputStream out) { this.out = out; } /** * Write the Golomb code of a value. * * @param divisor the divisor * @param value the value */ public void writeGolomb(int divisor, int value) { int q = value / divisor; for (int i = 0; i < q; i++) { writeBit(1); } writeBit(0); int r = value - q * divisor; int bit = 31 - Integer.numberOfLeadingZeros(divisor - 1); if (r < ((2 << bit) - divisor)) { bit--; } else { r += (2 << bit) - divisor; } for (; bit >= 0; bit--) { writeBit((r >>> bit) & 1); } } /** * Get the size of the Golomb code for this value. * * @param divisor the divisor * @param value the value * @return the number of bits */ public static int getGolombSize(int divisor, int value) { int q = value / divisor; int r = value - q * divisor; int bit = 31 - Integer.numberOfLeadingZeros(divisor - 1); if (r < ((2 << bit) - divisor)) { bit--; } return bit + q + 2; } /** * Write a bit. * * @param bit the bit (0 or 1) */ public void writeBit(int bit) { current = (current << 1) + bit; if (current > 0xff) { try { out.write(current & 0xff); } catch (IOException e) { throw new IllegalStateException(e); } current = 1; } } /** * Flush the stream. This will at write at most 7 '0' bits. * This will also flush the underlying stream. */ public void flush() { while (current > 1) { writeBit(0); } try { out.flush(); } catch (IOException e) { throw new IllegalStateException(e); } } /** * Flush and close the stream. * This will also close the underlying stream. */ public void close() { flush(); try { out.close(); } catch (IOException e) { throw new IllegalStateException(e); } } } /** * A Huffman code. */ public static class Huffman { private final int[] codes; private final Node tree; public Huffman(int[] frequencies) { PriorityQueue<Node> queue = new PriorityQueue<Node>(); for (int i = 0; i < frequencies.length; i++) { int f = frequencies[i]; if (f > 0) { queue.offer(new Node(i, f)); } } while (queue.size() > 1) { queue.offer(new Node(queue.poll(), queue.poll())); } codes = new int[frequencies.length]; tree = queue.poll(); if (tree != null) { tree.initCodes(codes, 1); } } /** * Write a value. * * @param out the output stream * @param value the value to write */ public void write(BitStream.Out out, int value) { int code = codes[value]; int bitCount = 30 - Integer.numberOfLeadingZeros(code); for (int i = bitCount; i >= 0; i--) { out.writeBit((code >> i) & 1); } } /** * Read a value. * * @param in the input stream * @return the value */ public int read(BitStream.In in) { Node n = tree; while (n.left != null) { n = in.readBit() == 1 ? n.right : n.left; } return n.value; } /** * Get the number of bits of the Huffman code for this value. * * @param value the value * @return the number of bits */ public int getBitCount(int value) { int code = codes[value]; return 30 - Integer.numberOfLeadingZeros(code); } } /** * A Huffman code node. */ private static class Node implements Comparable<Node> { int value; Node left; Node right; private final int frequency; Node(int value, int frequency) { this.frequency = frequency; this.value = value; } Node(Node left, Node right) { this.left = left; this.right = right; this.frequency = left.frequency + right.frequency; } @Override public int compareTo(Node o) { return frequency - o.frequency; } void initCodes(int[] codes, int bits) { if (left == null) { codes[value] = bits; } else { left.initCodes(codes, bits << 1); right.initCodes(codes, (bits << 1) + 1); } } } }