/* * 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 binary arithmetic stream. */ public class BinaryArithmeticStream { /** * The maximum probability. */ public static final int MAX_PROBABILITY = (1 << 12) - 1; /** * The low marker. */ protected int low; /** * The high marker. */ protected int high = 0xffffffff; /** * A binary arithmetic input stream. */ public static class In extends BinaryArithmeticStream { private final InputStream in; private int data; public In(InputStream in) throws IOException { this.in = in; data = ((in.read() & 0xff) << 24) | ((in.read() & 0xff) << 16) | ((in.read() & 0xff) << 8) | (in.read() & 0xff); } /** * Read a bit. * * @param probability the probability that the value is true * @return the value */ public boolean readBit(int probability) throws IOException { int split = low + probability * ((high - low) >>> 12); boolean value; // compare unsigned if (data + Integer.MIN_VALUE > split + Integer.MIN_VALUE) { low = split + 1; value = false; } else { high = split; value = true; } while (low >>> 24 == high >>> 24) { data = (data << 8) | (in.read() & 0xff); low <<= 8; high = (high << 8) | 0xff; } return value; } /** * Read a value that is stored as a Golomb code. * * @param divisor the divisor * @return the value */ public int readGolomb(int divisor) throws IOException { int q = 0; while (readBit(MAX_PROBABILITY / 2)) { 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(MAX_PROBABILITY / 2) ? 1 : 0); } if (r >= cutOff) { r = (r << 1) + (readBit(MAX_PROBABILITY / 2) ? 1 : 0) - cutOff; } } return q * divisor + r; } } /** * A binary arithmetic output stream. */ public static class Out extends BinaryArithmeticStream { private final OutputStream out; public Out(OutputStream out) { this.out = out; } /** * Write a bit. * * @param value the value * @param probability the probability that the value is true */ public void writeBit(boolean value, int probability) throws IOException { int split = low + probability * ((high - low) >>> 12); if (value) { high = split; } else { low = split + 1; } while (low >>> 24 == high >>> 24) { out.write(high >> 24); low <<= 8; high = (high << 8) | 0xff; } } /** * Flush the stream. */ public void flush() throws IOException { out.write(high >> 24); out.write(high >> 16); out.write(high >> 8); out.write(high); } /** * Write the Golomb code of a value. * * @param divisor the divisor * @param value the value */ public void writeGolomb(int divisor, int value) throws IOException { int q = value / divisor; for (int i = 0; i < q; i++) { writeBit(true, MAX_PROBABILITY / 2); } writeBit(false, MAX_PROBABILITY / 2); 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) == 1, MAX_PROBABILITY / 2); } } } /** * A Huffman code table / tree. */ 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(Out out, int value) throws IOException { int code = codes[value]; int bitCount = 30 - Integer.numberOfLeadingZeros(code); Node n = tree; for (int i = bitCount; i >= 0; i--) { boolean goRight = ((code >> i) & 1) == 1; int prob = (int) ((long) MAX_PROBABILITY * n.right.frequency / n.frequency); out.writeBit(goRight, prob); n = goRight ? n.right : n.left; } } /** * Read a value. * * @param in the input stream * @return the value */ public int read(In in) throws IOException { Node n = tree; while (n.left != null) { int prob = (int) ((long) MAX_PROBABILITY * n.right.frequency / n.frequency); boolean goRight = in.readBit(prob); n = goRight ? n.right : n.left; } return n.value; } } /** * A Huffman code node. */ private static class Node implements Comparable<Node> { int value; Node left; Node right; 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); } } } }