Skip to content

Commit

Permalink
Import from SVN r2110.
Browse files Browse the repository at this point in the history
  • Loading branch information
ijuma committed Dec 5, 2009
1 parent fa9f752 commit cfac750
Show file tree
Hide file tree
Showing 3 changed files with 410 additions and 0 deletions.
184 changes: 184 additions & 0 deletions src/main/org/h2/compress/CompressLZF.java
@@ -0,0 +1,184 @@
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Copyright (c) 2000-2005 Marc Alexander Lehmann <schmorp@schmorp.de>
* Copyright (c) 2005 Oren J. Maurice <oymaurice@hazorea.org.il>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

package org.h2.compress;

/**
* This class implements the LZF lossless data compression algorithm.
* LZF is optimized for speed.
*/
public class CompressLZF implements Compressor {

private static final int HASH_SIZE = 1 << 14;
private static final int MAX_LITERAL = 1 << 5;
private static final int MAX_OFF = 1 << 13;
private static final int MAX_REF = (1 << 8) + (1 << 3);

private int[] cachedHashTable;

public void setOptions(String options) {
// nothing to do
}

public int getAlgorithm() {
return Compressor.LZF;
}

private int first(byte[] in, int inPos) {
return (in[inPos] << 8) + (in[inPos + 1] & 255);
}

private int next(int v, byte[] in, int inPos) {
return (v << 8) + (in[inPos + 2] & 255);
}

private int hash(int h) {
// or 57321
return ((h * 184117) >> 9) & (HASH_SIZE - 1);
}

public int compress(byte[] in, int inLen, byte[] out, int outPos) {
int inPos = 0;
if (cachedHashTable == null) {
cachedHashTable = new int[HASH_SIZE];
}
int[] hashTab = cachedHashTable;
int literals = 0;
outPos++;
int hash = first(in, 0);
while (inPos < inLen - 4) {
byte p2 = in[inPos + 2];
// next
hash = (hash << 8) + (p2 & 255);
int off = hash(hash);
int ref = hashTab[off];
hashTab[off] = inPos;
if (ref < inPos
&& ref > 0
&& (off = inPos - ref - 1) < MAX_OFF
&& in[ref + 2] == p2
&& in[ref + 1] == (byte) (hash >> 8)
&& in[ref] == (byte) (hash >> 16)) {
// match
int maxLen = inLen - inPos - 2;
if (maxLen > MAX_REF) {
maxLen = MAX_REF;
}
if (literals == 0) {
outPos--;
} else {
out[outPos - literals - 1] = (byte) (literals - 1);
literals = 0;
}
int len = 3;
while (len < maxLen && in[ref + len] == in[inPos + len]) {
len++;
}
len -= 2;
if (len < 7) {
out[outPos++] = (byte) ((off >> 8) + (len << 5));
} else {
out[outPos++] = (byte) ((off >> 8) + (7 << 5));
out[outPos++] = (byte) (len - 7);
}
out[outPos++] = (byte) off;
outPos++;
inPos += len;
hash = first(in, inPos);
hash = next(hash, in, inPos);
hashTab[hash(hash)] = inPos++;
hash = next(hash, in, inPos);
hashTab[hash(hash)] = inPos++;
} else {
out[outPos++] = in[inPos++];
literals++;
if (literals == MAX_LITERAL) {
out[outPos - literals - 1] = (byte) (literals - 1);
literals = 0;
outPos++;
}
}
}
while (inPos < inLen) {
out[outPos++] = in[inPos++];
literals++;
if (literals == MAX_LITERAL) {
out[outPos - literals - 1] = (byte) (literals - 1);
literals = 0;
outPos++;
}
}
out[outPos - literals - 1] = (byte) (literals - 1);
if (literals == 0) {
outPos--;
}
return outPos;
}

public void expand(byte[] in, int inPos, int inLen, byte[] out, int outPos, int outLen) {
do {
int ctrl = in[inPos++] & 255;
if (ctrl < (1 << 5)) {
// literal run
ctrl += inPos;
do {
out[outPos++] = in[inPos];
} while (inPos++ < ctrl);
} else {
// back reference
int len = ctrl >> 5;
ctrl = -((ctrl & 0x1f) << 8) - 1;
if (len == 7) {
len += in[inPos++] & 255;
}
ctrl -= in[inPos++] & 255;
len += outPos + 2;
out[outPos] = out[outPos++ + ctrl];
out[outPos] = out[outPos++ + ctrl];
while (outPos < len - 8) {
out[outPos] = out[outPos++ + ctrl];
out[outPos] = out[outPos++ + ctrl];
out[outPos] = out[outPos++ + ctrl];
out[outPos] = out[outPos++ + ctrl];
out[outPos] = out[outPos++ + ctrl];
out[outPos] = out[outPos++ + ctrl];
out[outPos] = out[outPos++ + ctrl];
out[outPos] = out[outPos++ + ctrl];
}
while (outPos < len) {
out[outPos] = out[outPos++ + ctrl];
}
}
} while (outPos < outLen);
}
}
126 changes: 126 additions & 0 deletions src/main/org/h2/compress/LZFInputStream.java
@@ -0,0 +1,126 @@
/*
* Copyright 2004-2009 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.compress;

import java.io.IOException;
import java.io.InputStream;

import org.h2.util.MemoryUtils;

/**
* An input stream to read from an LZF stream.
* The data is automatically expanded.
*/
public class LZFInputStream extends InputStream {

private final InputStream in;
private CompressLZF decompress = new CompressLZF();
private int pos;
private int bufferLength;
private byte[] inBuffer;
private byte[] buffer;

public LZFInputStream(InputStream in) throws IOException {
this.in = in;
if (readInt() != LZFOutputStream.MAGIC) {
throw new IOException("Not an LZFInputStream");
}
}

private byte[] ensureSize(byte[] buff, int len) {
return buff == null || buff.length < len ? MemoryUtils.newBytes(len) : buff;
}

private void fillBuffer() throws IOException {
if (buffer != null && pos < bufferLength) {
return;
}
int len = readInt();
if (decompress == null) {
// EOF
this.bufferLength = 0;
} else if (len < 0) {
len = -len;
buffer = ensureSize(buffer, len);
readFully(buffer, len);
this.bufferLength = len;
} else {
inBuffer = ensureSize(inBuffer, len);
int size = readInt();
readFully(inBuffer, len);
buffer = ensureSize(buffer, size);
decompress.expand(inBuffer, 0, len, buffer, 0, size);
this.bufferLength = size;
}
pos = 0;
}

private void readFully(byte[] buff, int len) throws IOException {
int off = 0;
while (len > 0) {
int l = in.read(buff, off, len);
len -= l;
off += l;
}
}

private int readInt() throws IOException {
int x = in.read();
if (x < 0) {
decompress = null;
return 0;
}
x = (x << 24) + (in.read() << 16) + (in.read() << 8) + in.read();
return x;
}

public int read() throws IOException {
fillBuffer();
if (pos >= bufferLength) {
return -1;
}
return buffer[pos++] & 255;
}

public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}

public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
int read = 0;
while (len > 0) {
int r = readBlock(b, off, len);
if (r < 0) {
break;
}
read += r;
off += r;
len -= r;
}
return read == 0 ? -1 : read;
}

private int readBlock(byte[] b, int off, int len) throws IOException {
fillBuffer();
if (pos >= bufferLength) {
return -1;
}
int max = Math.min(len, bufferLength - pos);
max = Math.min(max, b.length - off);
System.arraycopy(buffer, pos, b, off, max);
pos += max;
return max;
}

public void close() throws IOException {
in.close();
}

}

0 comments on commit cfac750

Please sign in to comment.