提交 7c1c0f36 authored 作者: Thomas Mueller's avatar Thomas Mueller

BitStream with Golomb and Huffman coding

上级 bba28211
...@@ -150,6 +150,7 @@ import org.h2.test.synth.sql.TestSynth; ...@@ -150,6 +150,7 @@ import org.h2.test.synth.sql.TestSynth;
import org.h2.test.synth.thread.TestMulti; import org.h2.test.synth.thread.TestMulti;
import org.h2.test.unit.TestAutoReconnect; import org.h2.test.unit.TestAutoReconnect;
import org.h2.test.unit.TestBitField; import org.h2.test.unit.TestBitField;
import org.h2.test.unit.TestBitStream;
import org.h2.test.unit.TestBnf; import org.h2.test.unit.TestBnf;
import org.h2.test.unit.TestCache; import org.h2.test.unit.TestCache;
import org.h2.test.unit.TestClearReferences; import org.h2.test.unit.TestClearReferences;
...@@ -792,6 +793,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -792,6 +793,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
// unit // unit
addTest(new TestAutoReconnect()); addTest(new TestAutoReconnect());
addTest(new TestBitField()); addTest(new TestBitField());
addTest(new TestBitStream());
addTest(new TestBnf()); addTest(new TestBnf());
addTest(new TestCache()); addTest(new TestCache());
addTest(new TestClearReferences()); addTest(new TestClearReferences());
......
/*
* 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.test.unit;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Random;
import org.h2.dev.util.BitStream;
import org.h2.dev.util.BitStream.In;
import org.h2.dev.util.BitStream.Out;
import org.h2.test.TestBase;
/**
* Test the bit stream (Golomb code and Huffman code) utility.
*/
public class TestBitStream extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws Exception {
testHuffmanRandomized();
testHuffman();
testBitStream();
testGolomb("11110010", 10, 42);
testGolomb("00", 3, 0);
testGolomb("010", 3, 1);
testGolomb("011", 3, 2);
testGolomb("100", 3, 3);
testGolomb("1010", 3, 4);
testGolombRandomized();
}
private void testHuffmanRandomized() {
Random r = new Random(1);
int[] freq = new int[r.nextInt(200) + 1];
for (int i = 0; i < freq.length; i++) {
freq[i] = r.nextInt(1000) + 1;
}
int seed = r.nextInt();
r.setSeed(seed);
BitStream.Huffman huff = new BitStream.Huffman(freq);
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
BitStream.Out out = new BitStream.Out(byteOut);
for (int i = 0; i < 10000; i++) {
huff.write(out, r.nextInt(freq.length));
}
out.close();
BitStream.In in = new BitStream.In(new ByteArrayInputStream(byteOut.toByteArray()));
r.setSeed(seed);
for (int i = 0; i < 10000; i++) {
int expected = r.nextInt(freq.length);
assertEquals(expected, huff.read(in));
}
}
private void testHuffman() {
int[] freq = { 36, 18, 12, 9, 7, 6, 5, 4 };
BitStream.Huffman huff = new BitStream.Huffman(freq);
final StringBuilder buff = new StringBuilder();
Out o = new Out(null) {
@Override
public void writeBit(int bit) {
buff.append(bit == 0 ? '0' : '1');
}
};
for (int i = 0; i < freq.length; i++) {
buff.append(i + ": ");
huff.write(o, i);
buff.append("\n");
}
assertEquals(
"0: 0\n" +
"1: 110\n" +
"2: 100\n" +
"3: 1110\n" +
"4: 1011\n" +
"5: 1010\n" +
"6: 11111\n" +
"7: 11110\n", buff.toString());
}
private void testGolomb(String expected, int div, int value) {
final StringBuilder buff = new StringBuilder();
Out o = new Out(null) {
@Override
public void writeBit(int bit) {
buff.append(bit == 0 ? '0' : '1');
}
};
o.writeGolomb(div, value);
String got = buff.toString();
assertEquals(expected, got);
}
private void testGolombRandomized() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Out bitOut = new Out(out);
Random r = new Random(1);
int len = 1000;
for (int i = 0; i < len; i++) {
int div = r.nextInt(100) + 1;
int value = r.nextInt(1000000);
bitOut.writeGolomb(div, value);
}
bitOut.flush();
bitOut.close();
byte[] data = out.toByteArray();
ByteArrayInputStream in = new ByteArrayInputStream(data);
In bitIn = new In(in);
r.setSeed(1);
for (int i = 0; i < len; i++) {
int div = r.nextInt(100) + 1;
int value = r.nextInt(1000000);
int v = bitIn.readGolomb(div);
assertEquals("i=" + i + " div=" + div, value, v);
}
}
private void testBitStream() {
Random r = new Random();
for (int test = 0; test < 10000; test++) {
ByteArrayOutputStream buff = new ByteArrayOutputStream();
int len = r.nextInt(40);
Out out = new Out(buff);
long seed = r.nextLong();
Random r2 = new Random(seed);
for (int i = 0; i < len; i++) {
out.writeBit(r2.nextBoolean() ? 1 : 0);
}
out.close();
In in = new In(new ByteArrayInputStream(
buff.toByteArray()));
r2 = new Random(seed);
int i = 0;
for (; i < len; i++) {
int expected = r2.nextBoolean() ? 1 : 0;
assertEquals(expected, in.readBit());
}
for (; i % 8 != 0; i++) {
assertEquals(0, in.readBit());
}
assertEquals(-1, in.readBit());
}
}
}
/*
* 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);
}
}
/**
* 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();
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;
}
}
/**
* 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);
}
}
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论