Yee
6 years ago
66 changed files with 9278 additions and 1 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,301 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4BlockOutputStream.COMPRESSION_LEVEL_BASE; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4BlockOutputStream.COMPRESSION_METHOD_LZ4; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4BlockOutputStream.COMPRESSION_METHOD_RAW; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4BlockOutputStream.DEFAULT_SEED; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4BlockOutputStream.HEADER_LENGTH; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4BlockOutputStream.MAGIC; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4BlockOutputStream.MAGIC_LENGTH; |
||||
|
||||
import java.io.EOFException; |
||||
import java.io.FilterInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.util.zip.Checksum; |
||||
|
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
import com.fr.third.net.jpountz.xxhash.StreamingXXHash32; |
||||
import com.fr.third.net.jpountz.xxhash.XXHash32; |
||||
import com.fr.third.net.jpountz.xxhash.XXHashFactory; |
||||
|
||||
/** |
||||
* {@link InputStream} implementation to decode data written with |
||||
* {@link LZ4BlockOutputStream}. This class is not thread-safe and does not |
||||
* support {@link #mark(int)}/{@link #reset()}. |
||||
* @see LZ4BlockOutputStream |
||||
*/ |
||||
public final class LZ4BlockInputStream extends FilterInputStream { |
||||
|
||||
private final LZ4FastDecompressor decompressor; |
||||
private final Checksum checksum; |
||||
private final boolean stopOnEmptyBlock; |
||||
private byte[] buffer; |
||||
private byte[] compressedBuffer; |
||||
private int originalLen; |
||||
private int o; |
||||
private boolean finished; |
||||
|
||||
/** |
||||
* Creates a new LZ4 input stream to read from the specified underlying InputStream. |
||||
* |
||||
* @param in the {@link InputStream} to poll |
||||
* @param decompressor the {@link LZ4FastDecompressor decompressor} instance to |
||||
* use |
||||
* @param checksum the {@link Checksum} instance to use, must be |
||||
* equivalent to the instance which has been used to |
||||
* write the stream |
||||
* @param stopOnEmptyBlock whether read is stopped on an empty block |
||||
*/ |
||||
public LZ4BlockInputStream(InputStream in, LZ4FastDecompressor decompressor, Checksum checksum, boolean stopOnEmptyBlock) { |
||||
super(in); |
||||
this.decompressor = decompressor; |
||||
this.checksum = checksum; |
||||
this.stopOnEmptyBlock = stopOnEmptyBlock; |
||||
this.buffer = new byte[0]; |
||||
this.compressedBuffer = new byte[HEADER_LENGTH]; |
||||
o = originalLen = 0; |
||||
finished = false; |
||||
} |
||||
|
||||
/** |
||||
* Creates a new LZ4 input stream to read from the specified underlying InputStream. |
||||
* |
||||
* @param in the {@link InputStream} to poll |
||||
* @param decompressor the {@link LZ4FastDecompressor decompressor} instance to |
||||
* use |
||||
* @param checksum the {@link Checksum} instance to use, must be |
||||
* equivalent to the instance which has been used to |
||||
* write the stream |
||||
* |
||||
* @see #LZ4BlockInputStream(InputStream, LZ4FastDecompressor, Checksum, boolean) |
||||
*/ |
||||
public LZ4BlockInputStream(InputStream in, LZ4FastDecompressor decompressor, Checksum checksum) { |
||||
this(in, decompressor, checksum, true); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new LZ4 input stream to read from the specified underlying InputStream, using {@link XXHash32} for checksuming. |
||||
* |
||||
* @param in the {@link InputStream} to poll |
||||
* @param decompressor the {@link LZ4FastDecompressor decompressor} instance to |
||||
* use |
||||
* |
||||
* @see #LZ4BlockInputStream(InputStream, LZ4FastDecompressor, Checksum, boolean) |
||||
* @see StreamingXXHash32#asChecksum() |
||||
*/ |
||||
public LZ4BlockInputStream(InputStream in, LZ4FastDecompressor decompressor) { |
||||
this(in, decompressor, XXHashFactory.fastestInstance().newStreamingHash32(DEFAULT_SEED).asChecksum(), true); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new LZ4 input stream to read from the specified underlying InputStream, using {@link XXHash32} for checksuming. |
||||
* |
||||
* @param in the {@link InputStream} to poll |
||||
* @param stopOnEmptyBlock whether read is stopped on an empty block |
||||
* |
||||
* @see #LZ4BlockInputStream(InputStream, LZ4FastDecompressor, Checksum, boolean) |
||||
* @see LZ4Factory#fastestInstance() |
||||
* @see StreamingXXHash32#asChecksum() |
||||
*/ |
||||
public LZ4BlockInputStream(InputStream in, boolean stopOnEmptyBlock) { |
||||
this(in, LZ4Factory.fastestInstance().fastDecompressor(), XXHashFactory.fastestInstance().newStreamingHash32(DEFAULT_SEED).asChecksum(), stopOnEmptyBlock); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new LZ4 input stream to read from the specified underlying InputStream, using {@link XXHash32} for checksuming. |
||||
* |
||||
* @param in the {@link InputStream} to poll |
||||
* |
||||
* @see #LZ4BlockInputStream(InputStream, LZ4FastDecompressor) |
||||
* @see LZ4Factory#fastestInstance() |
||||
*/ |
||||
public LZ4BlockInputStream(InputStream in) { |
||||
this(in, LZ4Factory.fastestInstance().fastDecompressor()); |
||||
} |
||||
|
||||
@Override |
||||
public int available() throws IOException { |
||||
return originalLen - o; |
||||
} |
||||
|
||||
@Override |
||||
public int read() throws IOException { |
||||
if (finished) { |
||||
return -1; |
||||
} |
||||
if (o == originalLen) { |
||||
refill(); |
||||
} |
||||
if (finished) { |
||||
return -1; |
||||
} |
||||
return buffer[o++] & 0xFF; |
||||
} |
||||
|
||||
@Override |
||||
public int read(byte[] b, int off, int len) throws IOException { |
||||
SafeUtils.checkRange(b, off, len); |
||||
if (finished) { |
||||
return -1; |
||||
} |
||||
if (o == originalLen) { |
||||
refill(); |
||||
} |
||||
if (finished) { |
||||
return -1; |
||||
} |
||||
len = Math.min(len, originalLen - o); |
||||
System.arraycopy(buffer, o, b, off, len); |
||||
o += len; |
||||
return len; |
||||
} |
||||
|
||||
@Override |
||||
public int read(byte[] b) throws IOException { |
||||
return read(b, 0, b.length); |
||||
} |
||||
|
||||
@Override |
||||
public long skip(long n) throws IOException { |
||||
if (n <= 0 || finished) { |
||||
return 0; |
||||
} |
||||
if (o == originalLen) { |
||||
refill(); |
||||
} |
||||
if (finished) { |
||||
return 0; |
||||
} |
||||
final int skipped = (int) Math.min(n, originalLen - o); |
||||
o += skipped; |
||||
return skipped; |
||||
} |
||||
|
||||
private void refill() throws IOException { |
||||
try { |
||||
readFully(compressedBuffer, HEADER_LENGTH); |
||||
} catch (EOFException e) { |
||||
if (!stopOnEmptyBlock) { |
||||
finished = true; |
||||
} else { |
||||
throw e; |
||||
} |
||||
return; |
||||
} |
||||
for (int i = 0; i < MAGIC_LENGTH; ++i) { |
||||
if (compressedBuffer[i] != MAGIC[i]) { |
||||
throw new IOException("Stream is corrupted"); |
||||
} |
||||
} |
||||
final int token = compressedBuffer[MAGIC_LENGTH] & 0xFF; |
||||
final int compressionMethod = token & 0xF0; |
||||
final int compressionLevel = COMPRESSION_LEVEL_BASE + (token & 0x0F); |
||||
if (compressionMethod != COMPRESSION_METHOD_RAW && compressionMethod != COMPRESSION_METHOD_LZ4) { |
||||
throw new IOException("Stream is corrupted"); |
||||
} |
||||
final int compressedLen = SafeUtils.readIntLE(compressedBuffer, MAGIC_LENGTH + 1); |
||||
originalLen = SafeUtils.readIntLE(compressedBuffer, MAGIC_LENGTH + 5); |
||||
final int check = SafeUtils.readIntLE(compressedBuffer, MAGIC_LENGTH + 9); |
||||
assert HEADER_LENGTH == MAGIC_LENGTH + 13; |
||||
if (originalLen > 1 << compressionLevel |
||||
|| originalLen < 0 |
||||
|| compressedLen < 0 |
||||
|| (originalLen == 0 && compressedLen != 0) |
||||
|| (originalLen != 0 && compressedLen == 0) |
||||
|| (compressionMethod == COMPRESSION_METHOD_RAW && originalLen != compressedLen)) { |
||||
throw new IOException("Stream is corrupted"); |
||||
} |
||||
if (originalLen == 0 && compressedLen == 0) { |
||||
if (check != 0) { |
||||
throw new IOException("Stream is corrupted"); |
||||
} |
||||
if (!stopOnEmptyBlock) { |
||||
refill(); |
||||
} else { |
||||
finished = true; |
||||
} |
||||
return; |
||||
} |
||||
if (buffer.length < originalLen) { |
||||
buffer = new byte[Math.max(originalLen, buffer.length * 3 / 2)]; |
||||
} |
||||
switch (compressionMethod) { |
||||
case COMPRESSION_METHOD_RAW: |
||||
readFully(buffer, originalLen); |
||||
break; |
||||
case COMPRESSION_METHOD_LZ4: |
||||
if (compressedBuffer.length < compressedLen) { |
||||
compressedBuffer = new byte[Math.max(compressedLen, compressedBuffer.length * 3 / 2)]; |
||||
} |
||||
readFully(compressedBuffer, compressedLen); |
||||
try { |
||||
final int compressedLen2 = decompressor.decompress(compressedBuffer, 0, buffer, 0, originalLen); |
||||
if (compressedLen != compressedLen2) { |
||||
throw new IOException("Stream is corrupted"); |
||||
} |
||||
} catch (LZ4Exception e) { |
||||
throw new IOException("Stream is corrupted", e); |
||||
} |
||||
break; |
||||
default: |
||||
throw new AssertionError(); |
||||
} |
||||
checksum.reset(); |
||||
checksum.update(buffer, 0, originalLen); |
||||
if ((int) checksum.getValue() != check) { |
||||
throw new IOException("Stream is corrupted"); |
||||
} |
||||
o = 0; |
||||
} |
||||
|
||||
private void readFully(byte[] b, int len) throws IOException { |
||||
int read = 0; |
||||
while (read < len) { |
||||
final int r = in.read(b, read, len - read); |
||||
if (r < 0) { |
||||
throw new EOFException("Stream ended prematurely"); |
||||
} |
||||
read += r; |
||||
} |
||||
assert len == read; |
||||
} |
||||
|
||||
@Override |
||||
public boolean markSupported() { |
||||
return false; |
||||
} |
||||
|
||||
@SuppressWarnings("sync-override") |
||||
@Override |
||||
public void mark(int readlimit) { |
||||
// unsupported
|
||||
} |
||||
|
||||
@SuppressWarnings("sync-override") |
||||
@Override |
||||
public void reset() throws IOException { |
||||
throw new IOException("mark/reset not supported"); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName() + "(in=" + in |
||||
+ ", decompressor=" + decompressor + ", checksum=" + checksum + ")"; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,279 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.io.FilterOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.util.zip.Checksum; |
||||
|
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
import com.fr.third.net.jpountz.xxhash.StreamingXXHash32; |
||||
import com.fr.third.net.jpountz.xxhash.XXHashFactory; |
||||
|
||||
/** |
||||
* Streaming LZ4 (not compatible with the LZ4 Frame format). |
||||
* This class compresses data into fixed-size blocks of compressed data. |
||||
* This class uses its own format and is not compatible with the LZ4 Frame format. |
||||
* For interoperability with other LZ4 tools, use {@link LZ4FrameOutputStream}, |
||||
* which is compatible with the LZ4 Frame format. This class remains for backward compatibility. |
||||
* @see LZ4BlockInputStream |
||||
* @see LZ4FrameOutputStream |
||||
*/ |
||||
public final class LZ4BlockOutputStream extends FilterOutputStream { |
||||
|
||||
static final byte[] MAGIC = new byte[] { 'L', 'Z', '4', 'B', 'l', 'o', 'c', 'k' }; |
||||
static final int MAGIC_LENGTH = MAGIC.length; |
||||
|
||||
static final int HEADER_LENGTH = |
||||
MAGIC_LENGTH // magic bytes
|
||||
+ 1 // token
|
||||
+ 4 // compressed length
|
||||
+ 4 // decompressed length
|
||||
+ 4; // checksum
|
||||
|
||||
static final int COMPRESSION_LEVEL_BASE = 10; |
||||
static final int MIN_BLOCK_SIZE = 64; |
||||
static final int MAX_BLOCK_SIZE = 1 << (COMPRESSION_LEVEL_BASE + 0x0F); |
||||
|
||||
static final int COMPRESSION_METHOD_RAW = 0x10; |
||||
static final int COMPRESSION_METHOD_LZ4 = 0x20; |
||||
|
||||
static final int DEFAULT_SEED = 0x9747b28c; |
||||
|
||||
private static int compressionLevel(int blockSize) { |
||||
if (blockSize < MIN_BLOCK_SIZE) { |
||||
throw new IllegalArgumentException("blockSize must be >= " + MIN_BLOCK_SIZE + ", got " + blockSize); |
||||
} else if (blockSize > MAX_BLOCK_SIZE) { |
||||
throw new IllegalArgumentException("blockSize must be <= " + MAX_BLOCK_SIZE + ", got " + blockSize); |
||||
} |
||||
int compressionLevel = 32 - Integer.numberOfLeadingZeros(blockSize - 1); // ceil of log2
|
||||
assert (1 << compressionLevel) >= blockSize; |
||||
assert blockSize * 2 > (1 << compressionLevel); |
||||
compressionLevel = Math.max(0, compressionLevel - COMPRESSION_LEVEL_BASE); |
||||
assert compressionLevel >= 0 && compressionLevel <= 0x0F; |
||||
return compressionLevel; |
||||
} |
||||
|
||||
private final int blockSize; |
||||
private final int compressionLevel; |
||||
private final LZ4Compressor compressor; |
||||
private final Checksum checksum; |
||||
private final byte[] buffer; |
||||
private final byte[] compressedBuffer; |
||||
private final boolean syncFlush; |
||||
private boolean finished; |
||||
private int o; |
||||
|
||||
/** |
||||
* Creates a new {@link OutputStream} with configurable block size. Large |
||||
* blocks require more memory at compression and decompression time but |
||||
* should improve the compression ratio. |
||||
* |
||||
* @param out the {@link OutputStream} to feed |
||||
* @param blockSize the maximum number of bytes to try to compress at once, |
||||
* must be >= 64 and <= 32 M |
||||
* @param compressor the {@link LZ4Compressor} instance to use to compress |
||||
* data |
||||
* @param checksum the {@link Checksum} instance to use to check data for |
||||
* integrity. |
||||
* @param syncFlush true if pending data should also be flushed on {@link #flush()} |
||||
*/ |
||||
public LZ4BlockOutputStream(OutputStream out, int blockSize, LZ4Compressor compressor, Checksum checksum, boolean syncFlush) { |
||||
super(out); |
||||
this.blockSize = blockSize; |
||||
this.compressor = compressor; |
||||
this.checksum = checksum; |
||||
this.compressionLevel = compressionLevel(blockSize); |
||||
this.buffer = new byte[blockSize]; |
||||
final int compressedBlockSize = HEADER_LENGTH + compressor.maxCompressedLength(blockSize); |
||||
this.compressedBuffer = new byte[compressedBlockSize]; |
||||
this.syncFlush = syncFlush; |
||||
o = 0; |
||||
finished = false; |
||||
System.arraycopy(MAGIC, 0, compressedBuffer, 0, MAGIC_LENGTH); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new instance which checks stream integrity using |
||||
* {@link StreamingXXHash32} and doesn't sync flush. |
||||
* |
||||
* @param out the {@link OutputStream} to feed |
||||
* @param blockSize the maximum number of bytes to try to compress at once, |
||||
* must be >= 64 and <= 32 M |
||||
* @param compressor the {@link LZ4Compressor} instance to use to compress |
||||
* data |
||||
* |
||||
* @see #LZ4BlockOutputStream(OutputStream, int, LZ4Compressor, Checksum, boolean) |
||||
* @see StreamingXXHash32#asChecksum() |
||||
*/ |
||||
public LZ4BlockOutputStream(OutputStream out, int blockSize, LZ4Compressor compressor) { |
||||
this(out, blockSize, compressor, XXHashFactory.fastestInstance().newStreamingHash32(DEFAULT_SEED).asChecksum(), false); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new instance which compresses with the standard LZ4 compression |
||||
* algorithm. |
||||
* |
||||
* @param out the {@link OutputStream} to feed |
||||
* @param blockSize the maximum number of bytes to try to compress at once, |
||||
* must be >= 64 and <= 32 M |
||||
* |
||||
* @see #LZ4BlockOutputStream(OutputStream, int, LZ4Compressor) |
||||
* @see LZ4Factory#fastCompressor() |
||||
*/ |
||||
public LZ4BlockOutputStream(OutputStream out, int blockSize) { |
||||
this(out, blockSize, LZ4Factory.fastestInstance().fastCompressor()); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new instance which compresses into blocks of 64 KB. |
||||
* |
||||
* @param out the {@link OutputStream} to feed |
||||
* |
||||
* @see #LZ4BlockOutputStream(OutputStream, int) |
||||
*/ |
||||
public LZ4BlockOutputStream(OutputStream out) { |
||||
this(out, 1 << 16); |
||||
} |
||||
|
||||
private void ensureNotFinished() { |
||||
if (finished) { |
||||
throw new IllegalStateException("This stream is already closed"); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void write(int b) throws IOException { |
||||
ensureNotFinished(); |
||||
if (o == blockSize) { |
||||
flushBufferedData(); |
||||
} |
||||
buffer[o++] = (byte) b; |
||||
} |
||||
|
||||
@Override |
||||
public void write(byte[] b, int off, int len) throws IOException { |
||||
SafeUtils.checkRange(b, off, len); |
||||
ensureNotFinished(); |
||||
|
||||
while (o + len > blockSize) { |
||||
final int l = blockSize - o; |
||||
System.arraycopy(b, off, buffer, o, blockSize - o); |
||||
o = blockSize; |
||||
flushBufferedData(); |
||||
off += l; |
||||
len -= l; |
||||
} |
||||
System.arraycopy(b, off, buffer, o, len); |
||||
o += len; |
||||
} |
||||
|
||||
@Override |
||||
public void write(byte[] b) throws IOException { |
||||
ensureNotFinished(); |
||||
write(b, 0, b.length); |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
if (!finished) { |
||||
finish(); |
||||
} |
||||
if (out != null) { |
||||
out.close(); |
||||
out = null; |
||||
} |
||||
} |
||||
|
||||
private void flushBufferedData() throws IOException { |
||||
if (o == 0) { |
||||
return; |
||||
} |
||||
checksum.reset(); |
||||
checksum.update(buffer, 0, o); |
||||
final int check = (int) checksum.getValue(); |
||||
int compressedLength = compressor.compress(buffer, 0, o, compressedBuffer, HEADER_LENGTH); |
||||
final int compressMethod; |
||||
if (compressedLength >= o) { |
||||
compressMethod = COMPRESSION_METHOD_RAW; |
||||
compressedLength = o; |
||||
System.arraycopy(buffer, 0, compressedBuffer, HEADER_LENGTH, o); |
||||
} else { |
||||
compressMethod = COMPRESSION_METHOD_LZ4; |
||||
} |
||||
|
||||
compressedBuffer[MAGIC_LENGTH] = (byte) (compressMethod | compressionLevel); |
||||
writeIntLE(compressedLength, compressedBuffer, MAGIC_LENGTH + 1); |
||||
writeIntLE(o, compressedBuffer, MAGIC_LENGTH + 5); |
||||
writeIntLE(check, compressedBuffer, MAGIC_LENGTH + 9); |
||||
assert MAGIC_LENGTH + 13 == HEADER_LENGTH; |
||||
out.write(compressedBuffer, 0, HEADER_LENGTH + compressedLength); |
||||
o = 0; |
||||
} |
||||
|
||||
/** |
||||
* Flushes this compressed {@link OutputStream}. |
||||
* |
||||
* If the stream has been created with <code>syncFlush=true</code>, pending |
||||
* data will be compressed and appended to the underlying {@link OutputStream} |
||||
* before calling {@link OutputStream#flush()} on the underlying stream. |
||||
* Otherwise, this method just flushes the underlying stream, so pending |
||||
* data might not be available for reading until {@link #finish()} or |
||||
* {@link #close()} is called. |
||||
*/ |
||||
@Override |
||||
public void flush() throws IOException { |
||||
if (out != null) { |
||||
if (syncFlush) { |
||||
flushBufferedData(); |
||||
} |
||||
out.flush(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Same as {@link #close()} except that it doesn't close the underlying stream. |
||||
* This can be useful if you want to keep on using the underlying stream. |
||||
* |
||||
* @throws IOException if an I/O error occurs. |
||||
*/ |
||||
public void finish() throws IOException { |
||||
ensureNotFinished(); |
||||
flushBufferedData(); |
||||
compressedBuffer[MAGIC_LENGTH] = (byte) (COMPRESSION_METHOD_RAW | compressionLevel); |
||||
writeIntLE(0, compressedBuffer, MAGIC_LENGTH + 1); |
||||
writeIntLE(0, compressedBuffer, MAGIC_LENGTH + 5); |
||||
writeIntLE(0, compressedBuffer, MAGIC_LENGTH + 9); |
||||
assert MAGIC_LENGTH + 13 == HEADER_LENGTH; |
||||
out.write(compressedBuffer, 0, HEADER_LENGTH); |
||||
finished = true; |
||||
out.flush(); |
||||
} |
||||
|
||||
private static void writeIntLE(int i, byte[] buf, int off) { |
||||
buf[off++] = (byte) i; |
||||
buf[off++] = (byte) (i >>> 8); |
||||
buf[off++] = (byte) (i >>> 16); |
||||
buf[off++] = (byte) (i >>> 24); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName() + "(out=" + out + ", blockSize=" + blockSize |
||||
+ ", compressor=" + compressor + ", checksum=" + checksum + ")"; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,237 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.COPY_LENGTH; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.LAST_LITERALS; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.ML_BITS; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.ML_MASK; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.RUN_MASK; |
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.readByte; |
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.readInt; |
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.readLong; |
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.writeByte; |
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.writeInt; |
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.writeLong; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.nio.ByteOrder; |
||||
|
||||
enum LZ4ByteBufferUtils { |
||||
; |
||||
static int hash(ByteBuffer buf, int i) { |
||||
return LZ4Utils.hash(readInt(buf, i)); |
||||
} |
||||
|
||||
static int hash64k(ByteBuffer buf, int i) { |
||||
return LZ4Utils.hash64k(readInt(buf, i)); |
||||
} |
||||
|
||||
static boolean readIntEquals(ByteBuffer buf, int i, int j) { |
||||
return buf.getInt(i) == buf.getInt(j); |
||||
} |
||||
|
||||
static void safeIncrementalCopy(ByteBuffer dest, int matchOff, int dOff, int matchLen) { |
||||
for (int i = 0; i < matchLen; ++i) { |
||||
dest.put(dOff + i, dest.get(matchOff + i)); |
||||
} |
||||
} |
||||
|
||||
static void wildIncrementalCopy(ByteBuffer dest, int matchOff, int dOff, int matchCopyEnd) { |
||||
if (dOff - matchOff < 4) { |
||||
for (int i = 0; i < 4; ++i) { |
||||
writeByte(dest, dOff+i, readByte(dest, matchOff+i)); |
||||
} |
||||
dOff += 4; |
||||
matchOff += 4; |
||||
int dec = 0; |
||||
assert dOff >= matchOff && dOff - matchOff < 8; |
||||
switch (dOff - matchOff) { |
||||
case 1: |
||||
matchOff -= 3; |
||||
break; |
||||
case 2: |
||||
matchOff -= 2; |
||||
break; |
||||
case 3: |
||||
matchOff -= 3; |
||||
dec = -1; |
||||
break; |
||||
case 5: |
||||
dec = 1; |
||||
break; |
||||
case 6: |
||||
dec = 2; |
||||
break; |
||||
case 7: |
||||
dec = 3; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
writeInt(dest, dOff, readInt(dest, matchOff)); |
||||
dOff += 4; |
||||
matchOff -= dec; |
||||
} else if (dOff - matchOff < COPY_LENGTH) { |
||||
writeLong(dest, dOff, readLong(dest, matchOff)); |
||||
dOff += dOff - matchOff; |
||||
} |
||||
while (dOff < matchCopyEnd) { |
||||
writeLong(dest, dOff, readLong(dest, matchOff)); |
||||
dOff += 8; |
||||
matchOff += 8; |
||||
} |
||||
} |
||||
|
||||
static int commonBytes(ByteBuffer src, int ref, int sOff, int srcLimit) { |
||||
int matchLen = 0; |
||||
while (sOff <= srcLimit - 8) { |
||||
if (readLong(src, sOff) == readLong(src, ref)) { |
||||
matchLen += 8; |
||||
ref += 8; |
||||
sOff += 8; |
||||
} else { |
||||
final int zeroBits; |
||||
if (src.order() == ByteOrder.BIG_ENDIAN) { |
||||
zeroBits = Long.numberOfLeadingZeros(readLong(src, sOff) ^ readLong(src, ref)); |
||||
} else { |
||||
zeroBits = Long.numberOfTrailingZeros(readLong(src, sOff) ^ readLong(src, ref)); |
||||
} |
||||
return matchLen + (zeroBits >>> 3); |
||||
} |
||||
} |
||||
while (sOff < srcLimit && readByte(src, ref++) == readByte(src, sOff++)) { |
||||
++matchLen; |
||||
} |
||||
return matchLen; |
||||
} |
||||
|
||||
static int commonBytesBackward(ByteBuffer b, int o1, int o2, int l1, int l2) { |
||||
int count = 0; |
||||
while (o1 > l1 && o2 > l2 && b.get(--o1) == b.get(--o2)) { |
||||
++count; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
static void safeArraycopy(ByteBuffer src, int sOff, ByteBuffer dest, int dOff, int len) { |
||||
for (int i = 0; i < len; ++i) { |
||||
dest.put(dOff + i, src.get(sOff + i)); |
||||
} |
||||
} |
||||
|
||||
static void wildArraycopy(ByteBuffer src, int sOff, ByteBuffer dest, int dOff, int len) { |
||||
assert src.order().equals(dest.order()); |
||||
try { |
||||
for (int i = 0; i < len; i += 8) { |
||||
dest.putLong(dOff + i, src.getLong(sOff + i)); |
||||
} |
||||
} catch (IndexOutOfBoundsException e) { |
||||
throw new LZ4Exception("Malformed input at offset " + sOff); |
||||
} |
||||
} |
||||
|
||||
static int encodeSequence(ByteBuffer src, int anchor, int matchOff, int matchRef, int matchLen, ByteBuffer dest, int dOff, int destEnd) { |
||||
final int runLen = matchOff - anchor; |
||||
final int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
int token; |
||||
if (runLen >= RUN_MASK) { |
||||
token = (byte) (RUN_MASK << ML_BITS); |
||||
dOff = writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
token = runLen << ML_BITS; |
||||
} |
||||
|
||||
// copy literals
|
||||
wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
// encode offset
|
||||
final int matchDec = matchOff - matchRef; |
||||
dest.put(dOff++, (byte) matchDec); |
||||
dest.put(dOff++, (byte) (matchDec >>> 8)); |
||||
|
||||
// encode match len
|
||||
matchLen -= 4; |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
if (matchLen >= ML_MASK) { |
||||
token |= ML_MASK; |
||||
dOff = writeLen(matchLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
token |= matchLen; |
||||
} |
||||
|
||||
dest.put(tokenOff, (byte) token); |
||||
|
||||
return dOff; |
||||
} |
||||
|
||||
static int lastLiterals(ByteBuffer src, int sOff, int srcLen, ByteBuffer dest, int dOff, int destEnd) { |
||||
final int runLen = srcLen; |
||||
|
||||
if (dOff + runLen + 1 + (runLen + 255 - RUN_MASK) / 255 > destEnd) { |
||||
throw new LZ4Exception(); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
dest.put(dOff++, (byte) (RUN_MASK << ML_BITS)); |
||||
dOff = writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
dest.put(dOff++, (byte) (runLen << ML_BITS)); |
||||
} |
||||
// copy literals
|
||||
safeArraycopy(src, sOff, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
return dOff; |
||||
} |
||||
|
||||
static int writeLen(int len, ByteBuffer dest, int dOff) { |
||||
while (len >= 0xFF) { |
||||
dest.put(dOff++, (byte) 0xFF); |
||||
len -= 0xFF; |
||||
} |
||||
dest.put(dOff++, (byte) len); |
||||
return dOff; |
||||
} |
||||
|
||||
static class Match { |
||||
int start, ref, len; |
||||
|
||||
void fix(int correction) { |
||||
start += correction; |
||||
ref += correction; |
||||
len -= correction; |
||||
} |
||||
|
||||
int end() { |
||||
return start + len; |
||||
} |
||||
} |
||||
|
||||
static void copyTo(Match m1, Match m2) { |
||||
m2.len = m1.len; |
||||
m2.start = m1.start; |
||||
m2.ref = m1.ref; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,168 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.util.Arrays; |
||||
|
||||
/** |
||||
* LZ4 compressor. |
||||
* <p> |
||||
* Instances of this class are thread-safe. |
||||
*/ |
||||
public abstract class LZ4Compressor { |
||||
|
||||
/** |
||||
* Returns the maximum compressed length for an input of size <code>length</code>. |
||||
* |
||||
* @param length the input size in bytes |
||||
* @return the maximum compressed length in bytes |
||||
*/ |
||||
@SuppressWarnings("static-method") |
||||
public final int maxCompressedLength(int length) { |
||||
return LZ4Utils.maxCompressedLength(length); |
||||
} |
||||
|
||||
/** |
||||
* Compresses <code>src[srcOff:srcOff+srcLen]</code> into |
||||
* <code>dest[destOff:destOff+maxDestLen]</code> and returns the compressed |
||||
* length. |
||||
* |
||||
* This method will throw a {@link LZ4Exception} if this compressor is unable |
||||
* to compress the input into less than <code>maxDestLen</code> bytes. To |
||||
* prevent this exception to be thrown, you should make sure that |
||||
* <code>maxDestLen >= maxCompressedLength(srcLen)</code>. |
||||
* |
||||
* @param src the source data |
||||
* @param srcOff the start offset in src |
||||
* @param srcLen the number of bytes to compress |
||||
* @param dest the destination buffer |
||||
* @param destOff the start offset in dest |
||||
* @param maxDestLen the maximum number of bytes to write in dest |
||||
* @throws LZ4Exception if maxDestLen is too small |
||||
* @return the compressed size |
||||
*/ |
||||
public abstract int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen); |
||||
|
||||
/** |
||||
* Compresses <code>src[srcOff:srcOff+srcLen]</code> into |
||||
* <code>dest[destOff:destOff+maxDestLen]</code> and returns the compressed |
||||
* length. |
||||
* |
||||
* This method will throw a {@link LZ4Exception} if this compressor is unable |
||||
* to compress the input into less than <code>maxDestLen</code> bytes. To |
||||
* prevent this exception to be thrown, you should make sure that |
||||
* <code>maxDestLen >= maxCompressedLength(srcLen)</code>. |
||||
* |
||||
* {@link ByteBuffer} positions remain unchanged. |
||||
* |
||||
* @param src the source data |
||||
* @param srcOff the start offset in src |
||||
* @param srcLen the number of bytes to compress |
||||
* @param dest the destination buffer |
||||
* @param destOff the start offset in dest |
||||
* @param maxDestLen the maximum number of bytes to write in dest |
||||
* @throws LZ4Exception if maxDestLen is too small |
||||
* @return the compressed size |
||||
*/ |
||||
public abstract int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen); |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #compress(byte[], int, int, byte[], int, int) compress(src, srcOff, srcLen, dest, destOff, dest.length - destOff)}. |
||||
* |
||||
* @param src the source data |
||||
* @param srcOff the start offset in src |
||||
* @param srcLen the number of bytes to compress |
||||
* @param dest the destination buffer |
||||
* @param destOff the start offset in dest |
||||
* @throws LZ4Exception if dest is too small |
||||
* @return the compressed size |
||||
*/ |
||||
public final int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff) { |
||||
return compress(src, srcOff, srcLen, dest, destOff, dest.length - destOff); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #compress(byte[], int, int, byte[], int) compress(src, 0, src.length, dest, 0)}. |
||||
* |
||||
* @param src the source data |
||||
* @param dest the destination buffer |
||||
* @throws LZ4Exception if dest is too small |
||||
* @return the compressed size |
||||
*/ |
||||
public final int compress(byte[] src, byte[] dest) { |
||||
return compress(src, 0, src.length, dest, 0); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method which returns <code>src[srcOff:srcOff+srcLen]</code> |
||||
* compressed. |
||||
* <p><b><span style="color:red">Warning</span></b>: this method has an |
||||
* important overhead due to the fact that it needs to allocate a buffer to |
||||
* compress into, and then needs to resize this buffer to the actual |
||||
* compressed length.</p> |
||||
* <p>Here is how this method is implemented:</p> |
||||
* <pre> |
||||
* final int maxCompressedLength = maxCompressedLength(srcLen); |
||||
* final byte[] compressed = new byte[maxCompressedLength]; |
||||
* final int compressedLength = compress(src, srcOff, srcLen, compressed, 0); |
||||
* return Arrays.copyOf(compressed, compressedLength); |
||||
* </pre> |
||||
* |
||||
* @param src the source data |
||||
* @param srcOff the start offset in src |
||||
* @param srcLen the number of bytes to compress |
||||
* @return the compressed data |
||||
*/ |
||||
public final byte[] compress(byte[] src, int srcOff, int srcLen) { |
||||
final int maxCompressedLength = maxCompressedLength(srcLen); |
||||
final byte[] compressed = new byte[maxCompressedLength]; |
||||
final int compressedLength = compress(src, srcOff, srcLen, compressed, 0); |
||||
return Arrays.copyOf(compressed, compressedLength); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #compress(byte[], int, int) compress(src, 0, src.length)}. |
||||
* |
||||
* @param src the source data |
||||
* @return the compressed data |
||||
*/ |
||||
public final byte[] compress(byte[] src) { |
||||
return compress(src, 0, src.length); |
||||
} |
||||
|
||||
/** |
||||
* Compresses <code>src</code> into <code>dest</code>. Calling this method |
||||
* will update the positions of both {@link ByteBuffer}s. |
||||
* |
||||
* @param src the source data |
||||
* @param dest the destination buffer |
||||
* @throws LZ4Exception if dest is too small |
||||
*/ |
||||
public final void compress(ByteBuffer src, ByteBuffer dest) { |
||||
final int cpLen = compress(src, src.position(), src.remaining(), dest, dest.position(), dest.remaining()); |
||||
src.position(src.limit()); |
||||
dest.position(dest.position() + cpLen); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,53 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
|
||||
enum LZ4Constants { |
||||
; |
||||
|
||||
static final int DEFAULT_COMPRESSION_LEVEL = 8+1; |
||||
static final int MAX_COMPRESSION_LEVEL = 16+1; |
||||
|
||||
static final int MEMORY_USAGE = 14; |
||||
static final int NOT_COMPRESSIBLE_DETECTION_LEVEL = 6; |
||||
|
||||
static final int MIN_MATCH = 4; |
||||
|
||||
static final int HASH_LOG = MEMORY_USAGE - 2; |
||||
static final int HASH_TABLE_SIZE = 1 << HASH_LOG; |
||||
|
||||
static final int SKIP_STRENGTH = Math.max(NOT_COMPRESSIBLE_DETECTION_LEVEL, 2); |
||||
static final int COPY_LENGTH = 8; |
||||
static final int LAST_LITERALS = 5; |
||||
static final int MF_LIMIT = COPY_LENGTH + MIN_MATCH; |
||||
static final int MIN_LENGTH = MF_LIMIT + 1; |
||||
|
||||
static final int MAX_DISTANCE = 1 << 16; |
||||
|
||||
static final int ML_BITS = 4; |
||||
static final int ML_MASK = (1 << ML_BITS) - 1; |
||||
static final int RUN_BITS = 8 - ML_BITS; |
||||
static final int RUN_MASK = (1 << RUN_BITS) - 1; |
||||
|
||||
static final int LZ4_64K_LIMIT = (1 << 16) + (MF_LIMIT - 1); |
||||
static final int HASH_LOG_64K = HASH_LOG + 1; |
||||
static final int HASH_TABLE_SIZE_64K = 1 << HASH_LOG_64K; |
||||
|
||||
static final int HASH_LOG_HC = 15; |
||||
static final int HASH_TABLE_SIZE_HC = 1 << HASH_LOG_HC; |
||||
static final int OPTIMAL_ML = ML_MASK - 1 + MIN_MATCH; |
||||
|
||||
} |
@ -0,0 +1,25 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* @deprecated Use {@link LZ4FastDecompressor} instead. |
||||
*/ |
||||
@Deprecated |
||||
public interface LZ4Decompressor { |
||||
|
||||
int decompress(byte[] src, int srcOff, byte[] dest, int destOff, int destLen); |
||||
|
||||
} |
@ -0,0 +1,36 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* LZ4 compression or decompression error. |
||||
*/ |
||||
public class LZ4Exception extends RuntimeException { |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
public LZ4Exception(String msg, Throwable t) { |
||||
super(msg, t); |
||||
} |
||||
|
||||
public LZ4Exception(String msg) { |
||||
super(msg); |
||||
} |
||||
|
||||
public LZ4Exception() { |
||||
super(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,309 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.lang.reflect.Constructor; |
||||
import java.lang.reflect.Field; |
||||
import java.lang.reflect.InvocationTargetException; |
||||
import java.util.Arrays; |
||||
|
||||
import com.fr.third.net.jpountz.util.Native; |
||||
import com.fr.third.net.jpountz.util.Utils; |
||||
|
||||
/** |
||||
* Entry point for the LZ4 API. |
||||
* <p> |
||||
* This class has 3 instances<ul> |
||||
* <li>a {@link #nativeInstance() native} instance which is a JNI binding to |
||||
* <a href="http://code.google.com/p/lz4/">the original LZ4 C implementation</a>. |
||||
* <li>a {@link #safeInstance() safe Java} instance which is a pure Java port |
||||
* of the original C library,</li> |
||||
* <li>an {@link #unsafeInstance() unsafe Java} instance which is a Java port |
||||
* using the unofficial {@link sun.misc.Unsafe} API. |
||||
* </ul> |
||||
* <p> |
||||
* Only the {@link #safeInstance() safe instance} is guaranteed to work on your |
||||
* JVM, as a consequence it is advised to use the {@link #fastestInstance()} or |
||||
* {@link #fastestJavaInstance()} to pull a {@link LZ4Factory} instance. |
||||
* <p> |
||||
* All methods from this class are very costly, so you should get an instance |
||||
* once, and then reuse it whenever possible. This is typically done by storing |
||||
* a {@link LZ4Factory} instance in a static field. |
||||
*/ |
||||
public final class LZ4Factory { |
||||
|
||||
private static LZ4Factory instance(String impl) { |
||||
try { |
||||
return new LZ4Factory(impl); |
||||
} catch (Exception e) { |
||||
throw new AssertionError(e); |
||||
} |
||||
} |
||||
|
||||
private static LZ4Factory NATIVE_INSTANCE, |
||||
JAVA_UNSAFE_INSTANCE, |
||||
JAVA_SAFE_INSTANCE; |
||||
|
||||
/** |
||||
* Returns a {@link LZ4Factory} instance that returns compressors and |
||||
* decompressors that are native bindings to the original C library. |
||||
* <p> |
||||
* Please note that this instance has some traps you should be aware of:<ol> |
||||
* <li>Upon loading this instance, files will be written to the temporary |
||||
* directory of the system. Although these files are supposed to be deleted |
||||
* when the JVM exits, they might remain on systems that don't support |
||||
* removal of files being used such as Windows. |
||||
* <li>The instance can only be loaded once per JVM. This can be a problem |
||||
* if your application uses multiple class loaders (such as most servlet |
||||
* containers): this instance will only be available to the children of the |
||||
* class loader which has loaded it. As a consequence, it is advised to |
||||
* either not use this instance in webapps or to put this library in the lib |
||||
* directory of your servlet container so that it is loaded by the system |
||||
* class loader. |
||||
* </ol> |
||||
* |
||||
* @return a {@link LZ4Factory} instance that returns compressors and |
||||
* decompressors that are native bindings to the original C library |
||||
*/ |
||||
public static synchronized LZ4Factory nativeInstance() { |
||||
if (NATIVE_INSTANCE == null) { |
||||
NATIVE_INSTANCE = instance("JNI"); |
||||
} |
||||
return NATIVE_INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link LZ4Factory} instance that returns compressors and |
||||
* decompressors that are written with Java's official API. |
||||
* |
||||
* @return a {@link LZ4Factory} instance that returns compressors and |
||||
* decompressors that are written with Java's official API. |
||||
*/ |
||||
public static synchronized LZ4Factory safeInstance() { |
||||
if (JAVA_SAFE_INSTANCE == null) { |
||||
JAVA_SAFE_INSTANCE = instance("JavaSafe"); |
||||
} |
||||
return JAVA_SAFE_INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link LZ4Factory} instance that returns compressors and |
||||
* decompressors that may use {@link sun.misc.Unsafe} to speed up compression |
||||
* and decompression. |
||||
* |
||||
* @return a {@link LZ4Factory} instance that returns compressors and |
||||
* decompressors that may use {@link sun.misc.Unsafe} to speed up compression |
||||
* and decompression. |
||||
*/ |
||||
public static synchronized LZ4Factory unsafeInstance() { |
||||
if (JAVA_UNSAFE_INSTANCE == null) { |
||||
JAVA_UNSAFE_INSTANCE = instance("JavaUnsafe"); |
||||
} |
||||
return JAVA_UNSAFE_INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* Returns the fastest available {@link LZ4Factory} instance which does not |
||||
* rely on JNI bindings. It first tries to load the |
||||
* {@link #unsafeInstance() unsafe instance}, and then the |
||||
* {@link #safeInstance() safe Java instance} if the JVM doesn't have a |
||||
* working {@link sun.misc.Unsafe}. |
||||
* |
||||
* @return the fastest available {@link LZ4Factory} instance which does not |
||||
* rely on JNI bindings. |
||||
*/ |
||||
public static LZ4Factory fastestJavaInstance() { |
||||
if (Utils.isUnalignedAccessAllowed()) { |
||||
try { |
||||
return unsafeInstance(); |
||||
} catch (Throwable t) { |
||||
return safeInstance(); |
||||
} |
||||
} else { |
||||
return safeInstance(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the fastest available {@link LZ4Factory} instance. If the class
|
||||
* loader is the system class loader and if the |
||||
* {@link #nativeInstance() native instance} loads successfully, then the |
||||
* {@link #nativeInstance() native instance} is returned, otherwise the |
||||
* {@link #fastestJavaInstance() fastest Java instance} is returned. |
||||
* <p> |
||||
* Please read {@link #nativeInstance() javadocs of nativeInstance()} before |
||||
* using this method. |
||||
* |
||||
* @return the fastest available {@link LZ4Factory} instance |
||||
*/ |
||||
public static LZ4Factory fastestInstance() { |
||||
if (Native.isLoaded() |
||||
|| Native.class.getClassLoader() == ClassLoader.getSystemClassLoader()) { |
||||
try { |
||||
return nativeInstance(); |
||||
} catch (Throwable t) { |
||||
return fastestJavaInstance(); |
||||
} |
||||
} else { |
||||
return fastestJavaInstance(); |
||||
} |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private static <T> T classInstance(String cls) throws NoSuchFieldException, SecurityException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException { |
||||
ClassLoader loader = LZ4Factory.class.getClassLoader(); |
||||
loader = loader == null ? ClassLoader.getSystemClassLoader() : loader; |
||||
final Class<?> c = loader.loadClass(cls); |
||||
Field f = c.getField("INSTANCE"); |
||||
return (T) f.get(null); |
||||
} |
||||
|
||||
private final String impl; |
||||
private final LZ4Compressor fastCompressor; |
||||
private final LZ4Compressor highCompressor; |
||||
private final LZ4FastDecompressor fastDecompressor; |
||||
private final LZ4SafeDecompressor safeDecompressor; |
||||
private final LZ4Compressor[] highCompressors = new LZ4Compressor[LZ4Constants.MAX_COMPRESSION_LEVEL+1]; |
||||
|
||||
private LZ4Factory(String impl) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException { |
||||
this.impl = impl; |
||||
fastCompressor = classInstance("com.fr.third.net.jpountz.lz4.LZ4" + impl + "Compressor"); |
||||
highCompressor = classInstance("com.fr.third.net.jpountz.lz4.LZ4HC" + impl + "Compressor"); |
||||
fastDecompressor = classInstance("com.fr.third.net.jpountz.lz4.LZ4" + impl + "FastDecompressor"); |
||||
safeDecompressor = classInstance("com.fr.third.net.jpountz.lz4.LZ4" + impl + "SafeDecompressor"); |
||||
Constructor<? extends LZ4Compressor> highConstructor = highCompressor.getClass().getDeclaredConstructor(int.class); |
||||
highCompressors[LZ4Constants.DEFAULT_COMPRESSION_LEVEL] = highCompressor; |
||||
for(int level = 1; level <= LZ4Constants.MAX_COMPRESSION_LEVEL; level++) { |
||||
if(level == LZ4Constants.DEFAULT_COMPRESSION_LEVEL) continue; |
||||
highCompressors[level] = highConstructor.newInstance(level); |
||||
} |
||||
|
||||
// quickly test that everything works as expected
|
||||
final byte[] original = new byte[] {'a','b','c','d',' ',' ',' ',' ',' ',' ','a','b','c','d','e','f','g','h','i','j'}; |
||||
for (LZ4Compressor compressor : Arrays.asList(fastCompressor, highCompressor)) { |
||||
final int maxCompressedLength = compressor.maxCompressedLength(original.length); |
||||
final byte[] compressed = new byte[maxCompressedLength]; |
||||
final int compressedLength = compressor.compress(original, 0, original.length, compressed, 0, maxCompressedLength); |
||||
final byte[] restored = new byte[original.length]; |
||||
fastDecompressor.decompress(compressed, 0, restored, 0, original.length); |
||||
if (!Arrays.equals(original, restored)) { |
||||
throw new AssertionError(); |
||||
} |
||||
Arrays.fill(restored, (byte) 0); |
||||
final int decompressedLength = safeDecompressor.decompress(compressed, 0, compressedLength, restored, 0); |
||||
if (decompressedLength != original.length || !Arrays.equals(original, restored)) { |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
/** |
||||
* Returns a blazing fast {@link LZ4Compressor}. |
||||
* |
||||
* @return a blazing fast {@link LZ4Compressor} |
||||
*/ |
||||
public LZ4Compressor fastCompressor() { |
||||
return fastCompressor; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link LZ4Compressor} which requires more memory than |
||||
* {@link #fastCompressor()} and is slower but compresses more efficiently. |
||||
* |
||||
* @return a {@link LZ4Compressor} which requires more memory than |
||||
* {@link #fastCompressor()} and is slower but compresses more efficiently. |
||||
*/ |
||||
public LZ4Compressor highCompressor() { |
||||
return highCompressor; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link LZ4Compressor} which requires more memory than |
||||
* {@link #fastCompressor()} and is slower but compresses more efficiently. |
||||
* The compression level can be customized. |
||||
* <p>For current implementations, the following is true about compression level:<ol> |
||||
* <li>It should be in range [1, 17]</li> |
||||
* <li>A compression level higher than 17 would be treated as 17.</li> |
||||
* <li>A compression level lower than 1 would be treated as 9.</li> |
||||
* </ol> |
||||
* |
||||
* @param compressionLevel the compression level between [1, 17]; the higher the level, the higher the compression ratio |
||||
* @return a {@link LZ4Compressor} which requires more memory than |
||||
* {@link #fastCompressor()} and is slower but compresses more efficiently. |
||||
*/ |
||||
public LZ4Compressor highCompressor(int compressionLevel) { |
||||
if(compressionLevel > LZ4Constants.MAX_COMPRESSION_LEVEL) { |
||||
compressionLevel = LZ4Constants.MAX_COMPRESSION_LEVEL; |
||||
} else if (compressionLevel < 1) { |
||||
compressionLevel = LZ4Constants.DEFAULT_COMPRESSION_LEVEL; |
||||
} |
||||
return highCompressors[compressionLevel]; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link LZ4FastDecompressor} instance. |
||||
* |
||||
* @return a {@link LZ4FastDecompressor} instance |
||||
*/ |
||||
public LZ4FastDecompressor fastDecompressor() { |
||||
return fastDecompressor; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link LZ4SafeDecompressor} instance. |
||||
* |
||||
* @return a {@link LZ4SafeDecompressor} instance |
||||
*/ |
||||
public LZ4SafeDecompressor safeDecompressor() { |
||||
return safeDecompressor; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link LZ4UnknownSizeDecompressor} instance. |
||||
* @deprecated use {@link #safeDecompressor()} |
||||
* |
||||
* @return a {@link LZ4UnknownSizeDecompressor} instance |
||||
*/ |
||||
public LZ4UnknownSizeDecompressor unknownSizeDecompressor() { |
||||
return safeDecompressor(); |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link LZ4Decompressor} instance. |
||||
* @deprecated use {@link #fastDecompressor()} |
||||
* |
||||
* @return a {@link LZ4Decompressor} instance |
||||
*/ |
||||
public LZ4Decompressor decompressor() { |
||||
return fastDecompressor(); |
||||
} |
||||
|
||||
/** |
||||
* Prints the fastest instance. |
||||
* |
||||
* @param args no argument required |
||||
*/ |
||||
public static void main(String[] args) { |
||||
System.out.println("Fastest instance is " + fastestInstance()); |
||||
System.out.println("Fastest Java instance is " + fastestJavaInstance()); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName() + ":" + impl; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,135 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* LZ4 decompressor that requires the size of the original input to be known. |
||||
* Use {@link LZ4SafeDecompressor} if you only know the size of the |
||||
* compressed stream. |
||||
* <p> |
||||
* Instances of this class are thread-safe. |
||||
*/ |
||||
public abstract class LZ4FastDecompressor implements LZ4Decompressor { |
||||
|
||||
/** Decompresses <code>src[srcOff:]</code> into <code>dest[destOff:destOff+destLen]</code> |
||||
* and returns the number of bytes read from <code>src</code>. |
||||
* <code>destLen</code> must be exactly the size of the decompressed data. |
||||
* |
||||
* @param src the compressed data |
||||
* @param srcOff the start offset in src |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @param destOff the start offset in dest |
||||
* @param destLen the <b>exact</b> size of the original input |
||||
* @return the number of bytes read to restore the original input |
||||
*/ |
||||
public abstract int decompress(byte[] src, int srcOff, byte[] dest, int destOff, int destLen); |
||||
|
||||
/** Decompresses <code>src[srcOff:]</code> into <code>dest[destOff:destOff+destLen]</code> |
||||
* and returns the number of bytes read from <code>src</code>. |
||||
* <code>destLen</code> must be exactly the size of the decompressed data. |
||||
* The positions and limits of the {@link ByteBuffer}s remain unchanged. |
||||
* |
||||
* @param src the compressed data |
||||
* @param srcOff the start offset in src |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @param destOff the start offset in dest |
||||
* @param destLen the <b>exact</b> size of the original input |
||||
* @return the number of bytes read to restore the original input |
||||
*/ |
||||
public abstract int decompress(ByteBuffer src, int srcOff, ByteBuffer dest, int destOff, int destLen); |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #decompress(byte[], int, byte[], int, int) decompress(src, 0, dest, 0, destLen)}. |
||||
* |
||||
* @param src the compressed data |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @param destLen the <b>exact</b> size of the original input |
||||
* @return the number of bytes read to restore the original input |
||||
*/ |
||||
public final int decompress(byte[] src, byte[] dest, int destLen) { |
||||
return decompress(src, 0, dest, 0, destLen); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #decompress(byte[], byte[], int) decompress(src, dest, dest.length)}. |
||||
* |
||||
* @param src the compressed data |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @return the number of bytes read to restore the original input |
||||
*/ |
||||
public final int decompress(byte[] src, byte[] dest) { |
||||
return decompress(src, dest, dest.length); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method which returns <code>src[srcOff:?]</code> |
||||
* decompressed. |
||||
* <p><b><span style="color:red">Warning</span></b>: this method has an |
||||
* important overhead due to the fact that it needs to allocate a buffer to |
||||
* decompress into.</p> |
||||
* <p>Here is how this method is implemented:</p> |
||||
* <pre> |
||||
* final byte[] decompressed = new byte[destLen]; |
||||
* decompress(src, srcOff, decompressed, 0, destLen); |
||||
* return decompressed; |
||||
* </pre> |
||||
* |
||||
* @param src the compressed data |
||||
* @param srcOff the start offset in src |
||||
* @param destLen the <b>exact</b> size of the original input |
||||
* @return the decompressed data |
||||
*/ |
||||
public final byte[] decompress(byte[] src, int srcOff, int destLen) { |
||||
final byte[] decompressed = new byte[destLen]; |
||||
decompress(src, srcOff, decompressed, 0, destLen); |
||||
return decompressed; |
||||
} |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #decompress(byte[], int, int) decompress(src, 0, destLen)}. |
||||
* |
||||
* @param src the compressed data |
||||
* @param destLen the <b>exact</b> size of the original input |
||||
* @return the decompressed data |
||||
*/ |
||||
public final byte[] decompress(byte[] src, int destLen) { |
||||
return decompress(src, 0, destLen); |
||||
} |
||||
|
||||
/** |
||||
* Decompresses <code>src</code> into <code>dest</code>. <code>dest</code>'s |
||||
* {@link ByteBuffer#remaining()} must be exactly the size of the decompressed |
||||
* data. This method moves the positions of the buffers. |
||||
* |
||||
* @param src the compressed data |
||||
* @param dest the destination buffer to store the decompressed data |
||||
*/ |
||||
public final void decompress(ByteBuffer src, ByteBuffer dest) { |
||||
final int read = decompress(src, src.position(), dest, dest.position(), dest.remaining()); |
||||
dest.position(dest.limit()); |
||||
src.position(src.position() + read); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,351 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import com.fr.third.net.jpountz.xxhash.XXHash32; |
||||
import com.fr.third.net.jpountz.xxhash.XXHashFactory; |
||||
|
||||
import java.io.FilterInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.ByteOrder; |
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* Implementation of the v1.5.1 LZ4 Frame format. This class is NOT thread safe. |
||||
* <p> |
||||
* Not Supported:<ul> |
||||
* <li>Dependent blocks</li> |
||||
* <li>Legacy streams</li> |
||||
* </ul> |
||||
* <p> |
||||
* Originally based on kafka's KafkaLZ4BlockInputStream. |
||||
* |
||||
* @see <a href="https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md">LZ4 Framing Format Spec 1.5.1</a> |
||||
*/ |
||||
public class LZ4FrameInputStream extends FilterInputStream { |
||||
|
||||
static final String PREMATURE_EOS = "Stream ended prematurely"; |
||||
static final String NOT_SUPPORTED = "Stream unsupported"; |
||||
static final String BLOCK_HASH_MISMATCH = "Block checksum mismatch"; |
||||
static final String DESCRIPTOR_HASH_MISMATCH = "Stream frame descriptor corrupted"; |
||||
static final int MAGIC_SKIPPABLE_BASE = 0x184D2A50; |
||||
|
||||
private final LZ4SafeDecompressor decompressor; |
||||
private final XXHash32 checksum; |
||||
private final byte[] headerArray = new byte[LZ4FrameOutputStream.LZ4_MAX_HEADER_LENGTH]; |
||||
private final ByteBuffer headerBuffer = ByteBuffer.wrap(headerArray).order(ByteOrder.LITTLE_ENDIAN); |
||||
private byte[] compressedBuffer; |
||||
private ByteBuffer buffer = null; |
||||
private byte[] rawBuffer = null; |
||||
private int maxBlockSize = -1; |
||||
private long expectedContentSize = -1L; |
||||
private long totalContentSize = 0L; |
||||
|
||||
private LZ4FrameOutputStream.FrameInfo frameInfo = null; |
||||
|
||||
/** |
||||
* Creates a new {@link InputStream} that will decompress data using fastest instances of {@link LZ4SafeDecompressor} and {@link XXHash32}. |
||||
* |
||||
* @param in the stream to decompress |
||||
* @throws IOException if an I/O error occurs |
||||
* |
||||
* @see #LZ4FrameInputStream(InputStream, LZ4SafeDecompressor, XXHash32) |
||||
* @see LZ4Factory#fastestInstance() |
||||
* @see XXHashFactory#fastestInstance() |
||||
*/ |
||||
public LZ4FrameInputStream(InputStream in) throws IOException { |
||||
this(in, LZ4Factory.fastestInstance().safeDecompressor(), XXHashFactory.fastestInstance().hash32()); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@link InputStream} that will decompress data using the LZ4 algorithm. |
||||
* |
||||
* @param in the stream to decompress |
||||
* @param decompressor the decompressor to use |
||||
* @param checksum the hash function to use |
||||
* @throws IOException if an I/O error occurs |
||||
*/ |
||||
public LZ4FrameInputStream(InputStream in, LZ4SafeDecompressor decompressor, XXHash32 checksum) throws IOException { |
||||
super(in); |
||||
this.decompressor = decompressor; |
||||
this.checksum = checksum; |
||||
nextFrameInfo(); |
||||
} |
||||
|
||||
|
||||
|
||||
/** |
||||
* Try and load in the next valid frame info. This will skip over skippable frames. |
||||
* @return True if a frame was loaded. False if there are no more frames in the stream. |
||||
* @throws IOException On input stream read exception |
||||
*/ |
||||
private boolean nextFrameInfo() throws IOException { |
||||
while (true) { |
||||
int size = 0; |
||||
do { |
||||
final int mySize = in.read(readNumberBuff.array(), size, LZ4FrameOutputStream.INTEGER_BYTES - size); |
||||
if (mySize < 0) { |
||||
return false; |
||||
} |
||||
size += mySize; |
||||
} while (size < LZ4FrameOutputStream.INTEGER_BYTES); |
||||
final int magic = readNumberBuff.getInt(0); |
||||
if (magic == LZ4FrameOutputStream.MAGIC) { |
||||
readHeader(); |
||||
return true; |
||||
} else if ((magic >>> 4) == (MAGIC_SKIPPABLE_BASE >>> 4)) { |
||||
skippableFrame(); |
||||
} else { |
||||
throw new IOException(NOT_SUPPORTED); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void skippableFrame() throws IOException { |
||||
int skipSize = readInt(in); |
||||
final byte[] skipBuffer = new byte[1 << 10]; |
||||
while (skipSize > 0) { |
||||
final int mySize = in.read(skipBuffer, 0, Math.min(skipSize, skipBuffer.length)); |
||||
if (mySize < 0) { |
||||
throw new IOException(PREMATURE_EOS); |
||||
} |
||||
skipSize -= mySize; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Reads the frame descriptor from the underlying {@link InputStream}. |
||||
* |
||||
* @throws IOException |
||||
*/ |
||||
private void readHeader() throws IOException { |
||||
headerBuffer.rewind(); |
||||
|
||||
final int flgRead = in.read(); |
||||
if (flgRead < 0) { |
||||
throw new IOException(PREMATURE_EOS); |
||||
} |
||||
final int bdRead = in.read(); |
||||
if (bdRead < 0) { |
||||
throw new IOException(PREMATURE_EOS); |
||||
} |
||||
|
||||
final byte flgByte = (byte)(flgRead & 0xFF); |
||||
final LZ4FrameOutputStream.FLG flg = LZ4FrameOutputStream.FLG.fromByte(flgByte); |
||||
headerBuffer.put(flgByte); |
||||
final byte bdByte = (byte)(bdRead & 0xFF); |
||||
final LZ4FrameOutputStream.BD bd = LZ4FrameOutputStream.BD.fromByte(bdByte); |
||||
headerBuffer.put(bdByte); |
||||
|
||||
this.frameInfo = new LZ4FrameOutputStream.FrameInfo(flg, bd); |
||||
|
||||
if (flg.isEnabled(LZ4FrameOutputStream.FLG.Bits.CONTENT_SIZE)) { |
||||
expectedContentSize = readLong(in); |
||||
headerBuffer.putLong(expectedContentSize); |
||||
} |
||||
totalContentSize = 0L; |
||||
|
||||
// check stream descriptor hash
|
||||
final byte hash = (byte) ((checksum.hash(headerArray, 0, headerBuffer.position(), 0) >> 8) & 0xFF); |
||||
final int expectedHash = in.read(); |
||||
if (expectedHash < 0) { |
||||
throw new IOException(PREMATURE_EOS); |
||||
} |
||||
|
||||
if (hash != (byte)(expectedHash & 0xFF)) { |
||||
throw new IOException(DESCRIPTOR_HASH_MISMATCH); |
||||
} |
||||
|
||||
maxBlockSize = frameInfo.getBD().getBlockMaximumSize(); |
||||
compressedBuffer = new byte[maxBlockSize]; // Reused during different compressions
|
||||
rawBuffer = new byte[maxBlockSize]; |
||||
buffer = ByteBuffer.wrap(rawBuffer); |
||||
buffer.limit(0); |
||||
} |
||||
|
||||
private final ByteBuffer readNumberBuff = ByteBuffer.allocate(LZ4FrameOutputStream.LONG_BYTES).order(ByteOrder.LITTLE_ENDIAN); |
||||
|
||||
private long readLong(InputStream stream) throws IOException { |
||||
int offset = 0; |
||||
do { |
||||
final int mySize = stream.read(readNumberBuff.array(), offset, LZ4FrameOutputStream.LONG_BYTES - offset); |
||||
if (mySize < 0) { |
||||
throw new IOException(PREMATURE_EOS); |
||||
} |
||||
offset += mySize; |
||||
} while (offset < LZ4FrameOutputStream.LONG_BYTES); |
||||
return readNumberBuff.getLong(0); |
||||
} |
||||
|
||||
private int readInt(InputStream stream) throws IOException { |
||||
int offset = 0; |
||||
do { |
||||
final int mySize = stream.read(readNumberBuff.array(), offset, LZ4FrameOutputStream.INTEGER_BYTES - offset); |
||||
if (mySize < 0) { |
||||
throw new IOException(PREMATURE_EOS); |
||||
} |
||||
offset += mySize; |
||||
} while (offset < LZ4FrameOutputStream.INTEGER_BYTES); |
||||
return readNumberBuff.getInt(0); |
||||
} |
||||
|
||||
/** |
||||
* Decompress (if necessary) buffered data, optionally computes and validates a XXHash32 checksum, and writes the |
||||
* result to a buffer. |
||||
* |
||||
* @throws IOException |
||||
*/ |
||||
private void readBlock() throws IOException { |
||||
int blockSize = readInt(in); |
||||
final boolean compressed = (blockSize & LZ4FrameOutputStream.LZ4_FRAME_INCOMPRESSIBLE_MASK) == 0; |
||||
blockSize &= ~LZ4FrameOutputStream.LZ4_FRAME_INCOMPRESSIBLE_MASK; |
||||
|
||||
// Check for EndMark
|
||||
if (blockSize == 0) { |
||||
if (frameInfo.isEnabled(LZ4FrameOutputStream.FLG.Bits.CONTENT_CHECKSUM)) { |
||||
final int contentChecksum = readInt(in); |
||||
if (contentChecksum != frameInfo.currentStreamHash()) { |
||||
throw new IOException("Content checksum mismatch"); |
||||
} |
||||
} |
||||
if (frameInfo.isEnabled(LZ4FrameOutputStream.FLG.Bits.CONTENT_SIZE) && expectedContentSize != totalContentSize) { |
||||
throw new IOException("Size check mismatch"); |
||||
} |
||||
frameInfo.finish(); |
||||
return; |
||||
} |
||||
|
||||
final byte[] tmpBuffer; // Use a temporary buffer, potentially one used for compression
|
||||
if (compressed) { |
||||
tmpBuffer = compressedBuffer; |
||||
} else { |
||||
tmpBuffer = rawBuffer; |
||||
} |
||||
if (blockSize > maxBlockSize) { |
||||
throw new IOException(String.format(Locale.ROOT, "Block size %s exceeded max: %s", blockSize, maxBlockSize)); |
||||
} |
||||
|
||||
int offset = 0; |
||||
while (offset < blockSize) { |
||||
final int lastRead = in.read(tmpBuffer, offset, blockSize - offset); |
||||
if (lastRead < 0) { |
||||
throw new IOException(PREMATURE_EOS); |
||||
} |
||||
offset += lastRead; |
||||
} |
||||
|
||||
// verify block checksum
|
||||
if (frameInfo.isEnabled(LZ4FrameOutputStream.FLG.Bits.BLOCK_CHECKSUM)) { |
||||
final int hashCheck = readInt(in); |
||||
if (hashCheck != checksum.hash(tmpBuffer, 0, blockSize, 0)) { |
||||
throw new IOException(BLOCK_HASH_MISMATCH); |
||||
} |
||||
} |
||||
|
||||
final int currentBufferSize; |
||||
if (compressed) { |
||||
try { |
||||
currentBufferSize = decompressor.decompress(tmpBuffer, 0, blockSize, rawBuffer, 0, rawBuffer.length); |
||||
} catch (LZ4Exception e) { |
||||
throw new IOException(e); |
||||
} |
||||
} else { |
||||
currentBufferSize = blockSize; |
||||
} |
||||
if (frameInfo.isEnabled(LZ4FrameOutputStream.FLG.Bits.CONTENT_CHECKSUM)) { |
||||
frameInfo.updateStreamHash(rawBuffer, 0, currentBufferSize); |
||||
} |
||||
totalContentSize += currentBufferSize; |
||||
buffer.limit(currentBufferSize); |
||||
buffer.rewind(); |
||||
} |
||||
|
||||
@Override |
||||
public int read() throws IOException { |
||||
while (buffer.remaining() == 0) { |
||||
if (frameInfo.isFinished()) { |
||||
if (!nextFrameInfo()) { |
||||
return -1; |
||||
} |
||||
} |
||||
readBlock(); |
||||
} |
||||
return (int)buffer.get() & 0xFF; |
||||
} |
||||
|
||||
@Override |
||||
public int read(byte[] b, int off, int len) throws IOException { |
||||
if ((off < 0) || (len < 0) || (off + len > b.length)) { |
||||
throw new IndexOutOfBoundsException(); |
||||
} |
||||
while (buffer.remaining() == 0) { |
||||
if (frameInfo.isFinished()) { |
||||
if (!nextFrameInfo()) { |
||||
return -1; |
||||
} |
||||
} |
||||
readBlock(); |
||||
} |
||||
len = Math.min(len, buffer.remaining()); |
||||
buffer.get(b, off, len); |
||||
return len; |
||||
} |
||||
|
||||
@Override |
||||
public long skip(long n) throws IOException { |
||||
if (n <= 0) { |
||||
return 0; |
||||
} |
||||
while (buffer.remaining() == 0) { |
||||
if (frameInfo.isFinished()) { |
||||
if (!nextFrameInfo()) { |
||||
return 0; |
||||
} |
||||
} |
||||
readBlock(); |
||||
} |
||||
n = Math.min(n, buffer.remaining()); |
||||
buffer.position(buffer.position() + (int)n); |
||||
return n; |
||||
} |
||||
|
||||
@Override |
||||
public int available() throws IOException { |
||||
return buffer.remaining(); |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
super.close(); |
||||
} |
||||
|
||||
@Override |
||||
public synchronized void mark(int readlimit) { |
||||
throw new UnsupportedOperationException("mark not supported"); |
||||
} |
||||
|
||||
@Override |
||||
public synchronized void reset() throws IOException { |
||||
throw new UnsupportedOperationException("reset not supported"); |
||||
} |
||||
|
||||
@Override |
||||
public boolean markSupported() { |
||||
return false; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,434 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import com.fr.third.net.jpountz.xxhash.StreamingXXHash32; |
||||
import com.fr.third.net.jpountz.xxhash.XXHash32; |
||||
import com.fr.third.net.jpountz.xxhash.XXHashFactory; |
||||
|
||||
import java.io.FilterOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.OutputStream; |
||||
import java.nio.ByteBuffer; |
||||
import java.nio.ByteOrder; |
||||
import java.util.Arrays; |
||||
import java.util.BitSet; |
||||
import java.util.Locale; |
||||
|
||||
/** |
||||
* Implementation of the v1.5.1 LZ4 Frame format. This class is NOT thread safe. |
||||
* <p> |
||||
* Not Supported:<ul> |
||||
* <li>Dependent blocks</li> |
||||
* <li>Legacy streams</li> |
||||
* <li>Multiple frames (one LZ4FrameOutputStream is one frame)</li> |
||||
* </ul> |
||||
* <p> |
||||
* Originally based on kafka's KafkaLZ4BlockOutputStream. |
||||
* |
||||
* @see <a href="https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md">LZ4 Framing Format Spec 1.5.1</a> |
||||
*/ |
||||
public class LZ4FrameOutputStream extends FilterOutputStream { |
||||
|
||||
static final int INTEGER_BYTES = Integer.SIZE >>> 3; // or Integer.BYTES in Java 1.8
|
||||
static final int LONG_BYTES = Long.SIZE >>> 3; // or Long.BYTES in Java 1.8
|
||||
|
||||
static final int MAGIC = 0x184D2204; |
||||
static final int LZ4_MAX_HEADER_LENGTH = |
||||
4 + // magic
|
||||
1 + // FLG
|
||||
1 + // BD
|
||||
8 + // Content Size
|
||||
1; // HC
|
||||
static final int LZ4_FRAME_INCOMPRESSIBLE_MASK = 0x80000000; |
||||
static final FLG.Bits[] DEFAULT_FEATURES = new FLG.Bits[]{FLG.Bits.BLOCK_INDEPENDENCE}; |
||||
|
||||
static final String CLOSED_STREAM = "The stream is already closed"; |
||||
|
||||
public static enum BLOCKSIZE { |
||||
SIZE_64KB(4), SIZE_256KB(5), SIZE_1MB(6), SIZE_4MB(7); |
||||
private final int indicator; |
||||
BLOCKSIZE(int indicator) { |
||||
this.indicator = indicator; |
||||
} |
||||
public int getIndicator() { |
||||
return this.indicator; |
||||
} |
||||
public static BLOCKSIZE valueOf(int indicator) { |
||||
switch(indicator) { |
||||
case 7: return SIZE_4MB; |
||||
case 6: return SIZE_1MB; |
||||
case 5: return SIZE_256KB; |
||||
case 4: return SIZE_64KB; |
||||
default: throw new IllegalArgumentException(String.format(Locale.ROOT, "Block size must be 4-7. Cannot use value of [%d]", indicator)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private final LZ4Compressor compressor; |
||||
private final XXHash32 checksum; |
||||
private final ByteBuffer buffer; // Buffer for uncompressed input data
|
||||
private final byte[] compressedBuffer; // Only allocated once so it can be reused
|
||||
private final int maxBlockSize; |
||||
private final long knownSize; |
||||
private final ByteBuffer intLEBuffer = ByteBuffer.allocate(INTEGER_BYTES).order(ByteOrder.LITTLE_ENDIAN); |
||||
|
||||
private FrameInfo frameInfo = null; |
||||
|
||||
|
||||
/** |
||||
* Creates a new {@link OutputStream} that will compress data of unknown size using the LZ4 algorithm. |
||||
* |
||||
* @param out the output stream to compress |
||||
* @param blockSize the BLOCKSIZE to use |
||||
* @param bits a set of features to use |
||||
* @throws IOException if an I/O error occurs |
||||
* |
||||
* @see #LZ4FrameOutputStream(OutputStream, BLOCKSIZE, long, FLG.Bits...) |
||||
*/ |
||||
public LZ4FrameOutputStream(OutputStream out, BLOCKSIZE blockSize, FLG.Bits... bits) throws IOException { |
||||
this(out, blockSize, -1L, bits); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@link OutputStream} that will compress data using using fastest instances of {@link LZ4Compressor} and {@link XXHash32}. |
||||
* |
||||
* @param out the output stream to compress |
||||
* @param blockSize the BLOCKSIZE to use |
||||
* @param knownSize the size of the uncompressed data. A value less than zero means unknown. |
||||
* @param bits a set of features to use |
||||
* @throws IOException if an I/O error occurs |
||||
*/ |
||||
public LZ4FrameOutputStream(OutputStream out, BLOCKSIZE blockSize, long knownSize, FLG.Bits... bits) throws IOException { |
||||
super(out); |
||||
compressor = LZ4Factory.fastestInstance().fastCompressor(); |
||||
checksum = XXHashFactory.fastestInstance().hash32(); |
||||
frameInfo = new FrameInfo(new FLG(FLG.DEFAULT_VERSION, bits), new BD(blockSize)); |
||||
maxBlockSize = frameInfo.getBD().getBlockMaximumSize(); |
||||
buffer = ByteBuffer.allocate(maxBlockSize).order(ByteOrder.LITTLE_ENDIAN); |
||||
compressedBuffer = new byte[compressor.maxCompressedLength(maxBlockSize)]; |
||||
if (frameInfo.getFLG().isEnabled(FLG.Bits.CONTENT_SIZE) && knownSize < 0) { |
||||
throw new IllegalArgumentException("Known size must be greater than zero in order to use the known size feature"); |
||||
} |
||||
this.knownSize = knownSize; |
||||
writeHeader(); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@link OutputStream} that will compress data using the LZ4 algorithm. The block independence flag is set, and none of the other flags are set. |
||||
* |
||||
* @param out The stream to compress |
||||
* @param blockSize the BLOCKSIZE to use |
||||
* @throws IOException if an I/O error occurs |
||||
* |
||||
* @see #LZ4FrameOutputStream(OutputStream, BLOCKSIZE, FLG.Bits...) |
||||
*/ |
||||
public LZ4FrameOutputStream(OutputStream out, BLOCKSIZE blockSize) throws IOException { |
||||
this(out, blockSize, DEFAULT_FEATURES); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new {@link OutputStream} that will compress data using the LZ4 algorithm with 4-MB blocks. |
||||
* |
||||
* @param out the output stream to compress |
||||
* @throws IOException if an I/O error occurs |
||||
* |
||||
* @see #LZ4FrameOutputStream(OutputStream, BLOCKSIZE) |
||||
*/ |
||||
public LZ4FrameOutputStream(OutputStream out) throws IOException { |
||||
this(out, BLOCKSIZE.SIZE_4MB); |
||||
} |
||||
|
||||
/** |
||||
* Writes the magic number and frame descriptor to the underlying {@link OutputStream}. |
||||
* |
||||
* @throws IOException |
||||
*/ |
||||
private void writeHeader() throws IOException { |
||||
final ByteBuffer headerBuffer = ByteBuffer.allocate(LZ4_MAX_HEADER_LENGTH).order(ByteOrder.LITTLE_ENDIAN); |
||||
headerBuffer.putInt(MAGIC); |
||||
headerBuffer.put(frameInfo.getFLG().toByte()); |
||||
headerBuffer.put(frameInfo.getBD().toByte()); |
||||
if (frameInfo.isEnabled(FLG.Bits.CONTENT_SIZE)) { |
||||
headerBuffer.putLong(knownSize); |
||||
} |
||||
// compute checksum on all descriptor fields
|
||||
final int hash = (checksum.hash(headerBuffer.array(), INTEGER_BYTES, headerBuffer.position() - INTEGER_BYTES, 0) >> 8) & 0xFF; |
||||
headerBuffer.put((byte) hash); |
||||
// write out frame descriptor
|
||||
out.write(headerBuffer.array(), 0, headerBuffer.position()); |
||||
} |
||||
|
||||
/** |
||||
* Compresses buffered data, optionally computes an XXHash32 checksum, and writes the result to the underlying |
||||
* {@link OutputStream}. |
||||
* |
||||
* @throws IOException |
||||
*/ |
||||
private void writeBlock() throws IOException { |
||||
if (buffer.position() == 0) { |
||||
return; |
||||
} |
||||
// Make sure there's no stale data
|
||||
Arrays.fill(compressedBuffer, (byte) 0); |
||||
|
||||
int compressedLength = compressor.compress(buffer.array(), 0, buffer.position(), compressedBuffer, 0); |
||||
final byte[] bufferToWrite; |
||||
final int compressMethod; |
||||
|
||||
// Store block uncompressed if compressed length is greater (incompressible)
|
||||
if (compressedLength >= buffer.position()) { |
||||
compressedLength = buffer.position(); |
||||
bufferToWrite = Arrays.copyOf(buffer.array(), compressedLength); |
||||
compressMethod = LZ4_FRAME_INCOMPRESSIBLE_MASK; |
||||
} else { |
||||
bufferToWrite = compressedBuffer; |
||||
compressMethod = 0; |
||||
} |
||||
|
||||
// Write content
|
||||
intLEBuffer.putInt(0, compressedLength | compressMethod); |
||||
out.write(intLEBuffer.array()); |
||||
out.write(bufferToWrite, 0, compressedLength); |
||||
|
||||
// Calculate and write block checksum
|
||||
if (frameInfo.isEnabled(FLG.Bits.BLOCK_CHECKSUM)) { |
||||
intLEBuffer.putInt(0, checksum.hash(bufferToWrite, 0, compressedLength, 0)); |
||||
out.write(intLEBuffer.array()); |
||||
} |
||||
buffer.rewind(); |
||||
} |
||||
|
||||
/** |
||||
* Similar to the {@link #writeBlock()} method. Writes a 0-length block (without block checksum) to signal the end |
||||
* of the block stream. |
||||
* |
||||
* @throws IOException |
||||
*/ |
||||
private void writeEndMark() throws IOException { |
||||
intLEBuffer.putInt(0, 0); |
||||
out.write(intLEBuffer.array()); |
||||
if (frameInfo.isEnabled(FLG.Bits.CONTENT_CHECKSUM)) { |
||||
intLEBuffer.putInt(0, frameInfo.currentStreamHash()); |
||||
out.write(intLEBuffer.array()); |
||||
} |
||||
frameInfo.finish(); |
||||
} |
||||
|
||||
@Override |
||||
public void write(int b) throws IOException { |
||||
ensureNotFinished(); |
||||
if (buffer.position() == maxBlockSize) { |
||||
writeBlock(); |
||||
} |
||||
buffer.put((byte) b); |
||||
|
||||
if (frameInfo.isEnabled(FLG.Bits.CONTENT_CHECKSUM)) { |
||||
frameInfo.updateStreamHash(new byte[]{(byte) b}, 0, 1); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void write(byte[] b, int off, int len) throws IOException { |
||||
if ((off < 0) || (len < 0) || (off + len > b.length)) { |
||||
throw new IndexOutOfBoundsException(); |
||||
} |
||||
ensureNotFinished(); |
||||
|
||||
// while b will fill the buffer
|
||||
while (len > buffer.remaining()) { |
||||
int sizeWritten = buffer.remaining(); |
||||
// fill remaining space in buffer
|
||||
buffer.put(b, off, sizeWritten); |
||||
if (frameInfo.isEnabled(FLG.Bits.CONTENT_CHECKSUM)) { |
||||
frameInfo.updateStreamHash(b, off, sizeWritten); |
||||
} |
||||
writeBlock(); |
||||
// compute new offset and length
|
||||
off += sizeWritten; |
||||
len -= sizeWritten; |
||||
} |
||||
buffer.put(b, off, len); |
||||
|
||||
if (frameInfo.isEnabled(FLG.Bits.CONTENT_CHECKSUM)) { |
||||
frameInfo.updateStreamHash(b, off, len); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void flush() throws IOException { |
||||
if (!frameInfo.isFinished()) { |
||||
writeBlock(); |
||||
} |
||||
super.flush(); |
||||
} |
||||
|
||||
/** |
||||
* A simple state check to ensure the stream is still open. |
||||
*/ |
||||
private void ensureNotFinished() { |
||||
if (frameInfo.isFinished()) { |
||||
throw new IllegalStateException(CLOSED_STREAM); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
if (!frameInfo.isFinished()) { |
||||
flush(); |
||||
writeEndMark(); |
||||
} |
||||
super.close(); |
||||
} |
||||
|
||||
public static class FLG { |
||||
private static final int DEFAULT_VERSION = 1; |
||||
|
||||
private final BitSet bitSet; |
||||
private final int version; |
||||
|
||||
public enum Bits { |
||||
RESERVED_0(0), |
||||
RESERVED_1(1), |
||||
CONTENT_CHECKSUM(2), |
||||
CONTENT_SIZE(3), |
||||
BLOCK_CHECKSUM(4), |
||||
BLOCK_INDEPENDENCE(5); |
||||
|
||||
private final int position; |
||||
Bits(int position) { |
||||
this.position = position; |
||||
} |
||||
} |
||||
|
||||
public FLG(int version, Bits... bits) { |
||||
this.bitSet = new BitSet(8); |
||||
this.version = version; |
||||
if (bits != null) { |
||||
for (Bits bit : bits) { |
||||
bitSet.set(bit.position); |
||||
} |
||||
} |
||||
validate(); |
||||
} |
||||
|
||||
private FLG(int version, byte b) { |
||||
this.bitSet = BitSet.valueOf(new byte[]{b}); |
||||
this.version = version; |
||||
validate(); |
||||
} |
||||
|
||||
public static FLG fromByte(byte flg) { |
||||
final byte versionMask = (byte)(flg & (3 << 6)); |
||||
return new FLG(versionMask >>> 6, (byte) (flg ^ versionMask)); |
||||
} |
||||
|
||||
public byte toByte() { |
||||
return (byte)(bitSet.toByteArray()[0] | ((version & 3) << 6)); |
||||
} |
||||
|
||||
private void validate() { |
||||
if (bitSet.get(Bits.RESERVED_0.position)) { |
||||
throw new RuntimeException("Reserved0 field must be 0"); |
||||
} |
||||
if (bitSet.get(Bits.RESERVED_1.position)) { |
||||
throw new RuntimeException("Reserved1 field must be 0"); |
||||
} |
||||
if (!bitSet.get(Bits.BLOCK_INDEPENDENCE.position)) { |
||||
throw new RuntimeException("Dependent block stream is unsupported (BLOCK_INDEPENDENCE must be set)"); |
||||
} |
||||
if (version != DEFAULT_VERSION) { |
||||
throw new RuntimeException(String.format(Locale.ROOT, "Version %d is unsupported", version)); |
||||
} |
||||
} |
||||
|
||||
public boolean isEnabled(Bits bit) { |
||||
return bitSet.get(bit.position); |
||||
} |
||||
|
||||
public int getVersion() { |
||||
return version; |
||||
} |
||||
} |
||||
|
||||
public static class BD { |
||||
private static final int RESERVED_MASK = 0x8F; |
||||
|
||||
private final BLOCKSIZE blockSizeValue; |
||||
|
||||
private BD(BLOCKSIZE blockSizeValue) { |
||||
this.blockSizeValue = blockSizeValue; |
||||
} |
||||
|
||||
public static BD fromByte(byte bd) { |
||||
int blockMaximumSize = (bd >>> 4) & 7; |
||||
if ((bd & RESERVED_MASK) > 0) { |
||||
throw new RuntimeException("Reserved fields must be 0"); |
||||
} |
||||
|
||||
return new BD(BLOCKSIZE.valueOf(blockMaximumSize)); |
||||
} |
||||
|
||||
// 2^(2n+8)
|
||||
public int getBlockMaximumSize() { |
||||
return 1 << ((2 * blockSizeValue.getIndicator()) + 8); |
||||
} |
||||
|
||||
public byte toByte() { |
||||
return (byte) ((blockSizeValue.getIndicator() & 7) << 4); |
||||
} |
||||
} |
||||
|
||||
static class FrameInfo { |
||||
private final FLG flg; |
||||
private final BD bd; |
||||
private final StreamingXXHash32 streamHash; |
||||
private boolean finished = false; |
||||
|
||||
public FrameInfo(FLG flg, BD bd) { |
||||
this.flg = flg; |
||||
this.bd = bd; |
||||
this.streamHash = flg.isEnabled(FLG.Bits.CONTENT_CHECKSUM) ? XXHashFactory.fastestInstance().newStreamingHash32(0) : null; |
||||
} |
||||
|
||||
public boolean isEnabled(FLG.Bits bit) { |
||||
return flg.isEnabled(bit); |
||||
} |
||||
|
||||
public FLG getFLG() { |
||||
return this.flg; |
||||
} |
||||
|
||||
public BD getBD() { |
||||
return this.bd; |
||||
} |
||||
|
||||
public void updateStreamHash(byte[] buff, int off, int len) { |
||||
this.streamHash.update(buff, off, len); |
||||
} |
||||
|
||||
public int currentStreamHash() { |
||||
return this.streamHash.getValue(); |
||||
} |
||||
|
||||
public void finish() { |
||||
this.finished = true; |
||||
} |
||||
|
||||
public boolean isFinished() { |
||||
return this.finished; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,86 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
/** |
||||
* High compression {@link LZ4Compressor}s implemented with JNI bindings to the |
||||
* original C implementation of LZ4. |
||||
*/ |
||||
final class LZ4HCJNICompressor extends LZ4Compressor { |
||||
|
||||
public static final LZ4HCJNICompressor INSTANCE = new LZ4HCJNICompressor(); |
||||
private static LZ4Compressor SAFE_INSTANCE; |
||||
|
||||
private final int compressionLevel; |
||||
|
||||
LZ4HCJNICompressor() { this(LZ4Constants.DEFAULT_COMPRESSION_LEVEL); } |
||||
LZ4HCJNICompressor(int compressionLevel) { |
||||
this.compressionLevel = compressionLevel; |
||||
} |
||||
|
||||
@Override |
||||
public int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) { |
||||
SafeUtils.checkRange(src, srcOff, srcLen); |
||||
SafeUtils.checkRange(dest, destOff, maxDestLen); |
||||
final int result = LZ4JNI.LZ4_compressHC(src, null, srcOff, srcLen, dest, null, destOff, maxDestLen, compressionLevel); |
||||
if (result <= 0) { |
||||
throw new LZ4Exception(); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) { |
||||
ByteBufferUtils.checkNotReadOnly(dest); |
||||
ByteBufferUtils.checkRange(src, srcOff, srcLen); |
||||
ByteBufferUtils.checkRange(dest, destOff, maxDestLen); |
||||
|
||||
if ((src.hasArray() || src.isDirect()) && (dest.hasArray() || dest.isDirect())) { |
||||
byte[] srcArr = null, destArr = null; |
||||
ByteBuffer srcBuf = null, destBuf = null; |
||||
if (src.hasArray()) { |
||||
srcArr = src.array(); |
||||
srcOff += src.arrayOffset(); |
||||
} else { |
||||
assert src.isDirect(); |
||||
srcBuf = src; |
||||
} |
||||
if (dest.hasArray()) { |
||||
destArr = dest.array(); |
||||
destOff += dest.arrayOffset(); |
||||
} else { |
||||
assert dest.isDirect(); |
||||
destBuf = dest; |
||||
} |
||||
|
||||
final int result = LZ4JNI.LZ4_compressHC(srcArr, srcBuf, srcOff, srcLen, destArr, destBuf, destOff, maxDestLen, compressionLevel); |
||||
if (result <= 0) { |
||||
throw new LZ4Exception(); |
||||
} |
||||
return result; |
||||
} else { |
||||
LZ4Compressor safeInstance = SAFE_INSTANCE; |
||||
if (safeInstance == null) { |
||||
safeInstance = SAFE_INSTANCE = LZ4Factory.safeInstance().highCompressor(compressionLevel); |
||||
} |
||||
return safeInstance.compress(src, srcOff, srcLen, dest, destOff, maxDestLen); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,550 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.*; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Utils.*; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.util.Arrays; |
||||
|
||||
import com.fr.third.net.jpountz.lz4.LZ4Utils.Match; |
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
/** |
||||
* High compression compressor. |
||||
*/ |
||||
final class LZ4HCJavaSafeCompressor extends LZ4Compressor { |
||||
|
||||
public static final LZ4Compressor INSTANCE = new LZ4HCJavaSafeCompressor(); |
||||
|
||||
private final int maxAttempts; |
||||
final int compressionLevel; |
||||
|
||||
LZ4HCJavaSafeCompressor() { this(DEFAULT_COMPRESSION_LEVEL); } |
||||
LZ4HCJavaSafeCompressor(int compressionLevel) { |
||||
this.maxAttempts = 1<<(compressionLevel-1); |
||||
this.compressionLevel = compressionLevel; |
||||
} |
||||
|
||||
private class HashTable { |
||||
static final int MASK = MAX_DISTANCE - 1; |
||||
int nextToUpdate; |
||||
private final int base; |
||||
private final int[] hashTable; |
||||
private final short[] chainTable; |
||||
|
||||
HashTable(int base) { |
||||
this.base = base; |
||||
nextToUpdate = base; |
||||
hashTable = new int[HASH_TABLE_SIZE_HC]; |
||||
Arrays.fill(hashTable, -1); |
||||
chainTable = new short[MAX_DISTANCE]; |
||||
} |
||||
|
||||
private int hashPointer(byte[] bytes, int off) { |
||||
final int v = SafeUtils.readInt(bytes, off); |
||||
return hashPointer(v); |
||||
} |
||||
|
||||
private int hashPointer(ByteBuffer bytes, int off) { |
||||
final int v = ByteBufferUtils.readInt(bytes, off); |
||||
return hashPointer(v); |
||||
} |
||||
|
||||
private int hashPointer(int v) { |
||||
final int h = hashHC(v); |
||||
return hashTable[h]; |
||||
} |
||||
|
||||
private int next(int off) { |
||||
return off - (chainTable[off & MASK] & 0xFFFF); |
||||
} |
||||
|
||||
private void addHash(byte[] bytes, int off) { |
||||
final int v = SafeUtils.readInt(bytes, off); |
||||
addHash(v, off); |
||||
} |
||||
|
||||
private void addHash(ByteBuffer bytes, int off) { |
||||
final int v = ByteBufferUtils.readInt(bytes, off); |
||||
addHash(v, off); |
||||
} |
||||
|
||||
private void addHash(int v, int off) { |
||||
final int h = hashHC(v); |
||||
int delta = off - hashTable[h]; |
||||
assert delta > 0 : delta; |
||||
if (delta >= MAX_DISTANCE) { |
||||
delta = MAX_DISTANCE - 1; |
||||
} |
||||
chainTable[off & MASK] = (short) delta; |
||||
hashTable[h] = off; |
||||
} |
||||
|
||||
void insert(int off, byte[] bytes) { |
||||
for (; nextToUpdate < off; ++nextToUpdate) { |
||||
addHash(bytes, nextToUpdate); |
||||
} |
||||
} |
||||
|
||||
void insert(int off, ByteBuffer bytes) { |
||||
for (; nextToUpdate < off; ++nextToUpdate) { |
||||
addHash(bytes, nextToUpdate); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
boolean insertAndFindBestMatch(byte[] buf, int off, int matchLimit, Match match) { |
||||
match.start = off; |
||||
match.len = 0; |
||||
int delta = 0; |
||||
int repl = 0; |
||||
|
||||
insert(off, buf); |
||||
|
||||
int ref = hashPointer(buf, off); |
||||
|
||||
if (ref >= off - 4 && ref <= off && ref >= base) { // potential repetition
|
||||
if (LZ4SafeUtils.readIntEquals(buf, ref, off)) { // confirmed
|
||||
delta = off - ref; |
||||
repl = match.len = MIN_MATCH + LZ4SafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
match.ref = ref; |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
for (int i = 0; i < maxAttempts; ++i) { |
||||
if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { |
||||
break; |
||||
} |
||||
if (LZ4SafeUtils.readIntEquals(buf, ref, off)) { |
||||
final int matchLen = MIN_MATCH + LZ4SafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
if (matchLen > match.len) { |
||||
match.ref = ref; |
||||
match.len = matchLen; |
||||
} |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
if (repl != 0) { |
||||
int ptr = off; |
||||
final int end = off + repl - (MIN_MATCH - 1); |
||||
while (ptr < end - delta) { |
||||
chainTable[ptr & MASK] = (short) delta; // pre load
|
||||
++ptr; |
||||
} |
||||
do { |
||||
chainTable[ptr & MASK] = (short) delta; |
||||
hashTable[hashHC(SafeUtils.readInt(buf, ptr))] = ptr; |
||||
++ptr; |
||||
} while (ptr < end); |
||||
nextToUpdate = end; |
||||
} |
||||
|
||||
return match.len != 0; |
||||
} |
||||
|
||||
boolean insertAndFindWiderMatch(byte[] buf, int off, int startLimit, int matchLimit, int minLen, Match match) { |
||||
match.len = minLen; |
||||
|
||||
insert(off, buf); |
||||
|
||||
final int delta = off - startLimit; |
||||
int ref = hashPointer(buf, off); |
||||
for (int i = 0; i < maxAttempts; ++i) { |
||||
if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { |
||||
break; |
||||
} |
||||
if (LZ4SafeUtils.readIntEquals(buf, ref, off)) { |
||||
final int matchLenForward = MIN_MATCH +LZ4SafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
final int matchLenBackward = LZ4SafeUtils.commonBytesBackward(buf, ref, off, base, startLimit); |
||||
final int matchLen = matchLenBackward + matchLenForward; |
||||
if (matchLen > match.len) { |
||||
match.len = matchLen; |
||||
match.ref = ref - matchLenBackward; |
||||
match.start = off - matchLenBackward; |
||||
} |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
return match.len > minLen; |
||||
} |
||||
|
||||
|
||||
boolean insertAndFindBestMatch(ByteBuffer buf, int off, int matchLimit, Match match) { |
||||
match.start = off; |
||||
match.len = 0; |
||||
int delta = 0; |
||||
int repl = 0; |
||||
|
||||
insert(off, buf); |
||||
|
||||
int ref = hashPointer(buf, off); |
||||
|
||||
if (ref >= off - 4 && ref <= off && ref >= base) { // potential repetition
|
||||
if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) { // confirmed
|
||||
delta = off - ref; |
||||
repl = match.len = MIN_MATCH + LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
match.ref = ref; |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
for (int i = 0; i < maxAttempts; ++i) { |
||||
if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { |
||||
break; |
||||
} |
||||
if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) { |
||||
final int matchLen = MIN_MATCH + LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
if (matchLen > match.len) { |
||||
match.ref = ref; |
||||
match.len = matchLen; |
||||
} |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
if (repl != 0) { |
||||
int ptr = off; |
||||
final int end = off + repl - (MIN_MATCH - 1); |
||||
while (ptr < end - delta) { |
||||
chainTable[ptr & MASK] = (short) delta; // pre load
|
||||
++ptr; |
||||
} |
||||
do { |
||||
chainTable[ptr & MASK] = (short) delta; |
||||
hashTable[hashHC(ByteBufferUtils.readInt(buf, ptr))] = ptr; |
||||
++ptr; |
||||
} while (ptr < end); |
||||
nextToUpdate = end; |
||||
} |
||||
|
||||
return match.len != 0; |
||||
} |
||||
|
||||
boolean insertAndFindWiderMatch(ByteBuffer buf, int off, int startLimit, int matchLimit, int minLen, Match match) { |
||||
match.len = minLen; |
||||
|
||||
insert(off, buf); |
||||
|
||||
final int delta = off - startLimit; |
||||
int ref = hashPointer(buf, off); |
||||
for (int i = 0; i < maxAttempts; ++i) { |
||||
if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { |
||||
break; |
||||
} |
||||
if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) { |
||||
final int matchLenForward = MIN_MATCH +LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
final int matchLenBackward = LZ4ByteBufferUtils.commonBytesBackward(buf, ref, off, base, startLimit); |
||||
final int matchLen = matchLenBackward + matchLenForward; |
||||
if (matchLen > match.len) { |
||||
match.len = matchLen; |
||||
match.ref = ref - matchLenBackward; |
||||
match.start = off - matchLenBackward; |
||||
} |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
return match.len > minLen; |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) { |
||||
|
||||
SafeUtils.checkRange(src, srcOff, srcLen); |
||||
SafeUtils.checkRange(dest, destOff, maxDestLen); |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
final int destEnd = destOff + maxDestLen; |
||||
final int mfLimit = srcEnd - MF_LIMIT; |
||||
final int matchLimit = srcEnd - LAST_LITERALS; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
int anchor = sOff++; |
||||
|
||||
final HashTable ht = new HashTable(srcOff); |
||||
final Match match0 = new Match(); |
||||
final Match match1 = new Match(); |
||||
final Match match2 = new Match(); |
||||
final Match match3 = new Match(); |
||||
|
||||
main: |
||||
while (sOff < mfLimit) { |
||||
if (!ht.insertAndFindBestMatch(src, sOff, matchLimit, match1)) { |
||||
++sOff; |
||||
continue; |
||||
} |
||||
|
||||
// saved, in case we would skip too much
|
||||
copyTo(match1, match0); |
||||
|
||||
search2: |
||||
while (true) { |
||||
assert match1.start >= anchor; |
||||
if (match1.end() >= mfLimit |
||||
|| !ht.insertAndFindWiderMatch(src, match1.end() - 2, match1.start + 1, matchLimit, match1.len, match2)) { |
||||
// no better match
|
||||
dOff = LZ4SafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
continue main; |
||||
} |
||||
|
||||
if (match0.start < match1.start) { |
||||
if (match2.start < match1.start + match0.len) { // empirical
|
||||
copyTo(match0, match1); |
||||
} |
||||
} |
||||
assert match2.start > match1.start; |
||||
|
||||
if (match2.start - match1.start < 3) { // First Match too small : removed
|
||||
copyTo(match2, match1); |
||||
continue search2; |
||||
} |
||||
|
||||
search3: |
||||
while (true) { |
||||
if (match2.start - match1.start < OPTIMAL_ML) { |
||||
int newMatchLen = match1.len; |
||||
if (newMatchLen > OPTIMAL_ML) { |
||||
newMatchLen = OPTIMAL_ML; |
||||
} |
||||
if (match1.start + newMatchLen > match2.end() - MIN_MATCH) { |
||||
newMatchLen = match2.start - match1.start + match2.len - MIN_MATCH; |
||||
} |
||||
final int correction = newMatchLen - (match2.start - match1.start); |
||||
if (correction > 0) { |
||||
match2.fix(correction); |
||||
} |
||||
} |
||||
|
||||
if (match2.start + match2.len >= mfLimit |
||||
|| !ht.insertAndFindWiderMatch(src, match2.end() - 3, match2.start, matchLimit, match2.len, match3)) { |
||||
// no better match -> 2 sequences to encode
|
||||
if (match2.start < match1.end()) { |
||||
match1.len = match2.start - match1.start; |
||||
} |
||||
// encode seq 1
|
||||
dOff = LZ4SafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
// encode seq 2
|
||||
dOff = LZ4SafeUtils.encodeSequence(src, anchor, match2.start, match2.ref, match2.len, dest, dOff, destEnd); |
||||
anchor = sOff = match2.end(); |
||||
continue main; |
||||
} |
||||
|
||||
if (match3.start < match1.end() + 3) { // Not enough space for match 2 : remove it
|
||||
if (match3.start >= match1.end()) { // // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1
|
||||
if (match2.start < match1.end()) { |
||||
final int correction = match1.end() - match2.start; |
||||
match2.fix(correction); |
||||
if (match2.len < MIN_MATCH) { |
||||
copyTo(match3, match2); |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4SafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
|
||||
copyTo(match3, match1); |
||||
copyTo(match2, match0); |
||||
|
||||
continue search2; |
||||
} |
||||
|
||||
copyTo(match3, match2); |
||||
continue search3; |
||||
} |
||||
|
||||
// OK, now we have 3 ascending matches; let's write at least the first one
|
||||
if (match2.start < match1.end()) { |
||||
if (match2.start - match1.start < ML_MASK) { |
||||
if (match1.len > OPTIMAL_ML) { |
||||
match1.len = OPTIMAL_ML; |
||||
} |
||||
if (match1.end() > match2.end() - MIN_MATCH) { |
||||
match1.len = match2.end() - match1.start - MIN_MATCH; |
||||
} |
||||
final int correction = match1.end() - match2.start; |
||||
match2.fix(correction); |
||||
} else { |
||||
match1.len = match2.start - match1.start; |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4SafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
|
||||
copyTo(match2, match1); |
||||
copyTo(match3, match2); |
||||
|
||||
continue search3; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
dOff = LZ4SafeUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) { |
||||
|
||||
if (src.hasArray() && dest.hasArray()) { |
||||
return compress(src.array(), srcOff + src.arrayOffset(), srcLen, dest.array(), destOff + dest.arrayOffset(), maxDestLen); |
||||
} |
||||
src = ByteBufferUtils.inNativeByteOrder(src); |
||||
dest = ByteBufferUtils.inNativeByteOrder(dest); |
||||
|
||||
ByteBufferUtils.checkRange(src, srcOff, srcLen); |
||||
ByteBufferUtils.checkRange(dest, destOff, maxDestLen); |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
final int destEnd = destOff + maxDestLen; |
||||
final int mfLimit = srcEnd - MF_LIMIT; |
||||
final int matchLimit = srcEnd - LAST_LITERALS; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
int anchor = sOff++; |
||||
|
||||
final HashTable ht = new HashTable(srcOff); |
||||
final Match match0 = new Match(); |
||||
final Match match1 = new Match(); |
||||
final Match match2 = new Match(); |
||||
final Match match3 = new Match(); |
||||
|
||||
main: |
||||
while (sOff < mfLimit) { |
||||
if (!ht.insertAndFindBestMatch(src, sOff, matchLimit, match1)) { |
||||
++sOff; |
||||
continue; |
||||
} |
||||
|
||||
// saved, in case we would skip too much
|
||||
copyTo(match1, match0); |
||||
|
||||
search2: |
||||
while (true) { |
||||
assert match1.start >= anchor; |
||||
if (match1.end() >= mfLimit |
||||
|| !ht.insertAndFindWiderMatch(src, match1.end() - 2, match1.start + 1, matchLimit, match1.len, match2)) { |
||||
// no better match
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
continue main; |
||||
} |
||||
|
||||
if (match0.start < match1.start) { |
||||
if (match2.start < match1.start + match0.len) { // empirical
|
||||
copyTo(match0, match1); |
||||
} |
||||
} |
||||
assert match2.start > match1.start; |
||||
|
||||
if (match2.start - match1.start < 3) { // First Match too small : removed
|
||||
copyTo(match2, match1); |
||||
continue search2; |
||||
} |
||||
|
||||
search3: |
||||
while (true) { |
||||
if (match2.start - match1.start < OPTIMAL_ML) { |
||||
int newMatchLen = match1.len; |
||||
if (newMatchLen > OPTIMAL_ML) { |
||||
newMatchLen = OPTIMAL_ML; |
||||
} |
||||
if (match1.start + newMatchLen > match2.end() - MIN_MATCH) { |
||||
newMatchLen = match2.start - match1.start + match2.len - MIN_MATCH; |
||||
} |
||||
final int correction = newMatchLen - (match2.start - match1.start); |
||||
if (correction > 0) { |
||||
match2.fix(correction); |
||||
} |
||||
} |
||||
|
||||
if (match2.start + match2.len >= mfLimit |
||||
|| !ht.insertAndFindWiderMatch(src, match2.end() - 3, match2.start, matchLimit, match2.len, match3)) { |
||||
// no better match -> 2 sequences to encode
|
||||
if (match2.start < match1.end()) { |
||||
match1.len = match2.start - match1.start; |
||||
} |
||||
// encode seq 1
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
// encode seq 2
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match2.start, match2.ref, match2.len, dest, dOff, destEnd); |
||||
anchor = sOff = match2.end(); |
||||
continue main; |
||||
} |
||||
|
||||
if (match3.start < match1.end() + 3) { // Not enough space for match 2 : remove it
|
||||
if (match3.start >= match1.end()) { // // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1
|
||||
if (match2.start < match1.end()) { |
||||
final int correction = match1.end() - match2.start; |
||||
match2.fix(correction); |
||||
if (match2.len < MIN_MATCH) { |
||||
copyTo(match3, match2); |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
|
||||
copyTo(match3, match1); |
||||
copyTo(match2, match0); |
||||
|
||||
continue search2; |
||||
} |
||||
|
||||
copyTo(match3, match2); |
||||
continue search3; |
||||
} |
||||
|
||||
// OK, now we have 3 ascending matches; let's write at least the first one
|
||||
if (match2.start < match1.end()) { |
||||
if (match2.start - match1.start < ML_MASK) { |
||||
if (match1.len > OPTIMAL_ML) { |
||||
match1.len = OPTIMAL_ML; |
||||
} |
||||
if (match1.end() > match2.end() - MIN_MATCH) { |
||||
match1.len = match2.end() - match1.start - MIN_MATCH; |
||||
} |
||||
final int correction = match1.end() - match2.start; |
||||
match2.fix(correction); |
||||
} else { |
||||
match1.len = match2.start - match1.start; |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
|
||||
copyTo(match2, match1); |
||||
copyTo(match3, match2); |
||||
|
||||
continue search3; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,550 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.*; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Utils.*; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.util.Arrays; |
||||
|
||||
import com.fr.third.net.jpountz.lz4.LZ4Utils.Match; |
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.UnsafeUtils; |
||||
|
||||
/** |
||||
* High compression compressor. |
||||
*/ |
||||
final class LZ4HCJavaUnsafeCompressor extends LZ4Compressor { |
||||
|
||||
public static final LZ4Compressor INSTANCE = new LZ4HCJavaUnsafeCompressor(); |
||||
|
||||
private final int maxAttempts; |
||||
final int compressionLevel; |
||||
|
||||
LZ4HCJavaUnsafeCompressor() { this(DEFAULT_COMPRESSION_LEVEL); } |
||||
LZ4HCJavaUnsafeCompressor(int compressionLevel) { |
||||
this.maxAttempts = 1<<(compressionLevel-1); |
||||
this.compressionLevel = compressionLevel; |
||||
} |
||||
|
||||
private class HashTable { |
||||
static final int MASK = MAX_DISTANCE - 1; |
||||
int nextToUpdate; |
||||
private final int base; |
||||
private final int[] hashTable; |
||||
private final short[] chainTable; |
||||
|
||||
HashTable(int base) { |
||||
this.base = base; |
||||
nextToUpdate = base; |
||||
hashTable = new int[HASH_TABLE_SIZE_HC]; |
||||
Arrays.fill(hashTable, -1); |
||||
chainTable = new short[MAX_DISTANCE]; |
||||
} |
||||
|
||||
private int hashPointer(byte[] bytes, int off) { |
||||
final int v = UnsafeUtils.readInt(bytes, off); |
||||
return hashPointer(v); |
||||
} |
||||
|
||||
private int hashPointer(ByteBuffer bytes, int off) { |
||||
final int v = ByteBufferUtils.readInt(bytes, off); |
||||
return hashPointer(v); |
||||
} |
||||
|
||||
private int hashPointer(int v) { |
||||
final int h = hashHC(v); |
||||
return hashTable[h]; |
||||
} |
||||
|
||||
private int next(int off) { |
||||
return off - (chainTable[off & MASK] & 0xFFFF); |
||||
} |
||||
|
||||
private void addHash(byte[] bytes, int off) { |
||||
final int v = UnsafeUtils.readInt(bytes, off); |
||||
addHash(v, off); |
||||
} |
||||
|
||||
private void addHash(ByteBuffer bytes, int off) { |
||||
final int v = ByteBufferUtils.readInt(bytes, off); |
||||
addHash(v, off); |
||||
} |
||||
|
||||
private void addHash(int v, int off) { |
||||
final int h = hashHC(v); |
||||
int delta = off - hashTable[h]; |
||||
assert delta > 0 : delta; |
||||
if (delta >= MAX_DISTANCE) { |
||||
delta = MAX_DISTANCE - 1; |
||||
} |
||||
chainTable[off & MASK] = (short) delta; |
||||
hashTable[h] = off; |
||||
} |
||||
|
||||
void insert(int off, byte[] bytes) { |
||||
for (; nextToUpdate < off; ++nextToUpdate) { |
||||
addHash(bytes, nextToUpdate); |
||||
} |
||||
} |
||||
|
||||
void insert(int off, ByteBuffer bytes) { |
||||
for (; nextToUpdate < off; ++nextToUpdate) { |
||||
addHash(bytes, nextToUpdate); |
||||
} |
||||
} |
||||
|
||||
|
||||
|
||||
boolean insertAndFindBestMatch(byte[] buf, int off, int matchLimit, Match match) { |
||||
match.start = off; |
||||
match.len = 0; |
||||
int delta = 0; |
||||
int repl = 0; |
||||
|
||||
insert(off, buf); |
||||
|
||||
int ref = hashPointer(buf, off); |
||||
|
||||
if (ref >= off - 4 && ref <= off && ref >= base) { // potential repetition
|
||||
if (LZ4UnsafeUtils.readIntEquals(buf, ref, off)) { // confirmed
|
||||
delta = off - ref; |
||||
repl = match.len = MIN_MATCH + LZ4UnsafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
match.ref = ref; |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
for (int i = 0; i < maxAttempts; ++i) { |
||||
if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { |
||||
break; |
||||
} |
||||
if (LZ4UnsafeUtils.readIntEquals(buf, ref, off)) { |
||||
final int matchLen = MIN_MATCH + LZ4UnsafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
if (matchLen > match.len) { |
||||
match.ref = ref; |
||||
match.len = matchLen; |
||||
} |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
if (repl != 0) { |
||||
int ptr = off; |
||||
final int end = off + repl - (MIN_MATCH - 1); |
||||
while (ptr < end - delta) { |
||||
chainTable[ptr & MASK] = (short) delta; // pre load
|
||||
++ptr; |
||||
} |
||||
do { |
||||
chainTable[ptr & MASK] = (short) delta; |
||||
hashTable[hashHC(UnsafeUtils.readInt(buf, ptr))] = ptr; |
||||
++ptr; |
||||
} while (ptr < end); |
||||
nextToUpdate = end; |
||||
} |
||||
|
||||
return match.len != 0; |
||||
} |
||||
|
||||
boolean insertAndFindWiderMatch(byte[] buf, int off, int startLimit, int matchLimit, int minLen, Match match) { |
||||
match.len = minLen; |
||||
|
||||
insert(off, buf); |
||||
|
||||
final int delta = off - startLimit; |
||||
int ref = hashPointer(buf, off); |
||||
for (int i = 0; i < maxAttempts; ++i) { |
||||
if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { |
||||
break; |
||||
} |
||||
if (LZ4UnsafeUtils.readIntEquals(buf, ref, off)) { |
||||
final int matchLenForward = MIN_MATCH +LZ4UnsafeUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
final int matchLenBackward = LZ4UnsafeUtils.commonBytesBackward(buf, ref, off, base, startLimit); |
||||
final int matchLen = matchLenBackward + matchLenForward; |
||||
if (matchLen > match.len) { |
||||
match.len = matchLen; |
||||
match.ref = ref - matchLenBackward; |
||||
match.start = off - matchLenBackward; |
||||
} |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
return match.len > minLen; |
||||
} |
||||
|
||||
|
||||
boolean insertAndFindBestMatch(ByteBuffer buf, int off, int matchLimit, Match match) { |
||||
match.start = off; |
||||
match.len = 0; |
||||
int delta = 0; |
||||
int repl = 0; |
||||
|
||||
insert(off, buf); |
||||
|
||||
int ref = hashPointer(buf, off); |
||||
|
||||
if (ref >= off - 4 && ref <= off && ref >= base) { // potential repetition
|
||||
if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) { // confirmed
|
||||
delta = off - ref; |
||||
repl = match.len = MIN_MATCH + LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
match.ref = ref; |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
for (int i = 0; i < maxAttempts; ++i) { |
||||
if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { |
||||
break; |
||||
} |
||||
if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) { |
||||
final int matchLen = MIN_MATCH + LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
if (matchLen > match.len) { |
||||
match.ref = ref; |
||||
match.len = matchLen; |
||||
} |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
if (repl != 0) { |
||||
int ptr = off; |
||||
final int end = off + repl - (MIN_MATCH - 1); |
||||
while (ptr < end - delta) { |
||||
chainTable[ptr & MASK] = (short) delta; // pre load
|
||||
++ptr; |
||||
} |
||||
do { |
||||
chainTable[ptr & MASK] = (short) delta; |
||||
hashTable[hashHC(ByteBufferUtils.readInt(buf, ptr))] = ptr; |
||||
++ptr; |
||||
} while (ptr < end); |
||||
nextToUpdate = end; |
||||
} |
||||
|
||||
return match.len != 0; |
||||
} |
||||
|
||||
boolean insertAndFindWiderMatch(ByteBuffer buf, int off, int startLimit, int matchLimit, int minLen, Match match) { |
||||
match.len = minLen; |
||||
|
||||
insert(off, buf); |
||||
|
||||
final int delta = off - startLimit; |
||||
int ref = hashPointer(buf, off); |
||||
for (int i = 0; i < maxAttempts; ++i) { |
||||
if (ref < Math.max(base, off - MAX_DISTANCE + 1) || ref > off) { |
||||
break; |
||||
} |
||||
if (LZ4ByteBufferUtils.readIntEquals(buf, ref, off)) { |
||||
final int matchLenForward = MIN_MATCH +LZ4ByteBufferUtils.commonBytes(buf, ref + MIN_MATCH, off + MIN_MATCH, matchLimit); |
||||
final int matchLenBackward = LZ4ByteBufferUtils.commonBytesBackward(buf, ref, off, base, startLimit); |
||||
final int matchLen = matchLenBackward + matchLenForward; |
||||
if (matchLen > match.len) { |
||||
match.len = matchLen; |
||||
match.ref = ref - matchLenBackward; |
||||
match.start = off - matchLenBackward; |
||||
} |
||||
} |
||||
ref = next(ref); |
||||
} |
||||
|
||||
return match.len > minLen; |
||||
} |
||||
|
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) { |
||||
|
||||
UnsafeUtils.checkRange(src, srcOff, srcLen); |
||||
UnsafeUtils.checkRange(dest, destOff, maxDestLen); |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
final int destEnd = destOff + maxDestLen; |
||||
final int mfLimit = srcEnd - MF_LIMIT; |
||||
final int matchLimit = srcEnd - LAST_LITERALS; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
int anchor = sOff++; |
||||
|
||||
final HashTable ht = new HashTable(srcOff); |
||||
final Match match0 = new Match(); |
||||
final Match match1 = new Match(); |
||||
final Match match2 = new Match(); |
||||
final Match match3 = new Match(); |
||||
|
||||
main: |
||||
while (sOff < mfLimit) { |
||||
if (!ht.insertAndFindBestMatch(src, sOff, matchLimit, match1)) { |
||||
++sOff; |
||||
continue; |
||||
} |
||||
|
||||
// saved, in case we would skip too much
|
||||
copyTo(match1, match0); |
||||
|
||||
search2: |
||||
while (true) { |
||||
assert match1.start >= anchor; |
||||
if (match1.end() >= mfLimit |
||||
|| !ht.insertAndFindWiderMatch(src, match1.end() - 2, match1.start + 1, matchLimit, match1.len, match2)) { |
||||
// no better match
|
||||
dOff = LZ4UnsafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
continue main; |
||||
} |
||||
|
||||
if (match0.start < match1.start) { |
||||
if (match2.start < match1.start + match0.len) { // empirical
|
||||
copyTo(match0, match1); |
||||
} |
||||
} |
||||
assert match2.start > match1.start; |
||||
|
||||
if (match2.start - match1.start < 3) { // First Match too small : removed
|
||||
copyTo(match2, match1); |
||||
continue search2; |
||||
} |
||||
|
||||
search3: |
||||
while (true) { |
||||
if (match2.start - match1.start < OPTIMAL_ML) { |
||||
int newMatchLen = match1.len; |
||||
if (newMatchLen > OPTIMAL_ML) { |
||||
newMatchLen = OPTIMAL_ML; |
||||
} |
||||
if (match1.start + newMatchLen > match2.end() - MIN_MATCH) { |
||||
newMatchLen = match2.start - match1.start + match2.len - MIN_MATCH; |
||||
} |
||||
final int correction = newMatchLen - (match2.start - match1.start); |
||||
if (correction > 0) { |
||||
match2.fix(correction); |
||||
} |
||||
} |
||||
|
||||
if (match2.start + match2.len >= mfLimit |
||||
|| !ht.insertAndFindWiderMatch(src, match2.end() - 3, match2.start, matchLimit, match2.len, match3)) { |
||||
// no better match -> 2 sequences to encode
|
||||
if (match2.start < match1.end()) { |
||||
match1.len = match2.start - match1.start; |
||||
} |
||||
// encode seq 1
|
||||
dOff = LZ4UnsafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
// encode seq 2
|
||||
dOff = LZ4UnsafeUtils.encodeSequence(src, anchor, match2.start, match2.ref, match2.len, dest, dOff, destEnd); |
||||
anchor = sOff = match2.end(); |
||||
continue main; |
||||
} |
||||
|
||||
if (match3.start < match1.end() + 3) { // Not enough space for match 2 : remove it
|
||||
if (match3.start >= match1.end()) { // // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1
|
||||
if (match2.start < match1.end()) { |
||||
final int correction = match1.end() - match2.start; |
||||
match2.fix(correction); |
||||
if (match2.len < MIN_MATCH) { |
||||
copyTo(match3, match2); |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4UnsafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
|
||||
copyTo(match3, match1); |
||||
copyTo(match2, match0); |
||||
|
||||
continue search2; |
||||
} |
||||
|
||||
copyTo(match3, match2); |
||||
continue search3; |
||||
} |
||||
|
||||
// OK, now we have 3 ascending matches; let's write at least the first one
|
||||
if (match2.start < match1.end()) { |
||||
if (match2.start - match1.start < ML_MASK) { |
||||
if (match1.len > OPTIMAL_ML) { |
||||
match1.len = OPTIMAL_ML; |
||||
} |
||||
if (match1.end() > match2.end() - MIN_MATCH) { |
||||
match1.len = match2.end() - match1.start - MIN_MATCH; |
||||
} |
||||
final int correction = match1.end() - match2.start; |
||||
match2.fix(correction); |
||||
} else { |
||||
match1.len = match2.start - match1.start; |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4UnsafeUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
|
||||
copyTo(match2, match1); |
||||
copyTo(match3, match2); |
||||
|
||||
continue search3; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
dOff = LZ4UnsafeUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) { |
||||
|
||||
if (src.hasArray() && dest.hasArray()) { |
||||
return compress(src.array(), srcOff + src.arrayOffset(), srcLen, dest.array(), destOff + dest.arrayOffset(), maxDestLen); |
||||
} |
||||
src = ByteBufferUtils.inNativeByteOrder(src); |
||||
dest = ByteBufferUtils.inNativeByteOrder(dest); |
||||
|
||||
ByteBufferUtils.checkRange(src, srcOff, srcLen); |
||||
ByteBufferUtils.checkRange(dest, destOff, maxDestLen); |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
final int destEnd = destOff + maxDestLen; |
||||
final int mfLimit = srcEnd - MF_LIMIT; |
||||
final int matchLimit = srcEnd - LAST_LITERALS; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
int anchor = sOff++; |
||||
|
||||
final HashTable ht = new HashTable(srcOff); |
||||
final Match match0 = new Match(); |
||||
final Match match1 = new Match(); |
||||
final Match match2 = new Match(); |
||||
final Match match3 = new Match(); |
||||
|
||||
main: |
||||
while (sOff < mfLimit) { |
||||
if (!ht.insertAndFindBestMatch(src, sOff, matchLimit, match1)) { |
||||
++sOff; |
||||
continue; |
||||
} |
||||
|
||||
// saved, in case we would skip too much
|
||||
copyTo(match1, match0); |
||||
|
||||
search2: |
||||
while (true) { |
||||
assert match1.start >= anchor; |
||||
if (match1.end() >= mfLimit |
||||
|| !ht.insertAndFindWiderMatch(src, match1.end() - 2, match1.start + 1, matchLimit, match1.len, match2)) { |
||||
// no better match
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
continue main; |
||||
} |
||||
|
||||
if (match0.start < match1.start) { |
||||
if (match2.start < match1.start + match0.len) { // empirical
|
||||
copyTo(match0, match1); |
||||
} |
||||
} |
||||
assert match2.start > match1.start; |
||||
|
||||
if (match2.start - match1.start < 3) { // First Match too small : removed
|
||||
copyTo(match2, match1); |
||||
continue search2; |
||||
} |
||||
|
||||
search3: |
||||
while (true) { |
||||
if (match2.start - match1.start < OPTIMAL_ML) { |
||||
int newMatchLen = match1.len; |
||||
if (newMatchLen > OPTIMAL_ML) { |
||||
newMatchLen = OPTIMAL_ML; |
||||
} |
||||
if (match1.start + newMatchLen > match2.end() - MIN_MATCH) { |
||||
newMatchLen = match2.start - match1.start + match2.len - MIN_MATCH; |
||||
} |
||||
final int correction = newMatchLen - (match2.start - match1.start); |
||||
if (correction > 0) { |
||||
match2.fix(correction); |
||||
} |
||||
} |
||||
|
||||
if (match2.start + match2.len >= mfLimit |
||||
|| !ht.insertAndFindWiderMatch(src, match2.end() - 3, match2.start, matchLimit, match2.len, match3)) { |
||||
// no better match -> 2 sequences to encode
|
||||
if (match2.start < match1.end()) { |
||||
match1.len = match2.start - match1.start; |
||||
} |
||||
// encode seq 1
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
// encode seq 2
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match2.start, match2.ref, match2.len, dest, dOff, destEnd); |
||||
anchor = sOff = match2.end(); |
||||
continue main; |
||||
} |
||||
|
||||
if (match3.start < match1.end() + 3) { // Not enough space for match 2 : remove it
|
||||
if (match3.start >= match1.end()) { // // can write Seq1 immediately ==> Seq2 is removed, so Seq3 becomes Seq1
|
||||
if (match2.start < match1.end()) { |
||||
final int correction = match1.end() - match2.start; |
||||
match2.fix(correction); |
||||
if (match2.len < MIN_MATCH) { |
||||
copyTo(match3, match2); |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
|
||||
copyTo(match3, match1); |
||||
copyTo(match2, match0); |
||||
|
||||
continue search2; |
||||
} |
||||
|
||||
copyTo(match3, match2); |
||||
continue search3; |
||||
} |
||||
|
||||
// OK, now we have 3 ascending matches; let's write at least the first one
|
||||
if (match2.start < match1.end()) { |
||||
if (match2.start - match1.start < ML_MASK) { |
||||
if (match1.len > OPTIMAL_ML) { |
||||
match1.len = OPTIMAL_ML; |
||||
} |
||||
if (match1.end() > match2.end() - MIN_MATCH) { |
||||
match1.len = match2.end() - match1.start - MIN_MATCH; |
||||
} |
||||
final int correction = match1.end() - match2.start; |
||||
match2.fix(correction); |
||||
} else { |
||||
match1.len = match2.start - match1.start; |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.encodeSequence(src, anchor, match1.start, match1.ref, match1.len, dest, dOff, destEnd); |
||||
anchor = sOff = match1.end(); |
||||
|
||||
copyTo(match2, match1); |
||||
copyTo(match3, match2); |
||||
|
||||
continue search3; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,41 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.Native; |
||||
|
||||
|
||||
/** |
||||
* JNI bindings to the original C implementation of LZ4. |
||||
*/ |
||||
enum LZ4JNI { |
||||
; |
||||
|
||||
static { |
||||
Native.load(); |
||||
init(); |
||||
} |
||||
|
||||
static native void init(); |
||||
static native int LZ4_compress_limitedOutput(byte[] srcArray, ByteBuffer srcBuffer, int srcOff, int srcLen, byte[] destArray, ByteBuffer destBuffer, int destOff, int maxDestLen); |
||||
static native int LZ4_compressHC(byte[] srcArray, ByteBuffer srcBuffer, int srcOff, int srcLen, byte[] destArray, ByteBuffer destBuffer, int destOff, int maxDestLen, int compressionLevel); |
||||
static native int LZ4_decompress_fast(byte[] srcArray, ByteBuffer srcBuffer, int srcOff, byte[] destArray, ByteBuffer destBuffer, int destOff, int destLen); |
||||
static native int LZ4_decompress_safe(byte[] srcArray, ByteBuffer srcBuffer, int srcOff, int srcLen, byte[] destArray, ByteBuffer destBuffer, int destOff, int maxDestLen); |
||||
static native int LZ4_compressBound(int len); |
||||
|
||||
} |
||||
|
@ -0,0 +1,80 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.checkNotReadOnly; |
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.checkRange; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.checkRange; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
/** |
||||
* Fast {@link LZ4FastCompressor}s implemented with JNI bindings to the original C |
||||
* implementation of LZ4. |
||||
*/ |
||||
final class LZ4JNICompressor extends LZ4Compressor { |
||||
|
||||
public static final LZ4Compressor INSTANCE = new LZ4JNICompressor(); |
||||
private static LZ4Compressor SAFE_INSTANCE; |
||||
|
||||
@Override |
||||
public int compress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) { |
||||
checkRange(src, srcOff, srcLen); |
||||
checkRange(dest, destOff, maxDestLen); |
||||
final int result = LZ4JNI.LZ4_compress_limitedOutput(src, null, srcOff, srcLen, dest, null, destOff, maxDestLen); |
||||
if (result <= 0) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public int compress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) { |
||||
checkNotReadOnly(dest); |
||||
checkRange(src, srcOff, srcLen); |
||||
checkRange(dest, destOff, maxDestLen); |
||||
|
||||
if ((src.hasArray() || src.isDirect()) && (dest.hasArray() || dest.isDirect())) { |
||||
byte[] srcArr = null, destArr = null; |
||||
ByteBuffer srcBuf = null, destBuf = null; |
||||
if (src.hasArray()) { |
||||
srcArr = src.array(); |
||||
srcOff += src.arrayOffset(); |
||||
} else { |
||||
assert src.isDirect(); |
||||
srcBuf = src; |
||||
} |
||||
if (dest.hasArray()) { |
||||
destArr = dest.array(); |
||||
destOff += dest.arrayOffset(); |
||||
} else { |
||||
assert dest.isDirect(); |
||||
destBuf = dest; |
||||
} |
||||
|
||||
final int result = LZ4JNI.LZ4_compress_limitedOutput(srcArr, srcBuf, srcOff, srcLen, destArr, destBuf, destOff, maxDestLen); |
||||
if (result <= 0) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
return result; |
||||
} else { |
||||
LZ4Compressor safeInstance = SAFE_INSTANCE; |
||||
if (safeInstance == null) { |
||||
safeInstance = SAFE_INSTANCE = LZ4Factory.safeInstance().fastCompressor(); |
||||
} |
||||
return safeInstance.compress(src, srcOff, srcLen, dest, destOff, maxDestLen); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,82 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
|
||||
/** |
||||
* {@link LZ4FastDecompressor} implemented with JNI bindings to the original C |
||||
* implementation of LZ4. |
||||
*/ |
||||
final class LZ4JNIFastDecompressor extends LZ4FastDecompressor { |
||||
|
||||
public static final LZ4JNIFastDecompressor INSTANCE = new LZ4JNIFastDecompressor(); |
||||
private static LZ4FastDecompressor SAFE_INSTANCE; |
||||
|
||||
@Override |
||||
public final int decompress(byte[] src, int srcOff, byte[] dest, int destOff, int destLen) { |
||||
SafeUtils.checkRange(src, srcOff); |
||||
SafeUtils.checkRange(dest, destOff, destLen); |
||||
final int result = LZ4JNI.LZ4_decompress_fast(src, null, srcOff, dest, null, destOff, destLen); |
||||
if (result < 0) { |
||||
throw new LZ4Exception("Error decoding offset " + (srcOff - result) + " of input buffer"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public int decompress(ByteBuffer src, int srcOff, ByteBuffer dest, int destOff, int destLen) { |
||||
ByteBufferUtils.checkNotReadOnly(dest); |
||||
ByteBufferUtils.checkRange(src, srcOff); |
||||
ByteBufferUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if ((src.hasArray() || src.isDirect()) && (dest.hasArray() || dest.isDirect())) { |
||||
byte[] srcArr = null, destArr = null; |
||||
ByteBuffer srcBuf = null, destBuf = null; |
||||
if (src.hasArray()) { |
||||
srcArr = src.array(); |
||||
srcOff += src.arrayOffset(); |
||||
} else { |
||||
assert src.isDirect(); |
||||
srcBuf = src; |
||||
} |
||||
if (dest.hasArray()) { |
||||
destArr = dest.array(); |
||||
destOff += dest.arrayOffset(); |
||||
} else { |
||||
assert dest.isDirect(); |
||||
destBuf = dest; |
||||
} |
||||
|
||||
final int result = LZ4JNI.LZ4_decompress_fast(srcArr, srcBuf, srcOff, destArr, destBuf, destOff, destLen); |
||||
if (result < 0) { |
||||
throw new LZ4Exception("Error decoding offset " + (srcOff - result) + " of input buffer"); |
||||
} |
||||
return result; |
||||
} else { |
||||
LZ4FastDecompressor safeInstance = SAFE_INSTANCE; |
||||
if (safeInstance == null) { |
||||
safeInstance = SAFE_INSTANCE = LZ4Factory.safeInstance().fastDecompressor(); |
||||
} |
||||
return safeInstance.decompress(src, srcOff, dest, destOff, destLen); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,81 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
/** |
||||
* {@link LZ4SafeDecompressor} implemented with JNI bindings to the original C |
||||
* implementation of LZ4. |
||||
*/ |
||||
final class LZ4JNISafeDecompressor extends LZ4SafeDecompressor { |
||||
|
||||
public static final LZ4JNISafeDecompressor INSTANCE = new LZ4JNISafeDecompressor(); |
||||
private static LZ4SafeDecompressor SAFE_INSTANCE; |
||||
|
||||
@Override |
||||
public final int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen) { |
||||
SafeUtils.checkRange(src, srcOff, srcLen); |
||||
SafeUtils.checkRange(dest, destOff, maxDestLen); |
||||
final int result = LZ4JNI.LZ4_decompress_safe(src, null, srcOff, srcLen, dest, null, destOff, maxDestLen); |
||||
if (result < 0) { |
||||
throw new LZ4Exception("Error decoding offset " + (srcOff - result) + " of input buffer"); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public int decompress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen) { |
||||
ByteBufferUtils.checkNotReadOnly(dest); |
||||
ByteBufferUtils.checkRange(src, srcOff, srcLen); |
||||
ByteBufferUtils.checkRange(dest, destOff, maxDestLen); |
||||
|
||||
if ((src.hasArray() || src.isDirect()) && (dest.hasArray() || dest.isDirect())) { |
||||
byte[] srcArr = null, destArr = null; |
||||
ByteBuffer srcBuf = null, destBuf = null; |
||||
if (src.hasArray()) { |
||||
srcArr = src.array(); |
||||
srcOff += src.arrayOffset(); |
||||
} else { |
||||
assert src.isDirect(); |
||||
srcBuf = src; |
||||
} |
||||
if (dest.hasArray()) { |
||||
destArr = dest.array(); |
||||
destOff += dest.arrayOffset(); |
||||
} else { |
||||
assert dest.isDirect(); |
||||
destBuf = dest; |
||||
} |
||||
|
||||
final int result = LZ4JNI.LZ4_decompress_safe(srcArr, srcBuf, srcOff, srcLen, destArr, destBuf, destOff, maxDestLen); |
||||
if (result < 0) { |
||||
throw new LZ4Exception("Error decoding offset " + (srcOff - result) + " of input buffer"); |
||||
} |
||||
return result; |
||||
} else { |
||||
LZ4SafeDecompressor safeInstance = SAFE_INSTANCE; |
||||
if (safeInstance == null) { |
||||
safeInstance = SAFE_INSTANCE = LZ4Factory.safeInstance().safeDecompressor(); |
||||
} |
||||
return safeInstance.decompress(src, srcOff, srcLen, dest, destOff, maxDestLen); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,511 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.*; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Utils.*; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.util.Arrays; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
/** |
||||
* Compressor. |
||||
*/ |
||||
final class LZ4JavaSafeCompressor extends LZ4Compressor { |
||||
|
||||
public static final LZ4Compressor INSTANCE = new LZ4JavaSafeCompressor(); |
||||
|
||||
static int compress64k(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int destEnd) { |
||||
final int srcEnd = srcOff + srcLen; |
||||
final int srcLimit = srcEnd - LAST_LITERALS; |
||||
final int mflimit = srcEnd - MF_LIMIT; |
||||
|
||||
int sOff = srcOff, dOff = destOff; |
||||
|
||||
int anchor = sOff; |
||||
|
||||
if (srcLen >= MIN_LENGTH) { |
||||
|
||||
final short[] hashTable = new short[HASH_TABLE_SIZE_64K]; |
||||
|
||||
++sOff; |
||||
|
||||
main: |
||||
while (true) { |
||||
|
||||
// find a match
|
||||
int forwardOff = sOff; |
||||
|
||||
int ref; |
||||
int step = 1; |
||||
int searchMatchNb = 1 << SKIP_STRENGTH; |
||||
do { |
||||
sOff = forwardOff; |
||||
forwardOff += step; |
||||
step = searchMatchNb++ >>> SKIP_STRENGTH; |
||||
|
||||
if (forwardOff > mflimit) { |
||||
break main; |
||||
} |
||||
|
||||
final int h = hash64k(SafeUtils.readInt(src, sOff)); |
||||
ref = srcOff + SafeUtils.readShort(hashTable, h); |
||||
SafeUtils.writeShort(hashTable, h, sOff - srcOff); |
||||
} while (!LZ4SafeUtils.readIntEquals(src, ref, sOff)); |
||||
|
||||
// catch up
|
||||
final int excess = LZ4SafeUtils.commonBytesBackward(src, ref, sOff, srcOff, anchor); |
||||
sOff -= excess; |
||||
ref -= excess; |
||||
|
||||
// sequence == refsequence
|
||||
final int runLen = sOff - anchor; |
||||
|
||||
// encode literal length
|
||||
int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
SafeUtils.writeByte(dest, tokenOff, RUN_MASK << ML_BITS); |
||||
dOff = LZ4SafeUtils.writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
SafeUtils.writeByte(dest, tokenOff, runLen << ML_BITS); |
||||
} |
||||
|
||||
// copy literals
|
||||
LZ4SafeUtils.wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
while (true) { |
||||
// encode offset
|
||||
SafeUtils.writeShortLE(dest, dOff, (short) (sOff - ref)); |
||||
dOff += 2; |
||||
|
||||
// count nb matches
|
||||
sOff += MIN_MATCH; |
||||
ref += MIN_MATCH; |
||||
final int matchLen = LZ4SafeUtils.commonBytes(src, ref, sOff, srcLimit); |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
sOff += matchLen; |
||||
|
||||
// encode match len
|
||||
if (matchLen >= ML_MASK) { |
||||
SafeUtils.writeByte(dest, tokenOff, SafeUtils.readByte(dest, tokenOff) | ML_MASK); |
||||
dOff = LZ4SafeUtils.writeLen(matchLen - ML_MASK, dest, dOff); |
||||
} else { |
||||
SafeUtils.writeByte(dest, tokenOff, SafeUtils.readByte(dest, tokenOff) | matchLen); |
||||
} |
||||
|
||||
// test end of chunk
|
||||
if (sOff > mflimit) { |
||||
anchor = sOff; |
||||
break main; |
||||
} |
||||
|
||||
// fill table
|
||||
SafeUtils.writeShort(hashTable, hash64k(SafeUtils.readInt(src, sOff - 2)), sOff - 2 - srcOff); |
||||
|
||||
// test next position
|
||||
final int h = hash64k(SafeUtils.readInt(src, sOff)); |
||||
ref = srcOff + SafeUtils.readShort(hashTable, h); |
||||
SafeUtils.writeShort(hashTable, h, sOff - srcOff); |
||||
|
||||
if (!LZ4SafeUtils.readIntEquals(src, sOff, ref)) { |
||||
break; |
||||
} |
||||
|
||||
tokenOff = dOff++; |
||||
SafeUtils.writeByte(dest, tokenOff, 0); |
||||
} |
||||
|
||||
// prepare next loop
|
||||
anchor = sOff++; |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4SafeUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
@Override |
||||
public int compress(byte[] src, final int srcOff, int srcLen, byte[] dest, final int destOff, int maxDestLen) { |
||||
|
||||
SafeUtils.checkRange(src, srcOff, srcLen); |
||||
SafeUtils.checkRange(dest, destOff, maxDestLen); |
||||
final int destEnd = destOff + maxDestLen; |
||||
|
||||
if (srcLen < LZ4_64K_LIMIT) { |
||||
return compress64k(src, srcOff, srcLen, dest, destOff, destEnd); |
||||
} |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
final int srcLimit = srcEnd - LAST_LITERALS; |
||||
final int mflimit = srcEnd - MF_LIMIT; |
||||
|
||||
int sOff = srcOff, dOff = destOff; |
||||
int anchor = sOff++; |
||||
|
||||
final int[] hashTable = new int[HASH_TABLE_SIZE]; |
||||
Arrays.fill(hashTable, anchor); |
||||
|
||||
main: |
||||
while (true) { |
||||
|
||||
// find a match
|
||||
int forwardOff = sOff; |
||||
|
||||
int ref; |
||||
int step = 1; |
||||
int searchMatchNb = 1 << SKIP_STRENGTH; |
||||
int back; |
||||
do { |
||||
sOff = forwardOff; |
||||
forwardOff += step; |
||||
step = searchMatchNb++ >>> SKIP_STRENGTH; |
||||
|
||||
if (forwardOff > mflimit) { |
||||
break main; |
||||
} |
||||
|
||||
final int h = hash(SafeUtils.readInt(src, sOff)); |
||||
ref = SafeUtils.readInt(hashTable, h); |
||||
back = sOff - ref; |
||||
SafeUtils.writeInt(hashTable, h, sOff); |
||||
} while (back >= MAX_DISTANCE || !LZ4SafeUtils.readIntEquals(src, ref, sOff)); |
||||
|
||||
|
||||
final int excess = LZ4SafeUtils.commonBytesBackward(src, ref, sOff, srcOff, anchor); |
||||
sOff -= excess; |
||||
ref -= excess; |
||||
|
||||
// sequence == refsequence
|
||||
final int runLen = sOff - anchor; |
||||
|
||||
// encode literal length
|
||||
int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
SafeUtils.writeByte(dest, tokenOff, RUN_MASK << ML_BITS); |
||||
dOff = LZ4SafeUtils.writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
SafeUtils.writeByte(dest, tokenOff, runLen << ML_BITS); |
||||
} |
||||
|
||||
// copy literals
|
||||
LZ4SafeUtils.wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
while (true) { |
||||
// encode offset
|
||||
SafeUtils.writeShortLE(dest, dOff, back); |
||||
dOff += 2; |
||||
|
||||
// count nb matches
|
||||
sOff += MIN_MATCH; |
||||
final int matchLen = LZ4SafeUtils.commonBytes(src, ref + MIN_MATCH, sOff, srcLimit); |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
sOff += matchLen; |
||||
|
||||
// encode match len
|
||||
if (matchLen >= ML_MASK) { |
||||
SafeUtils.writeByte(dest, tokenOff, SafeUtils.readByte(dest, tokenOff) | ML_MASK); |
||||
dOff = LZ4SafeUtils.writeLen(matchLen - ML_MASK, dest, dOff); |
||||
} else { |
||||
SafeUtils.writeByte(dest, tokenOff, SafeUtils.readByte(dest, tokenOff) | matchLen); |
||||
} |
||||
|
||||
// test end of chunk
|
||||
if (sOff > mflimit) { |
||||
anchor = sOff; |
||||
break main; |
||||
} |
||||
|
||||
// fill table
|
||||
SafeUtils.writeInt(hashTable, hash(SafeUtils.readInt(src, sOff - 2)), sOff - 2); |
||||
|
||||
// test next position
|
||||
final int h = hash(SafeUtils.readInt(src, sOff)); |
||||
ref = SafeUtils.readInt(hashTable, h); |
||||
SafeUtils.writeInt(hashTable, h, sOff); |
||||
back = sOff - ref; |
||||
|
||||
if (back >= MAX_DISTANCE || !LZ4SafeUtils.readIntEquals(src, ref, sOff)) { |
||||
break; |
||||
} |
||||
|
||||
tokenOff = dOff++; |
||||
SafeUtils.writeByte(dest, tokenOff, 0); |
||||
} |
||||
|
||||
// prepare next loop
|
||||
anchor = sOff++; |
||||
} |
||||
|
||||
dOff = LZ4SafeUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
|
||||
static int compress64k(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int destEnd) { |
||||
final int srcEnd = srcOff + srcLen; |
||||
final int srcLimit = srcEnd - LAST_LITERALS; |
||||
final int mflimit = srcEnd - MF_LIMIT; |
||||
|
||||
int sOff = srcOff, dOff = destOff; |
||||
|
||||
int anchor = sOff; |
||||
|
||||
if (srcLen >= MIN_LENGTH) { |
||||
|
||||
final short[] hashTable = new short[HASH_TABLE_SIZE_64K]; |
||||
|
||||
++sOff; |
||||
|
||||
main: |
||||
while (true) { |
||||
|
||||
// find a match
|
||||
int forwardOff = sOff; |
||||
|
||||
int ref; |
||||
int step = 1; |
||||
int searchMatchNb = 1 << SKIP_STRENGTH; |
||||
do { |
||||
sOff = forwardOff; |
||||
forwardOff += step; |
||||
step = searchMatchNb++ >>> SKIP_STRENGTH; |
||||
|
||||
if (forwardOff > mflimit) { |
||||
break main; |
||||
} |
||||
|
||||
final int h = hash64k(ByteBufferUtils.readInt(src, sOff)); |
||||
ref = srcOff + SafeUtils.readShort(hashTable, h); |
||||
SafeUtils.writeShort(hashTable, h, sOff - srcOff); |
||||
} while (!LZ4ByteBufferUtils.readIntEquals(src, ref, sOff)); |
||||
|
||||
// catch up
|
||||
final int excess = LZ4ByteBufferUtils.commonBytesBackward(src, ref, sOff, srcOff, anchor); |
||||
sOff -= excess; |
||||
ref -= excess; |
||||
|
||||
// sequence == refsequence
|
||||
final int runLen = sOff - anchor; |
||||
|
||||
// encode literal length
|
||||
int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, RUN_MASK << ML_BITS); |
||||
dOff = LZ4ByteBufferUtils.writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, runLen << ML_BITS); |
||||
} |
||||
|
||||
// copy literals
|
||||
LZ4ByteBufferUtils.wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
while (true) { |
||||
// encode offset
|
||||
ByteBufferUtils.writeShortLE(dest, dOff, (short) (sOff - ref)); |
||||
dOff += 2; |
||||
|
||||
// count nb matches
|
||||
sOff += MIN_MATCH; |
||||
ref += MIN_MATCH; |
||||
final int matchLen = LZ4ByteBufferUtils.commonBytes(src, ref, sOff, srcLimit); |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
sOff += matchLen; |
||||
|
||||
// encode match len
|
||||
if (matchLen >= ML_MASK) { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, ByteBufferUtils.readByte(dest, tokenOff) | ML_MASK); |
||||
dOff = LZ4ByteBufferUtils.writeLen(matchLen - ML_MASK, dest, dOff); |
||||
} else { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, ByteBufferUtils.readByte(dest, tokenOff) | matchLen); |
||||
} |
||||
|
||||
// test end of chunk
|
||||
if (sOff > mflimit) { |
||||
anchor = sOff; |
||||
break main; |
||||
} |
||||
|
||||
// fill table
|
||||
SafeUtils.writeShort(hashTable, hash64k(ByteBufferUtils.readInt(src, sOff - 2)), sOff - 2 - srcOff); |
||||
|
||||
// test next position
|
||||
final int h = hash64k(ByteBufferUtils.readInt(src, sOff)); |
||||
ref = srcOff + SafeUtils.readShort(hashTable, h); |
||||
SafeUtils.writeShort(hashTable, h, sOff - srcOff); |
||||
|
||||
if (!LZ4ByteBufferUtils.readIntEquals(src, sOff, ref)) { |
||||
break; |
||||
} |
||||
|
||||
tokenOff = dOff++; |
||||
ByteBufferUtils.writeByte(dest, tokenOff, 0); |
||||
} |
||||
|
||||
// prepare next loop
|
||||
anchor = sOff++; |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
@Override |
||||
public int compress(ByteBuffer src, final int srcOff, int srcLen, ByteBuffer dest, final int destOff, int maxDestLen) { |
||||
|
||||
if (src.hasArray() && dest.hasArray()) { |
||||
return compress(src.array(), srcOff + src.arrayOffset(), srcLen, dest.array(), destOff + dest.arrayOffset(), maxDestLen); |
||||
} |
||||
src = ByteBufferUtils.inNativeByteOrder(src); |
||||
dest = ByteBufferUtils.inNativeByteOrder(dest); |
||||
|
||||
ByteBufferUtils.checkRange(src, srcOff, srcLen); |
||||
ByteBufferUtils.checkRange(dest, destOff, maxDestLen); |
||||
final int destEnd = destOff + maxDestLen; |
||||
|
||||
if (srcLen < LZ4_64K_LIMIT) { |
||||
return compress64k(src, srcOff, srcLen, dest, destOff, destEnd); |
||||
} |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
final int srcLimit = srcEnd - LAST_LITERALS; |
||||
final int mflimit = srcEnd - MF_LIMIT; |
||||
|
||||
int sOff = srcOff, dOff = destOff; |
||||
int anchor = sOff++; |
||||
|
||||
final int[] hashTable = new int[HASH_TABLE_SIZE]; |
||||
Arrays.fill(hashTable, anchor); |
||||
|
||||
main: |
||||
while (true) { |
||||
|
||||
// find a match
|
||||
int forwardOff = sOff; |
||||
|
||||
int ref; |
||||
int step = 1; |
||||
int searchMatchNb = 1 << SKIP_STRENGTH; |
||||
int back; |
||||
do { |
||||
sOff = forwardOff; |
||||
forwardOff += step; |
||||
step = searchMatchNb++ >>> SKIP_STRENGTH; |
||||
|
||||
if (forwardOff > mflimit) { |
||||
break main; |
||||
} |
||||
|
||||
final int h = hash(ByteBufferUtils.readInt(src, sOff)); |
||||
ref = SafeUtils.readInt(hashTable, h); |
||||
back = sOff - ref; |
||||
SafeUtils.writeInt(hashTable, h, sOff); |
||||
} while (back >= MAX_DISTANCE || !LZ4ByteBufferUtils.readIntEquals(src, ref, sOff)); |
||||
|
||||
|
||||
final int excess = LZ4ByteBufferUtils.commonBytesBackward(src, ref, sOff, srcOff, anchor); |
||||
sOff -= excess; |
||||
ref -= excess; |
||||
|
||||
// sequence == refsequence
|
||||
final int runLen = sOff - anchor; |
||||
|
||||
// encode literal length
|
||||
int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, RUN_MASK << ML_BITS); |
||||
dOff = LZ4ByteBufferUtils.writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, runLen << ML_BITS); |
||||
} |
||||
|
||||
// copy literals
|
||||
LZ4ByteBufferUtils.wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
while (true) { |
||||
// encode offset
|
||||
ByteBufferUtils.writeShortLE(dest, dOff, back); |
||||
dOff += 2; |
||||
|
||||
// count nb matches
|
||||
sOff += MIN_MATCH; |
||||
final int matchLen = LZ4ByteBufferUtils.commonBytes(src, ref + MIN_MATCH, sOff, srcLimit); |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
sOff += matchLen; |
||||
|
||||
// encode match len
|
||||
if (matchLen >= ML_MASK) { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, ByteBufferUtils.readByte(dest, tokenOff) | ML_MASK); |
||||
dOff = LZ4ByteBufferUtils.writeLen(matchLen - ML_MASK, dest, dOff); |
||||
} else { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, ByteBufferUtils.readByte(dest, tokenOff) | matchLen); |
||||
} |
||||
|
||||
// test end of chunk
|
||||
if (sOff > mflimit) { |
||||
anchor = sOff; |
||||
break main; |
||||
} |
||||
|
||||
// fill table
|
||||
SafeUtils.writeInt(hashTable, hash(ByteBufferUtils.readInt(src, sOff - 2)), sOff - 2); |
||||
|
||||
// test next position
|
||||
final int h = hash(ByteBufferUtils.readInt(src, sOff)); |
||||
ref = SafeUtils.readInt(hashTable, h); |
||||
SafeUtils.writeInt(hashTable, h, sOff); |
||||
back = sOff - ref; |
||||
|
||||
if (back >= MAX_DISTANCE || !LZ4ByteBufferUtils.readIntEquals(src, ref, sOff)) { |
||||
break; |
||||
} |
||||
|
||||
tokenOff = dOff++; |
||||
ByteBufferUtils.writeByte(dest, tokenOff, 0); |
||||
} |
||||
|
||||
// prepare next loop
|
||||
anchor = sOff++; |
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,205 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.*; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
/** |
||||
* Decompressor. |
||||
*/ |
||||
final class LZ4JavaSafeFastDecompressor extends LZ4FastDecompressor { |
||||
|
||||
public static final LZ4FastDecompressor INSTANCE = new LZ4JavaSafeFastDecompressor(); |
||||
|
||||
@Override |
||||
public int decompress(byte[] src, final int srcOff, byte[] dest, final int destOff, int destLen) { |
||||
|
||||
|
||||
SafeUtils.checkRange(src, srcOff); |
||||
SafeUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if (destLen == 0) { |
||||
if (SafeUtils.readByte(src, srcOff) != 0) { |
||||
throw new LZ4Exception("Malformed input at " + srcOff); |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
|
||||
final int destEnd = destOff + destLen; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
|
||||
while (true) { |
||||
final int token = SafeUtils.readByte(src, sOff) & 0xFF; |
||||
++sOff; |
||||
|
||||
// literals
|
||||
int literalLen = token >>> ML_BITS; |
||||
if (literalLen == RUN_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while ((len = SafeUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
literalLen += 0xFF; |
||||
} |
||||
literalLen += len & 0xFF; |
||||
} |
||||
|
||||
final int literalCopyEnd = dOff + literalLen; |
||||
|
||||
if (literalCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (literalCopyEnd != destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
|
||||
} else { |
||||
LZ4SafeUtils.safeArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
break; // EOF
|
||||
} |
||||
} |
||||
|
||||
LZ4SafeUtils.wildArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
|
||||
// matchs
|
||||
final int matchDec = SafeUtils.readShortLE(src, sOff); |
||||
sOff += 2; |
||||
int matchOff = dOff - matchDec; |
||||
|
||||
if (matchOff < destOff) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
|
||||
int matchLen = token & ML_MASK; |
||||
if (matchLen == ML_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while ((len = SafeUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
matchLen += 0xFF; |
||||
} |
||||
matchLen += len & 0xFF; |
||||
} |
||||
matchLen += MIN_MATCH; |
||||
|
||||
final int matchCopyEnd = dOff + matchLen; |
||||
|
||||
if (matchCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (matchCopyEnd > destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
LZ4SafeUtils.safeIncrementalCopy(dest, matchOff, dOff, matchLen); |
||||
} else { |
||||
LZ4SafeUtils.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); |
||||
} |
||||
dOff = matchCopyEnd; |
||||
} |
||||
|
||||
|
||||
return sOff - srcOff; |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int decompress(ByteBuffer src, final int srcOff, ByteBuffer dest, final int destOff, int destLen) { |
||||
|
||||
if (src.hasArray() && dest.hasArray()) { |
||||
return decompress(src.array(), srcOff + src.arrayOffset(), dest.array(), destOff + dest.arrayOffset(), destLen); |
||||
} |
||||
src = ByteBufferUtils.inNativeByteOrder(src); |
||||
dest = ByteBufferUtils.inNativeByteOrder(dest); |
||||
|
||||
|
||||
ByteBufferUtils.checkRange(src, srcOff); |
||||
ByteBufferUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if (destLen == 0) { |
||||
if (ByteBufferUtils.readByte(src, srcOff) != 0) { |
||||
throw new LZ4Exception("Malformed input at " + srcOff); |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
|
||||
final int destEnd = destOff + destLen; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
|
||||
while (true) { |
||||
final int token = ByteBufferUtils.readByte(src, sOff) & 0xFF; |
||||
++sOff; |
||||
|
||||
// literals
|
||||
int literalLen = token >>> ML_BITS; |
||||
if (literalLen == RUN_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while ((len = ByteBufferUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
literalLen += 0xFF; |
||||
} |
||||
literalLen += len & 0xFF; |
||||
} |
||||
|
||||
final int literalCopyEnd = dOff + literalLen; |
||||
|
||||
if (literalCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (literalCopyEnd != destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
|
||||
} else { |
||||
LZ4ByteBufferUtils.safeArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
break; // EOF
|
||||
} |
||||
} |
||||
|
||||
LZ4ByteBufferUtils.wildArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
|
||||
// matchs
|
||||
final int matchDec = ByteBufferUtils.readShortLE(src, sOff); |
||||
sOff += 2; |
||||
int matchOff = dOff - matchDec; |
||||
|
||||
if (matchOff < destOff) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
|
||||
int matchLen = token & ML_MASK; |
||||
if (matchLen == ML_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while ((len = ByteBufferUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
matchLen += 0xFF; |
||||
} |
||||
matchLen += len & 0xFF; |
||||
} |
||||
matchLen += MIN_MATCH; |
||||
|
||||
final int matchCopyEnd = dOff + matchLen; |
||||
|
||||
if (matchCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (matchCopyEnd > destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
LZ4ByteBufferUtils.safeIncrementalCopy(dest, matchOff, dOff, matchLen); |
||||
} else { |
||||
LZ4ByteBufferUtils.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); |
||||
} |
||||
dOff = matchCopyEnd; |
||||
} |
||||
|
||||
|
||||
return sOff - srcOff; |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,213 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.*; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
/** |
||||
* Decompressor. |
||||
*/ |
||||
final class LZ4JavaSafeSafeDecompressor extends LZ4SafeDecompressor { |
||||
|
||||
public static final LZ4SafeDecompressor INSTANCE = new LZ4JavaSafeSafeDecompressor(); |
||||
|
||||
@Override |
||||
public int decompress(byte[] src, final int srcOff, final int srcLen , byte[] dest, final int destOff, int destLen) { |
||||
|
||||
|
||||
SafeUtils.checkRange(src, srcOff, srcLen); |
||||
SafeUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if (destLen == 0) { |
||||
if (srcLen != 1 || SafeUtils.readByte(src, srcOff) != 0) { |
||||
throw new LZ4Exception("Output buffer too small"); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
|
||||
|
||||
final int destEnd = destOff + destLen; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
|
||||
while (true) { |
||||
final int token = SafeUtils.readByte(src, sOff) & 0xFF; |
||||
++sOff; |
||||
|
||||
// literals
|
||||
int literalLen = token >>> ML_BITS; |
||||
if (literalLen == RUN_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while (sOff < srcEnd &&(len = SafeUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
literalLen += 0xFF; |
||||
} |
||||
literalLen += len & 0xFF; |
||||
} |
||||
|
||||
final int literalCopyEnd = dOff + literalLen; |
||||
|
||||
if (literalCopyEnd > destEnd - COPY_LENGTH || sOff + literalLen > srcEnd - COPY_LENGTH) { |
||||
if (literalCopyEnd > destEnd) { |
||||
throw new LZ4Exception(); |
||||
} else if (sOff + literalLen != srcEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
|
||||
} else { |
||||
LZ4SafeUtils.safeArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
break; // EOF
|
||||
} |
||||
} |
||||
|
||||
LZ4SafeUtils.wildArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
|
||||
// matchs
|
||||
final int matchDec = SafeUtils.readShortLE(src, sOff); |
||||
sOff += 2; |
||||
int matchOff = dOff - matchDec; |
||||
|
||||
if (matchOff < destOff) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
|
||||
int matchLen = token & ML_MASK; |
||||
if (matchLen == ML_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while (sOff < srcEnd &&(len = SafeUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
matchLen += 0xFF; |
||||
} |
||||
matchLen += len & 0xFF; |
||||
} |
||||
matchLen += MIN_MATCH; |
||||
|
||||
final int matchCopyEnd = dOff + matchLen; |
||||
|
||||
if (matchCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (matchCopyEnd > destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
LZ4SafeUtils.safeIncrementalCopy(dest, matchOff, dOff, matchLen); |
||||
} else { |
||||
LZ4SafeUtils.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); |
||||
} |
||||
dOff = matchCopyEnd; |
||||
} |
||||
|
||||
|
||||
return dOff - destOff; |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int decompress(ByteBuffer src, final int srcOff, final int srcLen , ByteBuffer dest, final int destOff, int destLen) { |
||||
|
||||
if (src.hasArray() && dest.hasArray()) { |
||||
return decompress(src.array(), srcOff + src.arrayOffset(), srcLen, dest.array(), destOff + dest.arrayOffset(), destLen); |
||||
} |
||||
src = ByteBufferUtils.inNativeByteOrder(src); |
||||
dest = ByteBufferUtils.inNativeByteOrder(dest); |
||||
|
||||
|
||||
ByteBufferUtils.checkRange(src, srcOff, srcLen); |
||||
ByteBufferUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if (destLen == 0) { |
||||
if (srcLen != 1 || ByteBufferUtils.readByte(src, srcOff) != 0) { |
||||
throw new LZ4Exception("Output buffer too small"); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
|
||||
|
||||
final int destEnd = destOff + destLen; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
|
||||
while (true) { |
||||
final int token = ByteBufferUtils.readByte(src, sOff) & 0xFF; |
||||
++sOff; |
||||
|
||||
// literals
|
||||
int literalLen = token >>> ML_BITS; |
||||
if (literalLen == RUN_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while (sOff < srcEnd &&(len = ByteBufferUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
literalLen += 0xFF; |
||||
} |
||||
literalLen += len & 0xFF; |
||||
} |
||||
|
||||
final int literalCopyEnd = dOff + literalLen; |
||||
|
||||
if (literalCopyEnd > destEnd - COPY_LENGTH || sOff + literalLen > srcEnd - COPY_LENGTH) { |
||||
if (literalCopyEnd > destEnd) { |
||||
throw new LZ4Exception(); |
||||
} else if (sOff + literalLen != srcEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
|
||||
} else { |
||||
LZ4ByteBufferUtils.safeArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
break; // EOF
|
||||
} |
||||
} |
||||
|
||||
LZ4ByteBufferUtils.wildArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
|
||||
// matchs
|
||||
final int matchDec = ByteBufferUtils.readShortLE(src, sOff); |
||||
sOff += 2; |
||||
int matchOff = dOff - matchDec; |
||||
|
||||
if (matchOff < destOff) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
|
||||
int matchLen = token & ML_MASK; |
||||
if (matchLen == ML_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while (sOff < srcEnd &&(len = ByteBufferUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
matchLen += 0xFF; |
||||
} |
||||
matchLen += len & 0xFF; |
||||
} |
||||
matchLen += MIN_MATCH; |
||||
|
||||
final int matchCopyEnd = dOff + matchLen; |
||||
|
||||
if (matchCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (matchCopyEnd > destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
LZ4ByteBufferUtils.safeIncrementalCopy(dest, matchOff, dOff, matchLen); |
||||
} else { |
||||
LZ4ByteBufferUtils.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); |
||||
} |
||||
dOff = matchCopyEnd; |
||||
} |
||||
|
||||
|
||||
return dOff - destOff; |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,511 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.*; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Utils.*; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.util.Arrays; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.UnsafeUtils; |
||||
|
||||
/** |
||||
* Compressor. |
||||
*/ |
||||
final class LZ4JavaUnsafeCompressor extends LZ4Compressor { |
||||
|
||||
public static final LZ4Compressor INSTANCE = new LZ4JavaUnsafeCompressor(); |
||||
|
||||
static int compress64k(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int destEnd) { |
||||
final int srcEnd = srcOff + srcLen; |
||||
final int srcLimit = srcEnd - LAST_LITERALS; |
||||
final int mflimit = srcEnd - MF_LIMIT; |
||||
|
||||
int sOff = srcOff, dOff = destOff; |
||||
|
||||
int anchor = sOff; |
||||
|
||||
if (srcLen >= MIN_LENGTH) { |
||||
|
||||
final short[] hashTable = new short[HASH_TABLE_SIZE_64K]; |
||||
|
||||
++sOff; |
||||
|
||||
main: |
||||
while (true) { |
||||
|
||||
// find a match
|
||||
int forwardOff = sOff; |
||||
|
||||
int ref; |
||||
int step = 1; |
||||
int searchMatchNb = 1 << SKIP_STRENGTH; |
||||
do { |
||||
sOff = forwardOff; |
||||
forwardOff += step; |
||||
step = searchMatchNb++ >>> SKIP_STRENGTH; |
||||
|
||||
if (forwardOff > mflimit) { |
||||
break main; |
||||
} |
||||
|
||||
final int h = hash64k(UnsafeUtils.readInt(src, sOff)); |
||||
ref = srcOff + UnsafeUtils.readShort(hashTable, h); |
||||
UnsafeUtils.writeShort(hashTable, h, sOff - srcOff); |
||||
} while (!LZ4UnsafeUtils.readIntEquals(src, ref, sOff)); |
||||
|
||||
// catch up
|
||||
final int excess = LZ4UnsafeUtils.commonBytesBackward(src, ref, sOff, srcOff, anchor); |
||||
sOff -= excess; |
||||
ref -= excess; |
||||
|
||||
// sequence == refsequence
|
||||
final int runLen = sOff - anchor; |
||||
|
||||
// encode literal length
|
||||
int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
UnsafeUtils.writeByte(dest, tokenOff, RUN_MASK << ML_BITS); |
||||
dOff = LZ4UnsafeUtils.writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
UnsafeUtils.writeByte(dest, tokenOff, runLen << ML_BITS); |
||||
} |
||||
|
||||
// copy literals
|
||||
LZ4UnsafeUtils.wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
while (true) { |
||||
// encode offset
|
||||
UnsafeUtils.writeShortLE(dest, dOff, (short) (sOff - ref)); |
||||
dOff += 2; |
||||
|
||||
// count nb matches
|
||||
sOff += MIN_MATCH; |
||||
ref += MIN_MATCH; |
||||
final int matchLen = LZ4UnsafeUtils.commonBytes(src, ref, sOff, srcLimit); |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
sOff += matchLen; |
||||
|
||||
// encode match len
|
||||
if (matchLen >= ML_MASK) { |
||||
UnsafeUtils.writeByte(dest, tokenOff, UnsafeUtils.readByte(dest, tokenOff) | ML_MASK); |
||||
dOff = LZ4UnsafeUtils.writeLen(matchLen - ML_MASK, dest, dOff); |
||||
} else { |
||||
UnsafeUtils.writeByte(dest, tokenOff, UnsafeUtils.readByte(dest, tokenOff) | matchLen); |
||||
} |
||||
|
||||
// test end of chunk
|
||||
if (sOff > mflimit) { |
||||
anchor = sOff; |
||||
break main; |
||||
} |
||||
|
||||
// fill table
|
||||
UnsafeUtils.writeShort(hashTable, hash64k(UnsafeUtils.readInt(src, sOff - 2)), sOff - 2 - srcOff); |
||||
|
||||
// test next position
|
||||
final int h = hash64k(UnsafeUtils.readInt(src, sOff)); |
||||
ref = srcOff + UnsafeUtils.readShort(hashTable, h); |
||||
UnsafeUtils.writeShort(hashTable, h, sOff - srcOff); |
||||
|
||||
if (!LZ4UnsafeUtils.readIntEquals(src, sOff, ref)) { |
||||
break; |
||||
} |
||||
|
||||
tokenOff = dOff++; |
||||
UnsafeUtils.writeByte(dest, tokenOff, 0); |
||||
} |
||||
|
||||
// prepare next loop
|
||||
anchor = sOff++; |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4UnsafeUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
@Override |
||||
public int compress(byte[] src, final int srcOff, int srcLen, byte[] dest, final int destOff, int maxDestLen) { |
||||
|
||||
UnsafeUtils.checkRange(src, srcOff, srcLen); |
||||
UnsafeUtils.checkRange(dest, destOff, maxDestLen); |
||||
final int destEnd = destOff + maxDestLen; |
||||
|
||||
if (srcLen < LZ4_64K_LIMIT) { |
||||
return compress64k(src, srcOff, srcLen, dest, destOff, destEnd); |
||||
} |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
final int srcLimit = srcEnd - LAST_LITERALS; |
||||
final int mflimit = srcEnd - MF_LIMIT; |
||||
|
||||
int sOff = srcOff, dOff = destOff; |
||||
int anchor = sOff++; |
||||
|
||||
final int[] hashTable = new int[HASH_TABLE_SIZE]; |
||||
Arrays.fill(hashTable, anchor); |
||||
|
||||
main: |
||||
while (true) { |
||||
|
||||
// find a match
|
||||
int forwardOff = sOff; |
||||
|
||||
int ref; |
||||
int step = 1; |
||||
int searchMatchNb = 1 << SKIP_STRENGTH; |
||||
int back; |
||||
do { |
||||
sOff = forwardOff; |
||||
forwardOff += step; |
||||
step = searchMatchNb++ >>> SKIP_STRENGTH; |
||||
|
||||
if (forwardOff > mflimit) { |
||||
break main; |
||||
} |
||||
|
||||
final int h = hash(UnsafeUtils.readInt(src, sOff)); |
||||
ref = UnsafeUtils.readInt(hashTable, h); |
||||
back = sOff - ref; |
||||
UnsafeUtils.writeInt(hashTable, h, sOff); |
||||
} while (back >= MAX_DISTANCE || !LZ4UnsafeUtils.readIntEquals(src, ref, sOff)); |
||||
|
||||
|
||||
final int excess = LZ4UnsafeUtils.commonBytesBackward(src, ref, sOff, srcOff, anchor); |
||||
sOff -= excess; |
||||
ref -= excess; |
||||
|
||||
// sequence == refsequence
|
||||
final int runLen = sOff - anchor; |
||||
|
||||
// encode literal length
|
||||
int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
UnsafeUtils.writeByte(dest, tokenOff, RUN_MASK << ML_BITS); |
||||
dOff = LZ4UnsafeUtils.writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
UnsafeUtils.writeByte(dest, tokenOff, runLen << ML_BITS); |
||||
} |
||||
|
||||
// copy literals
|
||||
LZ4UnsafeUtils.wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
while (true) { |
||||
// encode offset
|
||||
UnsafeUtils.writeShortLE(dest, dOff, back); |
||||
dOff += 2; |
||||
|
||||
// count nb matches
|
||||
sOff += MIN_MATCH; |
||||
final int matchLen = LZ4UnsafeUtils.commonBytes(src, ref + MIN_MATCH, sOff, srcLimit); |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
sOff += matchLen; |
||||
|
||||
// encode match len
|
||||
if (matchLen >= ML_MASK) { |
||||
UnsafeUtils.writeByte(dest, tokenOff, UnsafeUtils.readByte(dest, tokenOff) | ML_MASK); |
||||
dOff = LZ4UnsafeUtils.writeLen(matchLen - ML_MASK, dest, dOff); |
||||
} else { |
||||
UnsafeUtils.writeByte(dest, tokenOff, UnsafeUtils.readByte(dest, tokenOff) | matchLen); |
||||
} |
||||
|
||||
// test end of chunk
|
||||
if (sOff > mflimit) { |
||||
anchor = sOff; |
||||
break main; |
||||
} |
||||
|
||||
// fill table
|
||||
UnsafeUtils.writeInt(hashTable, hash(UnsafeUtils.readInt(src, sOff - 2)), sOff - 2); |
||||
|
||||
// test next position
|
||||
final int h = hash(UnsafeUtils.readInt(src, sOff)); |
||||
ref = UnsafeUtils.readInt(hashTable, h); |
||||
UnsafeUtils.writeInt(hashTable, h, sOff); |
||||
back = sOff - ref; |
||||
|
||||
if (back >= MAX_DISTANCE || !LZ4UnsafeUtils.readIntEquals(src, ref, sOff)) { |
||||
break; |
||||
} |
||||
|
||||
tokenOff = dOff++; |
||||
UnsafeUtils.writeByte(dest, tokenOff, 0); |
||||
} |
||||
|
||||
// prepare next loop
|
||||
anchor = sOff++; |
||||
} |
||||
|
||||
dOff = LZ4UnsafeUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
|
||||
static int compress64k(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int destEnd) { |
||||
final int srcEnd = srcOff + srcLen; |
||||
final int srcLimit = srcEnd - LAST_LITERALS; |
||||
final int mflimit = srcEnd - MF_LIMIT; |
||||
|
||||
int sOff = srcOff, dOff = destOff; |
||||
|
||||
int anchor = sOff; |
||||
|
||||
if (srcLen >= MIN_LENGTH) { |
||||
|
||||
final short[] hashTable = new short[HASH_TABLE_SIZE_64K]; |
||||
|
||||
++sOff; |
||||
|
||||
main: |
||||
while (true) { |
||||
|
||||
// find a match
|
||||
int forwardOff = sOff; |
||||
|
||||
int ref; |
||||
int step = 1; |
||||
int searchMatchNb = 1 << SKIP_STRENGTH; |
||||
do { |
||||
sOff = forwardOff; |
||||
forwardOff += step; |
||||
step = searchMatchNb++ >>> SKIP_STRENGTH; |
||||
|
||||
if (forwardOff > mflimit) { |
||||
break main; |
||||
} |
||||
|
||||
final int h = hash64k(ByteBufferUtils.readInt(src, sOff)); |
||||
ref = srcOff + UnsafeUtils.readShort(hashTable, h); |
||||
UnsafeUtils.writeShort(hashTable, h, sOff - srcOff); |
||||
} while (!LZ4ByteBufferUtils.readIntEquals(src, ref, sOff)); |
||||
|
||||
// catch up
|
||||
final int excess = LZ4ByteBufferUtils.commonBytesBackward(src, ref, sOff, srcOff, anchor); |
||||
sOff -= excess; |
||||
ref -= excess; |
||||
|
||||
// sequence == refsequence
|
||||
final int runLen = sOff - anchor; |
||||
|
||||
// encode literal length
|
||||
int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, RUN_MASK << ML_BITS); |
||||
dOff = LZ4ByteBufferUtils.writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, runLen << ML_BITS); |
||||
} |
||||
|
||||
// copy literals
|
||||
LZ4ByteBufferUtils.wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
while (true) { |
||||
// encode offset
|
||||
ByteBufferUtils.writeShortLE(dest, dOff, (short) (sOff - ref)); |
||||
dOff += 2; |
||||
|
||||
// count nb matches
|
||||
sOff += MIN_MATCH; |
||||
ref += MIN_MATCH; |
||||
final int matchLen = LZ4ByteBufferUtils.commonBytes(src, ref, sOff, srcLimit); |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
sOff += matchLen; |
||||
|
||||
// encode match len
|
||||
if (matchLen >= ML_MASK) { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, ByteBufferUtils.readByte(dest, tokenOff) | ML_MASK); |
||||
dOff = LZ4ByteBufferUtils.writeLen(matchLen - ML_MASK, dest, dOff); |
||||
} else { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, ByteBufferUtils.readByte(dest, tokenOff) | matchLen); |
||||
} |
||||
|
||||
// test end of chunk
|
||||
if (sOff > mflimit) { |
||||
anchor = sOff; |
||||
break main; |
||||
} |
||||
|
||||
// fill table
|
||||
UnsafeUtils.writeShort(hashTable, hash64k(ByteBufferUtils.readInt(src, sOff - 2)), sOff - 2 - srcOff); |
||||
|
||||
// test next position
|
||||
final int h = hash64k(ByteBufferUtils.readInt(src, sOff)); |
||||
ref = srcOff + UnsafeUtils.readShort(hashTable, h); |
||||
UnsafeUtils.writeShort(hashTable, h, sOff - srcOff); |
||||
|
||||
if (!LZ4ByteBufferUtils.readIntEquals(src, sOff, ref)) { |
||||
break; |
||||
} |
||||
|
||||
tokenOff = dOff++; |
||||
ByteBufferUtils.writeByte(dest, tokenOff, 0); |
||||
} |
||||
|
||||
// prepare next loop
|
||||
anchor = sOff++; |
||||
} |
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
@Override |
||||
public int compress(ByteBuffer src, final int srcOff, int srcLen, ByteBuffer dest, final int destOff, int maxDestLen) { |
||||
|
||||
if (src.hasArray() && dest.hasArray()) { |
||||
return compress(src.array(), srcOff + src.arrayOffset(), srcLen, dest.array(), destOff + dest.arrayOffset(), maxDestLen); |
||||
} |
||||
src = ByteBufferUtils.inNativeByteOrder(src); |
||||
dest = ByteBufferUtils.inNativeByteOrder(dest); |
||||
|
||||
ByteBufferUtils.checkRange(src, srcOff, srcLen); |
||||
ByteBufferUtils.checkRange(dest, destOff, maxDestLen); |
||||
final int destEnd = destOff + maxDestLen; |
||||
|
||||
if (srcLen < LZ4_64K_LIMIT) { |
||||
return compress64k(src, srcOff, srcLen, dest, destOff, destEnd); |
||||
} |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
final int srcLimit = srcEnd - LAST_LITERALS; |
||||
final int mflimit = srcEnd - MF_LIMIT; |
||||
|
||||
int sOff = srcOff, dOff = destOff; |
||||
int anchor = sOff++; |
||||
|
||||
final int[] hashTable = new int[HASH_TABLE_SIZE]; |
||||
Arrays.fill(hashTable, anchor); |
||||
|
||||
main: |
||||
while (true) { |
||||
|
||||
// find a match
|
||||
int forwardOff = sOff; |
||||
|
||||
int ref; |
||||
int step = 1; |
||||
int searchMatchNb = 1 << SKIP_STRENGTH; |
||||
int back; |
||||
do { |
||||
sOff = forwardOff; |
||||
forwardOff += step; |
||||
step = searchMatchNb++ >>> SKIP_STRENGTH; |
||||
|
||||
if (forwardOff > mflimit) { |
||||
break main; |
||||
} |
||||
|
||||
final int h = hash(ByteBufferUtils.readInt(src, sOff)); |
||||
ref = UnsafeUtils.readInt(hashTable, h); |
||||
back = sOff - ref; |
||||
UnsafeUtils.writeInt(hashTable, h, sOff); |
||||
} while (back >= MAX_DISTANCE || !LZ4ByteBufferUtils.readIntEquals(src, ref, sOff)); |
||||
|
||||
|
||||
final int excess = LZ4ByteBufferUtils.commonBytesBackward(src, ref, sOff, srcOff, anchor); |
||||
sOff -= excess; |
||||
ref -= excess; |
||||
|
||||
// sequence == refsequence
|
||||
final int runLen = sOff - anchor; |
||||
|
||||
// encode literal length
|
||||
int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, RUN_MASK << ML_BITS); |
||||
dOff = LZ4ByteBufferUtils.writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, runLen << ML_BITS); |
||||
} |
||||
|
||||
// copy literals
|
||||
LZ4ByteBufferUtils.wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
while (true) { |
||||
// encode offset
|
||||
ByteBufferUtils.writeShortLE(dest, dOff, back); |
||||
dOff += 2; |
||||
|
||||
// count nb matches
|
||||
sOff += MIN_MATCH; |
||||
final int matchLen = LZ4ByteBufferUtils.commonBytes(src, ref + MIN_MATCH, sOff, srcLimit); |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
sOff += matchLen; |
||||
|
||||
// encode match len
|
||||
if (matchLen >= ML_MASK) { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, ByteBufferUtils.readByte(dest, tokenOff) | ML_MASK); |
||||
dOff = LZ4ByteBufferUtils.writeLen(matchLen - ML_MASK, dest, dOff); |
||||
} else { |
||||
ByteBufferUtils.writeByte(dest, tokenOff, ByteBufferUtils.readByte(dest, tokenOff) | matchLen); |
||||
} |
||||
|
||||
// test end of chunk
|
||||
if (sOff > mflimit) { |
||||
anchor = sOff; |
||||
break main; |
||||
} |
||||
|
||||
// fill table
|
||||
UnsafeUtils.writeInt(hashTable, hash(ByteBufferUtils.readInt(src, sOff - 2)), sOff - 2); |
||||
|
||||
// test next position
|
||||
final int h = hash(ByteBufferUtils.readInt(src, sOff)); |
||||
ref = UnsafeUtils.readInt(hashTable, h); |
||||
UnsafeUtils.writeInt(hashTable, h, sOff); |
||||
back = sOff - ref; |
||||
|
||||
if (back >= MAX_DISTANCE || !LZ4ByteBufferUtils.readIntEquals(src, ref, sOff)) { |
||||
break; |
||||
} |
||||
|
||||
tokenOff = dOff++; |
||||
ByteBufferUtils.writeByte(dest, tokenOff, 0); |
||||
} |
||||
|
||||
// prepare next loop
|
||||
anchor = sOff++; |
||||
} |
||||
|
||||
dOff = LZ4ByteBufferUtils.lastLiterals(src, anchor, srcEnd - anchor, dest, dOff, destEnd); |
||||
return dOff - destOff; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,205 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.*; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.UnsafeUtils; |
||||
|
||||
/** |
||||
* Decompressor. |
||||
*/ |
||||
final class LZ4JavaUnsafeFastDecompressor extends LZ4FastDecompressor { |
||||
|
||||
public static final LZ4FastDecompressor INSTANCE = new LZ4JavaUnsafeFastDecompressor(); |
||||
|
||||
@Override |
||||
public int decompress(byte[] src, final int srcOff, byte[] dest, final int destOff, int destLen) { |
||||
|
||||
|
||||
UnsafeUtils.checkRange(src, srcOff); |
||||
UnsafeUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if (destLen == 0) { |
||||
if (UnsafeUtils.readByte(src, srcOff) != 0) { |
||||
throw new LZ4Exception("Malformed input at " + srcOff); |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
|
||||
final int destEnd = destOff + destLen; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
|
||||
while (true) { |
||||
final int token = UnsafeUtils.readByte(src, sOff) & 0xFF; |
||||
++sOff; |
||||
|
||||
// literals
|
||||
int literalLen = token >>> ML_BITS; |
||||
if (literalLen == RUN_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while ((len = UnsafeUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
literalLen += 0xFF; |
||||
} |
||||
literalLen += len & 0xFF; |
||||
} |
||||
|
||||
final int literalCopyEnd = dOff + literalLen; |
||||
|
||||
if (literalCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (literalCopyEnd != destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
|
||||
} else { |
||||
LZ4UnsafeUtils.safeArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
break; // EOF
|
||||
} |
||||
} |
||||
|
||||
LZ4UnsafeUtils.wildArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
|
||||
// matchs
|
||||
final int matchDec = UnsafeUtils.readShortLE(src, sOff); |
||||
sOff += 2; |
||||
int matchOff = dOff - matchDec; |
||||
|
||||
if (matchOff < destOff) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
|
||||
int matchLen = token & ML_MASK; |
||||
if (matchLen == ML_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while ((len = UnsafeUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
matchLen += 0xFF; |
||||
} |
||||
matchLen += len & 0xFF; |
||||
} |
||||
matchLen += MIN_MATCH; |
||||
|
||||
final int matchCopyEnd = dOff + matchLen; |
||||
|
||||
if (matchCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (matchCopyEnd > destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
LZ4UnsafeUtils.safeIncrementalCopy(dest, matchOff, dOff, matchLen); |
||||
} else { |
||||
LZ4UnsafeUtils.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); |
||||
} |
||||
dOff = matchCopyEnd; |
||||
} |
||||
|
||||
|
||||
return sOff - srcOff; |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int decompress(ByteBuffer src, final int srcOff, ByteBuffer dest, final int destOff, int destLen) { |
||||
|
||||
if (src.hasArray() && dest.hasArray()) { |
||||
return decompress(src.array(), srcOff + src.arrayOffset(), dest.array(), destOff + dest.arrayOffset(), destLen); |
||||
} |
||||
src = ByteBufferUtils.inNativeByteOrder(src); |
||||
dest = ByteBufferUtils.inNativeByteOrder(dest); |
||||
|
||||
|
||||
ByteBufferUtils.checkRange(src, srcOff); |
||||
ByteBufferUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if (destLen == 0) { |
||||
if (ByteBufferUtils.readByte(src, srcOff) != 0) { |
||||
throw new LZ4Exception("Malformed input at " + srcOff); |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
|
||||
final int destEnd = destOff + destLen; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
|
||||
while (true) { |
||||
final int token = ByteBufferUtils.readByte(src, sOff) & 0xFF; |
||||
++sOff; |
||||
|
||||
// literals
|
||||
int literalLen = token >>> ML_BITS; |
||||
if (literalLen == RUN_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while ((len = ByteBufferUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
literalLen += 0xFF; |
||||
} |
||||
literalLen += len & 0xFF; |
||||
} |
||||
|
||||
final int literalCopyEnd = dOff + literalLen; |
||||
|
||||
if (literalCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (literalCopyEnd != destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
|
||||
} else { |
||||
LZ4ByteBufferUtils.safeArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
break; // EOF
|
||||
} |
||||
} |
||||
|
||||
LZ4ByteBufferUtils.wildArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
|
||||
// matchs
|
||||
final int matchDec = ByteBufferUtils.readShortLE(src, sOff); |
||||
sOff += 2; |
||||
int matchOff = dOff - matchDec; |
||||
|
||||
if (matchOff < destOff) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
|
||||
int matchLen = token & ML_MASK; |
||||
if (matchLen == ML_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while ((len = ByteBufferUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
matchLen += 0xFF; |
||||
} |
||||
matchLen += len & 0xFF; |
||||
} |
||||
matchLen += MIN_MATCH; |
||||
|
||||
final int matchCopyEnd = dOff + matchLen; |
||||
|
||||
if (matchCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (matchCopyEnd > destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
LZ4ByteBufferUtils.safeIncrementalCopy(dest, matchOff, dOff, matchLen); |
||||
} else { |
||||
LZ4ByteBufferUtils.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); |
||||
} |
||||
dOff = matchCopyEnd; |
||||
} |
||||
|
||||
|
||||
return sOff - srcOff; |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,213 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.*; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.UnsafeUtils; |
||||
|
||||
/** |
||||
* Decompressor. |
||||
*/ |
||||
final class LZ4JavaUnsafeSafeDecompressor extends LZ4SafeDecompressor { |
||||
|
||||
public static final LZ4SafeDecompressor INSTANCE = new LZ4JavaUnsafeSafeDecompressor(); |
||||
|
||||
@Override |
||||
public int decompress(byte[] src, final int srcOff, final int srcLen , byte[] dest, final int destOff, int destLen) { |
||||
|
||||
|
||||
UnsafeUtils.checkRange(src, srcOff, srcLen); |
||||
UnsafeUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if (destLen == 0) { |
||||
if (srcLen != 1 || UnsafeUtils.readByte(src, srcOff) != 0) { |
||||
throw new LZ4Exception("Output buffer too small"); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
|
||||
|
||||
final int destEnd = destOff + destLen; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
|
||||
while (true) { |
||||
final int token = UnsafeUtils.readByte(src, sOff) & 0xFF; |
||||
++sOff; |
||||
|
||||
// literals
|
||||
int literalLen = token >>> ML_BITS; |
||||
if (literalLen == RUN_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while (sOff < srcEnd &&(len = UnsafeUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
literalLen += 0xFF; |
||||
} |
||||
literalLen += len & 0xFF; |
||||
} |
||||
|
||||
final int literalCopyEnd = dOff + literalLen; |
||||
|
||||
if (literalCopyEnd > destEnd - COPY_LENGTH || sOff + literalLen > srcEnd - COPY_LENGTH) { |
||||
if (literalCopyEnd > destEnd) { |
||||
throw new LZ4Exception(); |
||||
} else if (sOff + literalLen != srcEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
|
||||
} else { |
||||
LZ4UnsafeUtils.safeArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
break; // EOF
|
||||
} |
||||
} |
||||
|
||||
LZ4UnsafeUtils.wildArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
|
||||
// matchs
|
||||
final int matchDec = UnsafeUtils.readShortLE(src, sOff); |
||||
sOff += 2; |
||||
int matchOff = dOff - matchDec; |
||||
|
||||
if (matchOff < destOff) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
|
||||
int matchLen = token & ML_MASK; |
||||
if (matchLen == ML_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while (sOff < srcEnd &&(len = UnsafeUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
matchLen += 0xFF; |
||||
} |
||||
matchLen += len & 0xFF; |
||||
} |
||||
matchLen += MIN_MATCH; |
||||
|
||||
final int matchCopyEnd = dOff + matchLen; |
||||
|
||||
if (matchCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (matchCopyEnd > destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
LZ4UnsafeUtils.safeIncrementalCopy(dest, matchOff, dOff, matchLen); |
||||
} else { |
||||
LZ4UnsafeUtils.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); |
||||
} |
||||
dOff = matchCopyEnd; |
||||
} |
||||
|
||||
|
||||
return dOff - destOff; |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int decompress(ByteBuffer src, final int srcOff, final int srcLen , ByteBuffer dest, final int destOff, int destLen) { |
||||
|
||||
if (src.hasArray() && dest.hasArray()) { |
||||
return decompress(src.array(), srcOff + src.arrayOffset(), srcLen, dest.array(), destOff + dest.arrayOffset(), destLen); |
||||
} |
||||
src = ByteBufferUtils.inNativeByteOrder(src); |
||||
dest = ByteBufferUtils.inNativeByteOrder(dest); |
||||
|
||||
|
||||
ByteBufferUtils.checkRange(src, srcOff, srcLen); |
||||
ByteBufferUtils.checkRange(dest, destOff, destLen); |
||||
|
||||
if (destLen == 0) { |
||||
if (srcLen != 1 || ByteBufferUtils.readByte(src, srcOff) != 0) { |
||||
throw new LZ4Exception("Output buffer too small"); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
final int srcEnd = srcOff + srcLen; |
||||
|
||||
|
||||
final int destEnd = destOff + destLen; |
||||
|
||||
int sOff = srcOff; |
||||
int dOff = destOff; |
||||
|
||||
while (true) { |
||||
final int token = ByteBufferUtils.readByte(src, sOff) & 0xFF; |
||||
++sOff; |
||||
|
||||
// literals
|
||||
int literalLen = token >>> ML_BITS; |
||||
if (literalLen == RUN_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while (sOff < srcEnd &&(len = ByteBufferUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
literalLen += 0xFF; |
||||
} |
||||
literalLen += len & 0xFF; |
||||
} |
||||
|
||||
final int literalCopyEnd = dOff + literalLen; |
||||
|
||||
if (literalCopyEnd > destEnd - COPY_LENGTH || sOff + literalLen > srcEnd - COPY_LENGTH) { |
||||
if (literalCopyEnd > destEnd) { |
||||
throw new LZ4Exception(); |
||||
} else if (sOff + literalLen != srcEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
|
||||
} else { |
||||
LZ4ByteBufferUtils.safeArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
break; // EOF
|
||||
} |
||||
} |
||||
|
||||
LZ4ByteBufferUtils.wildArraycopy(src, sOff, dest, dOff, literalLen); |
||||
sOff += literalLen; |
||||
dOff = literalCopyEnd; |
||||
|
||||
// matchs
|
||||
final int matchDec = ByteBufferUtils.readShortLE(src, sOff); |
||||
sOff += 2; |
||||
int matchOff = dOff - matchDec; |
||||
|
||||
if (matchOff < destOff) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
|
||||
int matchLen = token & ML_MASK; |
||||
if (matchLen == ML_MASK) { |
||||
byte len = (byte) 0xFF; |
||||
while (sOff < srcEnd &&(len = ByteBufferUtils.readByte(src, sOff++)) == (byte) 0xFF) { |
||||
matchLen += 0xFF; |
||||
} |
||||
matchLen += len & 0xFF; |
||||
} |
||||
matchLen += MIN_MATCH; |
||||
|
||||
final int matchCopyEnd = dOff + matchLen; |
||||
|
||||
if (matchCopyEnd > destEnd - COPY_LENGTH) { |
||||
if (matchCopyEnd > destEnd) { |
||||
throw new LZ4Exception("Malformed input at " + sOff); |
||||
} |
||||
LZ4ByteBufferUtils.safeIncrementalCopy(dest, matchOff, dOff, matchLen); |
||||
} else { |
||||
LZ4ByteBufferUtils.wildIncrementalCopy(dest, matchOff, dOff, matchCopyEnd); |
||||
} |
||||
dOff = matchCopyEnd; |
||||
} |
||||
|
||||
|
||||
return dOff - destOff; |
||||
|
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,155 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.util.Arrays; |
||||
|
||||
/** |
||||
* LZ4 decompressor that requires the size of the compressed data to be known. |
||||
* <p> |
||||
* Implementations of this class are usually a little slower than those of |
||||
* {@link LZ4FastDecompressor} but do not require the size of the original data to |
||||
* be known. |
||||
*/ |
||||
public abstract class LZ4SafeDecompressor implements LZ4UnknownSizeDecompressor { |
||||
|
||||
/** |
||||
* Decompresses <code>src[srcOff:srcOff+srcLen]</code> into |
||||
* <code>dest[destOff:destOff+maxDestLen]</code> and returns the number of |
||||
* decompressed bytes written into <code>dest</code>. |
||||
* |
||||
* @param src the compressed data |
||||
* @param srcOff the start offset in src |
||||
* @param srcLen the exact size of the compressed data |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @param destOff the start offset in dest |
||||
* @param maxDestLen the maximum number of bytes to write in dest |
||||
* @return the original input size |
||||
* @throws LZ4Exception if maxDestLen is too small |
||||
*/ |
||||
public abstract int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen); |
||||
|
||||
/** |
||||
* Decompresses <code>src[srcOff:srcOff+srcLen]</code> into |
||||
* <code>dest[destOff:destOff+maxDestLen]</code> and returns the number of |
||||
* decompressed bytes written into <code>dest</code>. |
||||
* |
||||
* @param src the compressed data |
||||
* @param srcOff the start offset in src |
||||
* @param srcLen the exact size of the compressed data |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @param destOff the start offset in dest |
||||
* @param maxDestLen the maximum number of bytes to write in dest |
||||
* @return the original input size |
||||
* @throws LZ4Exception if maxDestLen is too small |
||||
*/ |
||||
public abstract int decompress(ByteBuffer src, int srcOff, int srcLen, ByteBuffer dest, int destOff, int maxDestLen); |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #decompress(byte[], int, int, byte[], int, int) decompress(src, srcOff, srcLen, dest, destOff, dest.length - destOff)}. |
||||
* |
||||
* @param src the compressed data |
||||
* @param srcOff the start offset in src |
||||
* @param srcLen the exact size of the compressed data |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @param destOff the start offset in dest |
||||
* @return the original input size |
||||
* @throws LZ4Exception if dest is too small |
||||
*/ |
||||
public final int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff) { |
||||
return decompress(src, srcOff, srcLen, dest, destOff, dest.length - destOff); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #decompress(byte[], int, int, byte[], int) decompress(src, 0, src.length, dest, 0)} |
||||
* |
||||
* @param src the compressed data |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @return the original input size |
||||
* @throws LZ4Exception if dest is too small |
||||
*/ |
||||
public final int decompress(byte[] src, byte[] dest) { |
||||
return decompress(src, 0, src.length, dest, 0); |
||||
} |
||||
|
||||
/** |
||||
* Convenience method which returns <code>src[srcOff:srcOff+srcLen]</code> |
||||
* decompressed. |
||||
* <p><b><span style="color:red">Warning</span></b>: this method has an |
||||
* important overhead due to the fact that it needs to allocate a buffer to |
||||
* decompress into, and then needs to resize this buffer to the actual |
||||
* decompressed length.</p> |
||||
* <p>Here is how this method is implemented:</p> |
||||
* <pre> |
||||
* byte[] decompressed = new byte[maxDestLen]; |
||||
* final int decompressedLength = decompress(src, srcOff, srcLen, decompressed, 0, maxDestLen); |
||||
* if (decompressedLength != decompressed.length) { |
||||
* decompressed = Arrays.copyOf(decompressed, decompressedLength); |
||||
* } |
||||
* return decompressed; |
||||
* </pre> |
||||
* |
||||
* @param src the compressed data |
||||
* @param srcOff the start offset in src |
||||
* @param srcLen the exact size of the compressed data |
||||
* @param maxDestLen the maximum number of bytes to write in dest |
||||
* @return the decompressed data |
||||
* @throws LZ4Exception if maxDestLen is too small |
||||
*/ |
||||
public final byte[] decompress(byte[] src, int srcOff, int srcLen, int maxDestLen) { |
||||
byte[] decompressed = new byte[maxDestLen]; |
||||
final int decompressedLength = decompress(src, srcOff, srcLen, decompressed, 0, maxDestLen); |
||||
if (decompressedLength != decompressed.length) { |
||||
decompressed = Arrays.copyOf(decompressed, decompressedLength); |
||||
} |
||||
return decompressed; |
||||
} |
||||
|
||||
/** |
||||
* Convenience method, equivalent to calling |
||||
* {@link #decompress(byte[], int, int, int) decompress(src, 0, src.length, maxDestLen)}. |
||||
* |
||||
* @param src the compressed data |
||||
* @param maxDestLen the maximum number of bytes to write in dest |
||||
* @return the decompressed data |
||||
* @throws LZ4Exception if maxDestLen is too small |
||||
*/ |
||||
public final byte[] decompress(byte[] src, int maxDestLen) { |
||||
return decompress(src, 0, src.length, maxDestLen); |
||||
} |
||||
|
||||
/** |
||||
* Decompresses <code>src</code> into <code>dest</code>. <code>src</code>'s |
||||
* {@link ByteBuffer#remaining()} must be exactly the size of the compressed |
||||
* data. This method moves the positions of the buffers. |
||||
* @param src the compressed data |
||||
* @param dest the destination buffer to store the decompressed data |
||||
* @throws LZ4Exception if dest is too small |
||||
*/ |
||||
public final void decompress(ByteBuffer src, ByteBuffer dest) { |
||||
final int decompressed = decompress(src, src.position(), src.remaining(), dest, dest.position(), dest.remaining()); |
||||
src.position(src.limit()); |
||||
dest.position(dest.position() + decompressed); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,179 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.LAST_LITERALS; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.ML_BITS; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.ML_MASK; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.RUN_MASK; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
enum LZ4SafeUtils { |
||||
; |
||||
|
||||
static int hash(byte[] buf, int i) { |
||||
return LZ4Utils.hash(SafeUtils.readInt(buf, i)); |
||||
} |
||||
|
||||
static int hash64k(byte[] buf, int i) { |
||||
return LZ4Utils.hash64k(SafeUtils.readInt(buf, i)); |
||||
} |
||||
|
||||
static boolean readIntEquals(byte[] buf, int i, int j) { |
||||
return buf[i] == buf[j] && buf[i+1] == buf[j+1] && buf[i+2] == buf[j+2] && buf[i+3] == buf[j+3]; |
||||
} |
||||
|
||||
static void safeIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchLen) { |
||||
for (int i = 0; i < matchLen; ++i) { |
||||
dest[dOff + i] = dest[matchOff + i]; |
||||
} |
||||
} |
||||
|
||||
static void wildIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchCopyEnd) { |
||||
do { |
||||
copy8Bytes(dest, matchOff, dest, dOff); |
||||
matchOff += 8; |
||||
dOff += 8; |
||||
} while (dOff < matchCopyEnd); |
||||
} |
||||
|
||||
static void copy8Bytes(byte[] src, int sOff, byte[] dest, int dOff) { |
||||
for (int i = 0; i < 8; ++i) { |
||||
dest[dOff + i] = src[sOff + i]; |
||||
} |
||||
} |
||||
|
||||
static int commonBytes(byte[] b, int o1, int o2, int limit) { |
||||
int count = 0; |
||||
while (o2 < limit && b[o1++] == b[o2++]) { |
||||
++count; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) { |
||||
int count = 0; |
||||
while (o1 > l1 && o2 > l2 && b[--o1] == b[--o2]) { |
||||
++count; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
static void safeArraycopy(byte[] src, int sOff, byte[] dest, int dOff, int len) { |
||||
System.arraycopy(src, sOff, dest, dOff, len); |
||||
} |
||||
|
||||
static void wildArraycopy(byte[] src, int sOff, byte[] dest, int dOff, int len) { |
||||
try { |
||||
for (int i = 0; i < len; i += 8) { |
||||
copy8Bytes(src, sOff + i, dest, dOff + i); |
||||
} |
||||
} catch (ArrayIndexOutOfBoundsException e) { |
||||
throw new LZ4Exception("Malformed input at offset " + sOff); |
||||
} |
||||
} |
||||
|
||||
static int encodeSequence(byte[] src, int anchor, int matchOff, int matchRef, int matchLen, byte[] dest, int dOff, int destEnd) { |
||||
final int runLen = matchOff - anchor; |
||||
final int tokenOff = dOff++; |
||||
|
||||
if (dOff + runLen + (2 + 1 + LAST_LITERALS) + (runLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
|
||||
int token; |
||||
if (runLen >= RUN_MASK) { |
||||
token = (byte) (RUN_MASK << ML_BITS); |
||||
dOff = writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
token = runLen << ML_BITS; |
||||
} |
||||
|
||||
// copy literals
|
||||
wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
// encode offset
|
||||
final int matchDec = matchOff - matchRef; |
||||
dest[dOff++] = (byte) matchDec; |
||||
dest[dOff++] = (byte) (matchDec >>> 8); |
||||
|
||||
// encode match len
|
||||
matchLen -= 4; |
||||
if (dOff + (1 + LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
if (matchLen >= ML_MASK) { |
||||
token |= ML_MASK; |
||||
dOff = writeLen(matchLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
token |= matchLen; |
||||
} |
||||
|
||||
dest[tokenOff] = (byte) token; |
||||
|
||||
return dOff; |
||||
} |
||||
|
||||
static int lastLiterals(byte[] src, int sOff, int srcLen, byte[] dest, int dOff, int destEnd) { |
||||
final int runLen = srcLen; |
||||
|
||||
if (dOff + runLen + 1 + (runLen + 255 - RUN_MASK) / 255 > destEnd) { |
||||
throw new LZ4Exception(); |
||||
} |
||||
|
||||
if (runLen >= RUN_MASK) { |
||||
dest[dOff++] = (byte) (RUN_MASK << ML_BITS); |
||||
dOff = writeLen(runLen - RUN_MASK, dest, dOff); |
||||
} else { |
||||
dest[dOff++] = (byte) (runLen << ML_BITS); |
||||
} |
||||
// copy literals
|
||||
System.arraycopy(src, sOff, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
return dOff; |
||||
} |
||||
|
||||
static int writeLen(int len, byte[] dest, int dOff) { |
||||
while (len >= 0xFF) { |
||||
dest[dOff++] = (byte) 0xFF; |
||||
len -= 0xFF; |
||||
} |
||||
dest[dOff++] = (byte) len; |
||||
return dOff; |
||||
} |
||||
|
||||
static class Match { |
||||
int start, ref, len; |
||||
|
||||
void fix(int correction) { |
||||
start += correction; |
||||
ref += correction; |
||||
len -= correction; |
||||
} |
||||
|
||||
int end() { |
||||
return start + len; |
||||
} |
||||
} |
||||
|
||||
static void copyTo(Match m1, Match m2) { |
||||
m2.len = m1.len; |
||||
m2.start = m1.start; |
||||
m2.ref = m1.ref; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,27 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* @deprecated Use {@link LZ4SafeDecompressor} instead. |
||||
*/ |
||||
@Deprecated |
||||
public interface LZ4UnknownSizeDecompressor { |
||||
|
||||
int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff, int maxDestLen); |
||||
|
||||
int decompress(byte[] src, int srcOff, int srcLen, byte[] dest, int destOff); |
||||
|
||||
} |
@ -0,0 +1,200 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import com.fr.third.net.jpountz.util.UnsafeUtils; |
||||
import com.fr.third.net.jpountz.util.Utils; |
||||
|
||||
import static com.fr.third.net.jpountz.util.UnsafeUtils.readInt; |
||||
import static com.fr.third.net.jpountz.util.UnsafeUtils.readShort; |
||||
import static com.fr.third.net.jpountz.util.UnsafeUtils.writeByte; |
||||
import static com.fr.third.net.jpountz.util.UnsafeUtils.writeInt; |
||||
import static com.fr.third.net.jpountz.util.UnsafeUtils.writeShort; |
||||
|
||||
import java.nio.ByteOrder; |
||||
|
||||
enum LZ4UnsafeUtils { |
||||
; |
||||
|
||||
static void safeArraycopy(byte[] src, int srcOff, byte[] dest, int destOff, int len) { |
||||
final int fastLen = len & 0xFFFFFFF8; |
||||
wildArraycopy(src, srcOff, dest, destOff, fastLen); |
||||
for (int i = 0, slowLen = len & 0x7; i < slowLen; i += 1) { |
||||
UnsafeUtils.writeByte(dest, destOff + fastLen + i, UnsafeUtils.readByte(src, srcOff + fastLen + i)); |
||||
} |
||||
} |
||||
|
||||
static void wildArraycopy(byte[] src, int srcOff, byte[] dest, int destOff, int len) { |
||||
for (int i = 0; i < len; i += 8) { |
||||
UnsafeUtils.writeLong(dest, destOff + i, UnsafeUtils.readLong(src, srcOff + i)); |
||||
} |
||||
} |
||||
|
||||
static void wildIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchCopyEnd) { |
||||
if (dOff - matchOff < 4) { |
||||
for (int i = 0; i < 4; ++i) { |
||||
UnsafeUtils.writeByte(dest, dOff+i, UnsafeUtils.readByte(dest, matchOff+i)); |
||||
} |
||||
dOff += 4; |
||||
matchOff += 4; |
||||
int dec = 0; |
||||
assert dOff >= matchOff && dOff - matchOff < 8; |
||||
switch (dOff - matchOff) { |
||||
case 1: |
||||
matchOff -= 3; |
||||
break; |
||||
case 2: |
||||
matchOff -= 2; |
||||
break; |
||||
case 3: |
||||
matchOff -= 3; |
||||
dec = -1; |
||||
break; |
||||
case 5: |
||||
dec = 1; |
||||
break; |
||||
case 6: |
||||
dec = 2; |
||||
break; |
||||
case 7: |
||||
dec = 3; |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
UnsafeUtils.writeInt(dest, dOff, UnsafeUtils.readInt(dest, matchOff)); |
||||
dOff += 4; |
||||
matchOff -= dec; |
||||
} else if (dOff - matchOff < LZ4Constants.COPY_LENGTH) { |
||||
UnsafeUtils.writeLong(dest, dOff, UnsafeUtils.readLong(dest, matchOff)); |
||||
dOff += dOff - matchOff; |
||||
} |
||||
while (dOff < matchCopyEnd) { |
||||
UnsafeUtils.writeLong(dest, dOff, UnsafeUtils.readLong(dest, matchOff)); |
||||
dOff += 8; |
||||
matchOff += 8; |
||||
} |
||||
} |
||||
|
||||
static void safeIncrementalCopy(byte[] dest, int matchOff, int dOff, int matchLen) { |
||||
for (int i = 0; i < matchLen; ++i) { |
||||
dest[dOff + i] = dest[matchOff + i]; |
||||
UnsafeUtils.writeByte(dest, dOff + i, UnsafeUtils.readByte(dest, matchOff + i)); |
||||
} |
||||
} |
||||
|
||||
static int readShortLittleEndian(byte[] src, int srcOff) { |
||||
short s = UnsafeUtils.readShort(src, srcOff); |
||||
if (Utils.NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { |
||||
s = Short.reverseBytes(s); |
||||
} |
||||
return s & 0xFFFF; |
||||
} |
||||
|
||||
static void writeShortLittleEndian(byte[] dest, int destOff, int value) { |
||||
short s = (short) value; |
||||
if (Utils.NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { |
||||
s = Short.reverseBytes(s); |
||||
} |
||||
UnsafeUtils.writeShort(dest, destOff, s); |
||||
} |
||||
|
||||
static boolean readIntEquals(byte[] src, int ref, int sOff) { |
||||
return UnsafeUtils.readInt(src, ref) == UnsafeUtils.readInt(src, sOff); |
||||
} |
||||
|
||||
static int commonBytes(byte[] src, int ref, int sOff, int srcLimit) { |
||||
int matchLen = 0; |
||||
while (sOff <= srcLimit - 8) { |
||||
if (UnsafeUtils.readLong(src, sOff) == UnsafeUtils.readLong(src, ref)) { |
||||
matchLen += 8; |
||||
ref += 8; |
||||
sOff += 8; |
||||
} else { |
||||
final int zeroBits; |
||||
if (Utils.NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { |
||||
zeroBits = Long.numberOfLeadingZeros(UnsafeUtils.readLong(src, sOff) ^ UnsafeUtils.readLong(src, ref)); |
||||
} else { |
||||
zeroBits = Long.numberOfTrailingZeros(UnsafeUtils.readLong(src, sOff) ^ UnsafeUtils.readLong(src, ref)); |
||||
} |
||||
return matchLen + (zeroBits >>> 3); |
||||
} |
||||
} |
||||
while (sOff < srcLimit && UnsafeUtils.readByte(src, ref++) == UnsafeUtils.readByte(src, sOff++)) { |
||||
++matchLen; |
||||
} |
||||
return matchLen; |
||||
} |
||||
|
||||
static int writeLen(int len, byte[] dest, int dOff) { |
||||
while (len >= 0xFF) { |
||||
UnsafeUtils.writeByte(dest, dOff++, 0xFF); |
||||
len -= 0xFF; |
||||
} |
||||
UnsafeUtils.writeByte(dest, dOff++, len); |
||||
return dOff; |
||||
} |
||||
|
||||
static int encodeSequence(byte[] src, int anchor, int matchOff, int matchRef, int matchLen, byte[] dest, int dOff, int destEnd) { |
||||
final int runLen = matchOff - anchor; |
||||
final int tokenOff = dOff++; |
||||
int token; |
||||
|
||||
if (runLen >= LZ4Constants.RUN_MASK) { |
||||
token = (byte) (LZ4Constants.RUN_MASK << LZ4Constants.ML_BITS); |
||||
dOff = writeLen(runLen - LZ4Constants.RUN_MASK, dest, dOff); |
||||
} else { |
||||
token = runLen << LZ4Constants.ML_BITS; |
||||
} |
||||
|
||||
// copy literals
|
||||
wildArraycopy(src, anchor, dest, dOff, runLen); |
||||
dOff += runLen; |
||||
|
||||
// encode offset
|
||||
final int matchDec = matchOff - matchRef; |
||||
dest[dOff++] = (byte) matchDec; |
||||
dest[dOff++] = (byte) (matchDec >>> 8); |
||||
|
||||
// encode match len
|
||||
matchLen -= 4; |
||||
if (dOff + (1 + LZ4Constants.LAST_LITERALS) + (matchLen >>> 8) > destEnd) { |
||||
throw new LZ4Exception("maxDestLen is too small"); |
||||
} |
||||
if (matchLen >= LZ4Constants.ML_MASK) { |
||||
token |= LZ4Constants.ML_MASK; |
||||
dOff = writeLen(matchLen - LZ4Constants.RUN_MASK, dest, dOff); |
||||
} else { |
||||
token |= matchLen; |
||||
} |
||||
|
||||
dest[tokenOff] = (byte) token; |
||||
|
||||
return dOff; |
||||
} |
||||
|
||||
static int commonBytesBackward(byte[] b, int o1, int o2, int l1, int l2) { |
||||
int count = 0; |
||||
while (o1 > l1 && o2 > l2 && UnsafeUtils.readByte(b, --o1) == UnsafeUtils.readByte(b, --o2)) { |
||||
++count; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
static int lastLiterals(byte[] src, int sOff, int srcLen, byte[] dest, int dOff, int destEnd) { |
||||
return LZ4SafeUtils.lastLiterals(src, sOff, srcLen, dest, dOff, destEnd); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,68 @@
|
||||
package com.fr.third.net.jpountz.lz4; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.HASH_LOG; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.HASH_LOG_64K; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.HASH_LOG_HC; |
||||
import static com.fr.third.net.jpountz.lz4.LZ4Constants.MIN_MATCH; |
||||
|
||||
enum LZ4Utils { |
||||
; |
||||
|
||||
private static final int MAX_INPUT_SIZE = 0x7E000000; |
||||
|
||||
static int maxCompressedLength(int length) { |
||||
if (length < 0) { |
||||
throw new IllegalArgumentException("length must be >= 0, got " + length); |
||||
} else if (length >= MAX_INPUT_SIZE) { |
||||
throw new IllegalArgumentException("length must be < " + MAX_INPUT_SIZE); |
||||
} |
||||
return length + length / 255 + 16; |
||||
} |
||||
|
||||
static int hash(int i) { |
||||
return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG); |
||||
} |
||||
|
||||
static int hash64k(int i) { |
||||
return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG_64K); |
||||
} |
||||
|
||||
static int hashHC(int i) { |
||||
return (i * -1640531535) >>> ((MIN_MATCH * 8) - HASH_LOG_HC); |
||||
} |
||||
|
||||
static class Match { |
||||
int start, ref, len; |
||||
|
||||
void fix(int correction) { |
||||
start += correction; |
||||
ref += correction; |
||||
len -= correction; |
||||
} |
||||
|
||||
int end() { |
||||
return start + len; |
||||
} |
||||
} |
||||
|
||||
static void copyTo(Match m1, Match m2) { |
||||
m2.len = m1.len; |
||||
m2.start = m1.start; |
||||
m2.ref = m1.ref; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,55 @@
|
||||
<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> |
||||
<!-- |
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
--> |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
||||
</head> |
||||
<body> |
||||
<p>LZ4 compression. The entry point of the API is the |
||||
{@link net.jpountz.lz4.LZ4Factory} class, which gives access to |
||||
{@link com.fr.third.net.jpountz.lz4.LZ4Compressor compressors} and |
||||
{@link com.fr.third.net.jpountz.lz4.LZ4SafeDecompressor decompressors}.</p> |
||||
|
||||
|
||||
<p>Sample usage:</p> |
||||
|
||||
<pre class="prettyprint"> |
||||
LZ4Factory factory = LZ4Factory.fastestInstance(); |
||||
|
||||
byte[] data = "12345345234572".getBytes("UTF-8"); |
||||
final int decompressedLength = data.length; |
||||
|
||||
// compress data |
||||
LZ4Compressor compressor = factory.fastCompressor(); |
||||
int maxCompressedLength = compressor.maxCompressedLength(decompressedLength); |
||||
byte[] compressed = new byte[maxCompressedLength]; |
||||
int compressedLength = compressor.compress(data, 0, decompressedLength, compressed, 0, maxCompressedLength); |
||||
|
||||
// decompress data |
||||
// - method 1: when the decompressed length is known |
||||
LZ4FastDecompressor decompressor = factory.fastDecompressor(); |
||||
byte[] restored = new byte[decompressedLength]; |
||||
int compressedLength2 = decompressor.decompress(compressed, 0, restored, 0, decompressedLength); |
||||
// compressedLength == compressedLength2 |
||||
|
||||
// - method 2: when the compressed length is known (a little slower) |
||||
// the destination buffer needs to be over-sized |
||||
LZ4SafeDecompressor decompressor2 = factory.safeDecompressor(); |
||||
int decompressedLength2 = decompressor2.decompress(compressed, 0, compressedLength, restored, 0); |
||||
// decompressedLength == decompressedLength2 |
||||
</pre> |
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,92 @@
|
||||
package com.fr.third.net.jpountz.util; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
import java.nio.ByteOrder; |
||||
import java.nio.ReadOnlyBufferException; |
||||
|
||||
public enum ByteBufferUtils { |
||||
; |
||||
|
||||
public static void checkRange(ByteBuffer buf, int off, int len) { |
||||
SafeUtils.checkLength(len); |
||||
if (len > 0) { |
||||
checkRange(buf, off); |
||||
checkRange(buf, off + len - 1); |
||||
} |
||||
} |
||||
|
||||
public static void checkRange(ByteBuffer buf, int off) { |
||||
if (off < 0 || off >= buf.capacity()) { |
||||
throw new ArrayIndexOutOfBoundsException(off); |
||||
} |
||||
} |
||||
|
||||
public static ByteBuffer inLittleEndianOrder(ByteBuffer buf) { |
||||
if (buf.order().equals(ByteOrder.LITTLE_ENDIAN)) { |
||||
return buf; |
||||
} else { |
||||
return buf.duplicate().order(ByteOrder.LITTLE_ENDIAN); |
||||
} |
||||
} |
||||
|
||||
public static ByteBuffer inNativeByteOrder(ByteBuffer buf) { |
||||
if (buf.order().equals(Utils.NATIVE_BYTE_ORDER)) { |
||||
return buf; |
||||
} else { |
||||
return buf.duplicate().order(Utils.NATIVE_BYTE_ORDER); |
||||
} |
||||
} |
||||
|
||||
public static byte readByte(ByteBuffer buf, int i) { |
||||
return buf.get(i); |
||||
} |
||||
|
||||
public static void writeInt(ByteBuffer buf, int i, int v) { |
||||
assert buf.order() == Utils.NATIVE_BYTE_ORDER; |
||||
buf.putInt(i, v); |
||||
} |
||||
|
||||
public static int readInt(ByteBuffer buf, int i) { |
||||
assert buf.order() == Utils.NATIVE_BYTE_ORDER; |
||||
return buf.getInt(i); |
||||
} |
||||
|
||||
public static int readIntLE(ByteBuffer buf, int i) { |
||||
assert buf.order() == ByteOrder.LITTLE_ENDIAN; |
||||
return buf.getInt(i); |
||||
} |
||||
|
||||
public static void writeLong(ByteBuffer buf, int i, long v) { |
||||
assert buf.order() == Utils.NATIVE_BYTE_ORDER; |
||||
buf.putLong(i, v); |
||||
} |
||||
|
||||
public static long readLong(ByteBuffer buf, int i) { |
||||
assert buf.order() == Utils.NATIVE_BYTE_ORDER; |
||||
return buf.getLong(i); |
||||
} |
||||
|
||||
public static long readLongLE(ByteBuffer buf, int i) { |
||||
assert buf.order() == ByteOrder.LITTLE_ENDIAN; |
||||
return buf.getLong(i); |
||||
} |
||||
|
||||
public static void writeByte(ByteBuffer dest, int off, int i) { |
||||
dest.put(off, (byte) i); |
||||
} |
||||
|
||||
public static void writeShortLE(ByteBuffer dest, int off, int i) { |
||||
dest.put(off, (byte) i); |
||||
dest.put(off + 1, (byte) (i >>> 8)); |
||||
} |
||||
|
||||
public static void checkNotReadOnly(ByteBuffer buffer) { |
||||
if (buffer.isReadOnly()) { |
||||
throw new ReadOnlyBufferException(); |
||||
} |
||||
} |
||||
|
||||
public static int readShortLE(ByteBuffer buf, int i) { |
||||
return (buf.get(i) & 0xFF) | ((buf.get(i+1) & 0xFF) << 8); |
||||
} |
||||
} |
@ -0,0 +1,133 @@
|
||||
package com.fr.third.net.jpountz.util; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.io.File; |
||||
import java.io.FileOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
|
||||
/** FOR INTERNAL USE ONLY */ |
||||
public enum Native { |
||||
; |
||||
|
||||
private enum OS { |
||||
// Even on Windows, the default compiler from cpptasks (gcc) uses .so as a shared lib extension
|
||||
WINDOWS("win32", "so"), LINUX("linux", "so"), MAC("darwin", "dylib"), SOLARIS("solaris", "so"); |
||||
public final String name, libExtension; |
||||
|
||||
private OS(String name, String libExtension) { |
||||
this.name = name; |
||||
this.libExtension = libExtension; |
||||
} |
||||
} |
||||
|
||||
private static String arch() { |
||||
return System.getProperty("os.arch"); |
||||
} |
||||
|
||||
private static OS os() { |
||||
String osName = System.getProperty("os.name"); |
||||
if (osName.contains("Linux")) { |
||||
return OS.LINUX; |
||||
} else if (osName.contains("Mac")) { |
||||
return OS.MAC; |
||||
} else if (osName.contains("Windows")) { |
||||
return OS.WINDOWS; |
||||
} else if (osName.contains("Solaris") || osName.contains("SunOS")) { |
||||
return OS.SOLARIS; |
||||
} else { |
||||
throw new UnsupportedOperationException("Unsupported operating system: " |
||||
+ osName); |
||||
} |
||||
} |
||||
|
||||
private static String resourceName() { |
||||
OS os = os(); |
||||
String packagePrefix = Native.class.getPackage().getName().replace('.', '/'); |
||||
|
||||
return "/" + packagePrefix + "/" + os.name + "/" + arch() + "/liblz4-java." + os.libExtension; |
||||
} |
||||
|
||||
private static boolean loaded = false; |
||||
|
||||
public static synchronized boolean isLoaded() { |
||||
return loaded; |
||||
} |
||||
|
||||
public static synchronized void load() { |
||||
if (loaded) { |
||||
return; |
||||
} |
||||
|
||||
// Try to load lz4-java (liblz4-java.so on Linux) from the java.library.path.
|
||||
try { |
||||
System.loadLibrary("lz4-java"); |
||||
loaded = true; |
||||
return; |
||||
} catch (UnsatisfiedLinkError ex) { |
||||
// Doesn't exist, so proceed to loading bundled library.
|
||||
} |
||||
|
||||
String resourceName = resourceName(); |
||||
InputStream is = Native.class.getResourceAsStream(resourceName); |
||||
if (is == null) { |
||||
throw new UnsupportedOperationException("Unsupported OS/arch, cannot find " + resourceName + ". Please try building from source."); |
||||
} |
||||
File tempLib; |
||||
try { |
||||
tempLib = File.createTempFile("liblz4-java", "." + os().libExtension); |
||||
// copy to tempLib
|
||||
FileOutputStream out = new FileOutputStream(tempLib); |
||||
try { |
||||
byte[] buf = new byte[4096]; |
||||
while (true) { |
||||
int read = is.read(buf); |
||||
if (read == -1) { |
||||
break; |
||||
} |
||||
out.write(buf, 0, read); |
||||
} |
||||
try { |
||||
out.close(); |
||||
out = null; |
||||
} catch (IOException e) { |
||||
// ignore
|
||||
} |
||||
System.load(tempLib.getAbsolutePath()); |
||||
loaded = true; |
||||
} finally { |
||||
try { |
||||
if (out != null) { |
||||
out.close(); |
||||
} |
||||
} catch (IOException e) { |
||||
// ignore
|
||||
} |
||||
if (tempLib != null && tempLib.exists()) { |
||||
if (!loaded) { |
||||
tempLib.delete(); |
||||
} else { |
||||
// try to delete on exit, does it work on Windows?
|
||||
tempLib.deleteOnExit(); |
||||
} |
||||
} |
||||
} |
||||
} catch (IOException e) { |
||||
throw new ExceptionInInitializerError("Cannot unpack liblz4-java"); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,95 @@
|
||||
package com.fr.third.net.jpountz.util; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.nio.ByteOrder; |
||||
|
||||
public enum SafeUtils { |
||||
; |
||||
|
||||
public static void checkRange(byte[] buf, int off) { |
||||
if (off < 0 || off >= buf.length) { |
||||
throw new ArrayIndexOutOfBoundsException(off); |
||||
} |
||||
} |
||||
|
||||
public static void checkRange(byte[] buf, int off, int len) { |
||||
checkLength(len); |
||||
if (len > 0) { |
||||
checkRange(buf, off); |
||||
checkRange(buf, off + len - 1); |
||||
} |
||||
} |
||||
|
||||
public static void checkLength(int len) { |
||||
if (len < 0) { |
||||
throw new IllegalArgumentException("lengths must be >= 0"); |
||||
} |
||||
} |
||||
|
||||
public static byte readByte(byte[] buf, int i) { |
||||
return buf[i]; |
||||
} |
||||
|
||||
public static int readIntBE(byte[] buf, int i) { |
||||
return ((buf[i] & 0xFF) << 24) | ((buf[i+1] & 0xFF) << 16) | ((buf[i+2] & 0xFF) << 8) | (buf[i+3] & 0xFF); |
||||
} |
||||
|
||||
public static int readIntLE(byte[] buf, int i) { |
||||
return (buf[i] & 0xFF) | ((buf[i+1] & 0xFF) << 8) | ((buf[i+2] & 0xFF) << 16) | ((buf[i+3] & 0xFF) << 24); |
||||
} |
||||
|
||||
public static int readInt(byte[] buf, int i) { |
||||
if (Utils.NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { |
||||
return readIntBE(buf, i); |
||||
} else { |
||||
return readIntLE(buf, i); |
||||
} |
||||
} |
||||
|
||||
public static long readLongLE(byte[] buf, int i) { |
||||
return (buf[i] & 0xFFL) | ((buf[i+1] & 0xFFL) << 8) | ((buf[i+2] & 0xFFL) << 16) | ((buf[i+3] & 0xFFL) << 24) |
||||
| ((buf[i+4] & 0xFFL) << 32) | ((buf[i+5] & 0xFFL) << 40) | ((buf[i+6] & 0xFFL) << 48) | ((buf[i+7] & 0xFFL) << 56); |
||||
} |
||||
|
||||
public static void writeShortLE(byte[] buf, int off, int v) { |
||||
buf[off++] = (byte) v; |
||||
buf[off++] = (byte) (v >>> 8); |
||||
} |
||||
|
||||
public static void writeInt(int[] buf, int off, int v) { |
||||
buf[off] = v; |
||||
} |
||||
|
||||
public static int readInt(int[] buf, int off) { |
||||
return buf[off]; |
||||
} |
||||
|
||||
public static void writeByte(byte[] dest, int off, int i) { |
||||
dest[off] = (byte) i; |
||||
} |
||||
|
||||
public static void writeShort(short[] buf, int off, int v) { |
||||
buf[off] = (short) v; |
||||
} |
||||
|
||||
public static int readShortLE(byte[] buf, int i) { |
||||
return (buf[i] & 0xFF) | ((buf[i+1] & 0xFF) << 8); |
||||
} |
||||
|
||||
public static int readShort(short[] buf, int off) { |
||||
return buf[off] & 0xFFFF; |
||||
} |
||||
} |
@ -0,0 +1,147 @@
|
||||
package com.fr.third.net.jpountz.util; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import static com.fr.third.net.jpountz.util.Utils.NATIVE_BYTE_ORDER; |
||||
|
||||
import java.lang.reflect.Field; |
||||
import java.nio.ByteOrder; |
||||
|
||||
import sun.misc.Unsafe; |
||||
|
||||
public enum UnsafeUtils { |
||||
; |
||||
|
||||
private static final Unsafe UNSAFE; |
||||
private static final long BYTE_ARRAY_OFFSET; |
||||
private static final int BYTE_ARRAY_SCALE; |
||||
private static final long INT_ARRAY_OFFSET; |
||||
private static final int INT_ARRAY_SCALE; |
||||
private static final long SHORT_ARRAY_OFFSET; |
||||
private static final int SHORT_ARRAY_SCALE; |
||||
|
||||
static { |
||||
try { |
||||
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); |
||||
theUnsafe.setAccessible(true); |
||||
UNSAFE = (Unsafe) theUnsafe.get(null); |
||||
BYTE_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(byte[].class); |
||||
BYTE_ARRAY_SCALE = UNSAFE.arrayIndexScale(byte[].class); |
||||
INT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(int[].class); |
||||
INT_ARRAY_SCALE = UNSAFE.arrayIndexScale(int[].class); |
||||
SHORT_ARRAY_OFFSET = UNSAFE.arrayBaseOffset(short[].class); |
||||
SHORT_ARRAY_SCALE = UNSAFE.arrayIndexScale(short[].class); |
||||
} catch (IllegalAccessException e) { |
||||
throw new ExceptionInInitializerError("Cannot access Unsafe"); |
||||
} catch (NoSuchFieldException e) { |
||||
throw new ExceptionInInitializerError("Cannot access Unsafe"); |
||||
} catch (SecurityException e) { |
||||
throw new ExceptionInInitializerError("Cannot access Unsafe"); |
||||
} |
||||
} |
||||
|
||||
public static void checkRange(byte[] buf, int off) { |
||||
SafeUtils.checkRange(buf, off); |
||||
} |
||||
|
||||
public static void checkRange(byte[] buf, int off, int len) { |
||||
SafeUtils.checkRange(buf, off, len); |
||||
} |
||||
|
||||
public static void checkLength(int len) { |
||||
SafeUtils.checkLength(len); |
||||
} |
||||
|
||||
public static byte readByte(byte[] src, int srcOff) { |
||||
return UNSAFE.getByte(src, BYTE_ARRAY_OFFSET + BYTE_ARRAY_SCALE * srcOff); |
||||
} |
||||
|
||||
public static void writeByte(byte[] src, int srcOff, byte value) { |
||||
UNSAFE.putByte(src, BYTE_ARRAY_OFFSET + BYTE_ARRAY_SCALE * srcOff, (byte) value); |
||||
} |
||||
|
||||
public static void writeByte(byte[] src, int srcOff, int value) { |
||||
writeByte(src, srcOff, (byte) value); |
||||
} |
||||
|
||||
public static long readLong(byte[] src, int srcOff) { |
||||
return UNSAFE.getLong(src, BYTE_ARRAY_OFFSET + srcOff); |
||||
} |
||||
|
||||
public static long readLongLE(byte[] src, int srcOff) { |
||||
long i = readLong(src, srcOff); |
||||
if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { |
||||
i = Long.reverseBytes(i); |
||||
} |
||||
return i; |
||||
} |
||||
|
||||
public static void writeLong(byte[] dest, int destOff, long value) { |
||||
UNSAFE.putLong(dest, BYTE_ARRAY_OFFSET + destOff, value); |
||||
} |
||||
|
||||
public static int readInt(byte[] src, int srcOff) { |
||||
return UNSAFE.getInt(src, BYTE_ARRAY_OFFSET + srcOff); |
||||
} |
||||
|
||||
public static int readIntLE(byte[] src, int srcOff) { |
||||
int i = readInt(src, srcOff); |
||||
if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { |
||||
i = Integer.reverseBytes(i); |
||||
} |
||||
return i; |
||||
} |
||||
|
||||
public static void writeInt(byte[] dest, int destOff, int value) { |
||||
UNSAFE.putInt(dest, BYTE_ARRAY_OFFSET + destOff, value); |
||||
} |
||||
|
||||
public static short readShort(byte[] src, int srcOff) { |
||||
return UNSAFE.getShort(src, BYTE_ARRAY_OFFSET + srcOff); |
||||
} |
||||
|
||||
public static int readShortLE(byte[] src, int srcOff) { |
||||
short s = readShort(src, srcOff); |
||||
if (NATIVE_BYTE_ORDER == ByteOrder.BIG_ENDIAN) { |
||||
s = Short.reverseBytes(s); |
||||
} |
||||
return s & 0xFFFF; |
||||
} |
||||
|
||||
public static void writeShort(byte[] dest, int destOff, short value) { |
||||
UNSAFE.putShort(dest, BYTE_ARRAY_OFFSET + destOff, value); |
||||
} |
||||
|
||||
public static void writeShortLE(byte[] buf, int off, int v) { |
||||
writeByte(buf, off, (byte) v); |
||||
writeByte(buf, off + 1, (byte) (v >>> 8)); |
||||
} |
||||
|
||||
public static int readInt(int[] src, int srcOff) { |
||||
return UNSAFE.getInt(src, INT_ARRAY_OFFSET + INT_ARRAY_SCALE * srcOff); |
||||
} |
||||
|
||||
public static void writeInt(int[] dest, int destOff, int value) { |
||||
UNSAFE.putInt(dest, INT_ARRAY_OFFSET + INT_ARRAY_SCALE * destOff, value); |
||||
} |
||||
|
||||
public static int readShort(short[] src, int srcOff) { |
||||
return UNSAFE.getShort(src, SHORT_ARRAY_OFFSET + SHORT_ARRAY_SCALE * srcOff) & 0xFFFF; |
||||
} |
||||
|
||||
public static void writeShort(short[] dest, int destOff, int value) { |
||||
UNSAFE.putShort(dest, SHORT_ARRAY_OFFSET + SHORT_ARRAY_SCALE * destOff, (short) value); |
||||
} |
||||
} |
@ -0,0 +1,36 @@
|
||||
package com.fr.third.net.jpountz.util; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.nio.ByteOrder; |
||||
|
||||
public enum Utils { |
||||
; |
||||
|
||||
public static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder(); |
||||
|
||||
private static final boolean unalignedAccessAllowed; |
||||
static { |
||||
String arch = System.getProperty("os.arch"); |
||||
unalignedAccessAllowed = arch.equals("i386") || arch.equals("x86") |
||||
|| arch.equals("amd64") || arch.equals("x86_64") |
||||
|| arch.equals("aarch64") || arch.equals("ppc64le"); |
||||
} |
||||
|
||||
public static boolean isUnalignedAccessAllowed() { |
||||
return unalignedAccessAllowed; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,22 @@
|
||||
<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> |
||||
<!-- |
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
--> |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
||||
</head> |
||||
<body> |
||||
<p>Utility classes.</p> |
||||
</body> |
||||
</html> |
@ -0,0 +1,39 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
abstract class AbstractStreamingXXHash32Java extends StreamingXXHash32 { |
||||
|
||||
int v1, v2, v3, v4, memSize; |
||||
long totalLen; |
||||
final byte[] memory; |
||||
|
||||
AbstractStreamingXXHash32Java(int seed) { |
||||
super(seed); |
||||
memory = new byte[16]; |
||||
reset(); |
||||
} |
||||
|
||||
@Override |
||||
public void reset() { |
||||
v1 = seed + XXHashConstants.PRIME1 + XXHashConstants.PRIME2; |
||||
v2 = seed + XXHashConstants.PRIME2; |
||||
v3 = seed + 0; |
||||
v4 = seed - XXHashConstants.PRIME1; |
||||
totalLen = 0; |
||||
memSize = 0; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,40 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
abstract class AbstractStreamingXXHash64Java extends StreamingXXHash64 { |
||||
|
||||
int memSize; |
||||
long v1, v2, v3, v4; |
||||
long totalLen; |
||||
final byte[] memory; |
||||
|
||||
AbstractStreamingXXHash64Java(long seed) { |
||||
super(seed); |
||||
memory = new byte[32]; |
||||
reset(); |
||||
} |
||||
|
||||
@Override |
||||
public void reset() { |
||||
v1 = seed + XXHashConstants.PRIME64_1 + XXHashConstants.PRIME64_2; |
||||
v2 = seed + XXHashConstants.PRIME64_2; |
||||
v3 = seed + 0; |
||||
v4 = seed - XXHashConstants.PRIME64_1; |
||||
totalLen = 0; |
||||
memSize = 0; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,119 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import java.util.zip.Checksum; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
|
||||
|
||||
/** |
||||
* Streaming interface for {@link XXHash32}. |
||||
* <p> |
||||
* This API is compatible with the {@link XXHash32 block API} and the following |
||||
* code samples are equivalent: |
||||
* <pre class="prettyprint"> |
||||
* int hash(XXHashFactory xxhashFactory, byte[] buf, int off, int len, int seed) { |
||||
* return xxhashFactory.hash32().hash(buf, off, len, seed); |
||||
* } |
||||
* </pre> |
||||
* <pre class="prettyprint"> |
||||
* int hash(XXHashFactory xxhashFactory, byte[] buf, int off, int len, int seed) { |
||||
* StreamingXXHash32 sh32 = xxhashFactory.newStreamingHash32(seed); |
||||
* sh32.update(buf, off, len); |
||||
* return sh32.getValue(); |
||||
* } |
||||
* </pre> |
||||
* <p> |
||||
* Instances of this class are <b>not</b> thread-safe. |
||||
*/ |
||||
public abstract class StreamingXXHash32 { |
||||
|
||||
interface Factory { |
||||
|
||||
StreamingXXHash32 newStreamingHash(int seed); |
||||
|
||||
} |
||||
|
||||
final int seed; |
||||
|
||||
StreamingXXHash32(int seed) { |
||||
this.seed = seed; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value of the checksum. |
||||
* |
||||
* @return the checksum |
||||
*/ |
||||
public abstract int getValue(); |
||||
|
||||
/** |
||||
* Updates the value of the hash with buf[off:off+len]. |
||||
* |
||||
* @param buf the input data |
||||
* @param off the start offset in buf |
||||
* @param len the number of bytes to hash |
||||
*/ |
||||
public abstract void update(byte[] buf, int off, int len); |
||||
|
||||
/** |
||||
* Resets this instance to the state it had right after instantiation. The |
||||
* seed remains unchanged. |
||||
*/ |
||||
public abstract void reset(); |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName() + "(seed=" + seed + ")"; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link Checksum} view of this instance. Modifications to the view |
||||
* will modify this instance too and vice-versa. |
||||
* |
||||
* @return the {@link Checksum} object representing this instance |
||||
*/ |
||||
public final Checksum asChecksum() { |
||||
return new Checksum() { |
||||
|
||||
@Override |
||||
public long getValue() { |
||||
return StreamingXXHash32.this.getValue() & 0xFFFFFFFL; |
||||
} |
||||
|
||||
@Override |
||||
public void reset() { |
||||
StreamingXXHash32.this.reset(); |
||||
} |
||||
|
||||
@Override |
||||
public void update(int b) { |
||||
StreamingXXHash32.this.update(new byte[] {(byte) b}, 0, 1); |
||||
} |
||||
|
||||
@Override |
||||
public void update(byte[] b, int off, int len) { |
||||
StreamingXXHash32.this.update(b, off, len); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return StreamingXXHash32.this.toString(); |
||||
} |
||||
|
||||
}; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,71 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
|
||||
final class StreamingXXHash32JNI extends StreamingXXHash32 { |
||||
|
||||
static class Factory implements StreamingXXHash32.Factory { |
||||
|
||||
public static final StreamingXXHash32.Factory INSTANCE = new Factory(); |
||||
|
||||
@Override |
||||
public StreamingXXHash32 newStreamingHash(int seed) { |
||||
return new StreamingXXHash32JNI(seed); |
||||
} |
||||
|
||||
} |
||||
|
||||
private long state; |
||||
|
||||
StreamingXXHash32JNI(int seed) { |
||||
super(seed); |
||||
state = XXHashJNI.XXH32_init(seed); |
||||
} |
||||
|
||||
private void checkState() { |
||||
if (state == 0) { |
||||
throw new AssertionError("Already finalized"); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void reset() { |
||||
checkState(); |
||||
XXHashJNI.XXH32_free(state); |
||||
state = XXHashJNI.XXH32_init(seed); |
||||
} |
||||
|
||||
@Override |
||||
public int getValue() { |
||||
checkState(); |
||||
return XXHashJNI.XXH32_digest(state); |
||||
} |
||||
|
||||
@Override |
||||
public void update(byte[] bytes, int off, int len) { |
||||
checkState(); |
||||
XXHashJNI.XXH32_update(state, bytes, off, len); |
||||
} |
||||
|
||||
@Override |
||||
protected void finalize() throws Throwable { |
||||
super.finalize(); |
||||
// free memory
|
||||
XXHashJNI.XXH32_free(state); |
||||
state = 0; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,142 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import static com.fr.third.net.jpountz.xxhash.XXHashConstants.*; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.*; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.checkRange; |
||||
import static java.lang.Integer.rotateLeft; |
||||
|
||||
/** |
||||
* Streaming xxhash. |
||||
*/ |
||||
final class StreamingXXHash32JavaSafe extends AbstractStreamingXXHash32Java { |
||||
|
||||
static class Factory implements StreamingXXHash32.Factory { |
||||
|
||||
public static final StreamingXXHash32.Factory INSTANCE = new Factory(); |
||||
|
||||
@Override |
||||
public StreamingXXHash32 newStreamingHash(int seed) { |
||||
return new StreamingXXHash32JavaSafe(seed); |
||||
} |
||||
|
||||
} |
||||
|
||||
StreamingXXHash32JavaSafe(int seed) { |
||||
super(seed); |
||||
} |
||||
|
||||
@Override |
||||
public int getValue() { |
||||
int h32; |
||||
if (totalLen >= 16) { |
||||
h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
} else { |
||||
h32 = seed + PRIME5; |
||||
} |
||||
|
||||
h32 += totalLen; |
||||
|
||||
int off = 0; |
||||
while (off <= memSize - 4) { |
||||
h32 += readIntLE(memory, off) * PRIME3; |
||||
h32 = rotateLeft(h32, 17) * PRIME4; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < memSize) { |
||||
h32 += (readByte(memory, off) & 0xFF) * PRIME5; |
||||
h32 = rotateLeft(h32, 11) * PRIME1; |
||||
++off; |
||||
} |
||||
|
||||
h32 ^= h32 >>> 15; |
||||
h32 *= PRIME2; |
||||
h32 ^= h32 >>> 13; |
||||
h32 *= PRIME3; |
||||
h32 ^= h32 >>> 16; |
||||
|
||||
return h32; |
||||
} |
||||
|
||||
@Override |
||||
public void update(byte[] buf, int off, int len) { |
||||
checkRange(buf, off, len); |
||||
|
||||
totalLen += len; |
||||
|
||||
if (memSize + len < 16) { // fill in tmp buffer
|
||||
System.arraycopy(buf, off, memory, memSize, len); |
||||
memSize += len; |
||||
return; |
||||
} |
||||
|
||||
final int end = off + len; |
||||
|
||||
if (memSize > 0) { // data left from previous update
|
||||
System.arraycopy(buf, off, memory, memSize, 16 - memSize); |
||||
|
||||
v1 += readIntLE(memory, 0) * PRIME2; |
||||
v1 = rotateLeft(v1, 13); |
||||
v1 *= PRIME1; |
||||
|
||||
v2 += readIntLE(memory, 4) * PRIME2; |
||||
v2 = rotateLeft(v2, 13); |
||||
v2 *= PRIME1; |
||||
|
||||
v3 += readIntLE(memory, 8) * PRIME2; |
||||
v3 = rotateLeft(v3, 13); |
||||
v3 *= PRIME1; |
||||
|
||||
v4 += readIntLE(memory, 12) * PRIME2; |
||||
v4 = rotateLeft(v4, 13); |
||||
v4 *= PRIME1; |
||||
|
||||
off += 16 - memSize; |
||||
memSize = 0; |
||||
} |
||||
|
||||
{ |
||||
final int limit = end - 16; |
||||
int v1 = this.v1; |
||||
int v2 = this.v2; |
||||
int v3 = this.v3; |
||||
int v4 = this.v4; |
||||
|
||||
while (off <= limit) { |
||||
v1 += readIntLE(buf, off) * PRIME2; |
||||
v1 = rotateLeft(v1, 13); |
||||
v1 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v2 += readIntLE(buf, off) * PRIME2; |
||||
v2 = rotateLeft(v2, 13); |
||||
v2 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v3 += readIntLE(buf, off) * PRIME2; |
||||
v3 = rotateLeft(v3, 13); |
||||
v3 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v4 += readIntLE(buf, off) * PRIME2; |
||||
v4 = rotateLeft(v4, 13); |
||||
v4 *= PRIME1; |
||||
off += 4; |
||||
} |
||||
|
||||
this.v1 = v1; |
||||
this.v2 = v2; |
||||
this.v3 = v3; |
||||
this.v4 = v4; |
||||
} |
||||
|
||||
if (off < end) { |
||||
System.arraycopy(buf, off, memory, 0, end - off); |
||||
memSize = end - off; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,142 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import static com.fr.third.net.jpountz.xxhash.XXHashConstants.*; |
||||
import static com.fr.third.net.jpountz.util.UnsafeUtils.*; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.checkRange; |
||||
import static java.lang.Integer.rotateLeft; |
||||
|
||||
/** |
||||
* Streaming xxhash. |
||||
*/ |
||||
final class StreamingXXHash32JavaUnsafe extends AbstractStreamingXXHash32Java { |
||||
|
||||
static class Factory implements StreamingXXHash32.Factory { |
||||
|
||||
public static final StreamingXXHash32.Factory INSTANCE = new Factory(); |
||||
|
||||
@Override |
||||
public StreamingXXHash32 newStreamingHash(int seed) { |
||||
return new StreamingXXHash32JavaUnsafe(seed); |
||||
} |
||||
|
||||
} |
||||
|
||||
StreamingXXHash32JavaUnsafe(int seed) { |
||||
super(seed); |
||||
} |
||||
|
||||
@Override |
||||
public int getValue() { |
||||
int h32; |
||||
if (totalLen >= 16) { |
||||
h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
} else { |
||||
h32 = seed + PRIME5; |
||||
} |
||||
|
||||
h32 += totalLen; |
||||
|
||||
int off = 0; |
||||
while (off <= memSize - 4) { |
||||
h32 += readIntLE(memory, off) * PRIME3; |
||||
h32 = rotateLeft(h32, 17) * PRIME4; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < memSize) { |
||||
h32 += (readByte(memory, off) & 0xFF) * PRIME5; |
||||
h32 = rotateLeft(h32, 11) * PRIME1; |
||||
++off; |
||||
} |
||||
|
||||
h32 ^= h32 >>> 15; |
||||
h32 *= PRIME2; |
||||
h32 ^= h32 >>> 13; |
||||
h32 *= PRIME3; |
||||
h32 ^= h32 >>> 16; |
||||
|
||||
return h32; |
||||
} |
||||
|
||||
@Override |
||||
public void update(byte[] buf, int off, int len) { |
||||
checkRange(buf, off, len); |
||||
|
||||
totalLen += len; |
||||
|
||||
if (memSize + len < 16) { // fill in tmp buffer
|
||||
System.arraycopy(buf, off, memory, memSize, len); |
||||
memSize += len; |
||||
return; |
||||
} |
||||
|
||||
final int end = off + len; |
||||
|
||||
if (memSize > 0) { // data left from previous update
|
||||
System.arraycopy(buf, off, memory, memSize, 16 - memSize); |
||||
|
||||
v1 += readIntLE(memory, 0) * PRIME2; |
||||
v1 = rotateLeft(v1, 13); |
||||
v1 *= PRIME1; |
||||
|
||||
v2 += readIntLE(memory, 4) * PRIME2; |
||||
v2 = rotateLeft(v2, 13); |
||||
v2 *= PRIME1; |
||||
|
||||
v3 += readIntLE(memory, 8) * PRIME2; |
||||
v3 = rotateLeft(v3, 13); |
||||
v3 *= PRIME1; |
||||
|
||||
v4 += readIntLE(memory, 12) * PRIME2; |
||||
v4 = rotateLeft(v4, 13); |
||||
v4 *= PRIME1; |
||||
|
||||
off += 16 - memSize; |
||||
memSize = 0; |
||||
} |
||||
|
||||
{ |
||||
final int limit = end - 16; |
||||
int v1 = this.v1; |
||||
int v2 = this.v2; |
||||
int v3 = this.v3; |
||||
int v4 = this.v4; |
||||
|
||||
while (off <= limit) { |
||||
v1 += readIntLE(buf, off) * PRIME2; |
||||
v1 = rotateLeft(v1, 13); |
||||
v1 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v2 += readIntLE(buf, off) * PRIME2; |
||||
v2 = rotateLeft(v2, 13); |
||||
v2 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v3 += readIntLE(buf, off) * PRIME2; |
||||
v3 = rotateLeft(v3, 13); |
||||
v3 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v4 += readIntLE(buf, off) * PRIME2; |
||||
v4 = rotateLeft(v4, 13); |
||||
v4 *= PRIME1; |
||||
off += 4; |
||||
} |
||||
|
||||
this.v1 = v1; |
||||
this.v2 = v2; |
||||
this.v3 = v3; |
||||
this.v4 = v4; |
||||
} |
||||
|
||||
if (off < end) { |
||||
System.arraycopy(buf, off, memory, 0, end - off); |
||||
memSize = end - off; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,119 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import java.util.zip.Checksum; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
|
||||
|
||||
/** |
||||
* Streaming interface for {@link XXHash64}. |
||||
* <p> |
||||
* This API is compatible with the {@link XXHash64 block API} and the following |
||||
* code samples are equivalent: |
||||
* <pre class="prettyprint"> |
||||
* long hash(XXHashFactory xxhashFactory, byte[] buf, int off, int len, long seed) { |
||||
* return xxhashFactory.hash64().hash(buf, off, len, seed); |
||||
* } |
||||
* </pre> |
||||
* <pre class="prettyprint"> |
||||
* long hash(XXHashFactory xxhashFactory, byte[] buf, int off, int len, long seed) { |
||||
* StreamingXXHash64 sh64 = xxhashFactory.newStreamingHash64(seed); |
||||
* sh64.update(buf, off, len); |
||||
* return sh64.getValue(); |
||||
* } |
||||
* </pre> |
||||
* <p> |
||||
* Instances of this class are <b>not</b> thread-safe. |
||||
*/ |
||||
public abstract class StreamingXXHash64 { |
||||
|
||||
interface Factory { |
||||
|
||||
StreamingXXHash64 newStreamingHash(long seed); |
||||
|
||||
} |
||||
|
||||
final long seed; |
||||
|
||||
StreamingXXHash64(long seed) { |
||||
this.seed = seed; |
||||
} |
||||
|
||||
/** |
||||
* Returns the value of the checksum. |
||||
* |
||||
* @return the checksum |
||||
*/ |
||||
public abstract long getValue(); |
||||
|
||||
/** |
||||
* Updates the value of the hash with buf[off:off+len]. |
||||
* |
||||
* @param buf the input data |
||||
* @param off the start offset in buf |
||||
* @param len the number of bytes to hash |
||||
*/ |
||||
public abstract void update(byte[] buf, int off, int len); |
||||
|
||||
/** |
||||
* Resets this instance to the state it had right after instantiation. The |
||||
* seed remains unchanged. |
||||
*/ |
||||
public abstract void reset(); |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName() + "(seed=" + seed + ")"; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link Checksum} view of this instance. Modifications to the view |
||||
* will modify this instance too and vice-versa. |
||||
* |
||||
* @return the {@link Checksum} object representing this instance |
||||
*/ |
||||
public final Checksum asChecksum() { |
||||
return new Checksum() { |
||||
|
||||
@Override |
||||
public long getValue() { |
||||
return StreamingXXHash64.this.getValue(); |
||||
} |
||||
|
||||
@Override |
||||
public void reset() { |
||||
StreamingXXHash64.this.reset(); |
||||
} |
||||
|
||||
@Override |
||||
public void update(int b) { |
||||
StreamingXXHash64.this.update(new byte[] {(byte) b}, 0, 1); |
||||
} |
||||
|
||||
@Override |
||||
public void update(byte[] b, int off, int len) { |
||||
StreamingXXHash64.this.update(b, off, len); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return StreamingXXHash64.this.toString(); |
||||
} |
||||
|
||||
}; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,71 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
|
||||
final class StreamingXXHash64JNI extends StreamingXXHash64 { |
||||
|
||||
static class Factory implements StreamingXXHash64.Factory { |
||||
|
||||
public static final StreamingXXHash64.Factory INSTANCE = new Factory(); |
||||
|
||||
@Override |
||||
public StreamingXXHash64 newStreamingHash(long seed) { |
||||
return new StreamingXXHash64JNI(seed); |
||||
} |
||||
|
||||
} |
||||
|
||||
private long state; |
||||
|
||||
StreamingXXHash64JNI(long seed) { |
||||
super(seed); |
||||
state = XXHashJNI.XXH64_init(seed); |
||||
} |
||||
|
||||
private void checkState() { |
||||
if (state == 0) { |
||||
throw new AssertionError("Already finalized"); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void reset() { |
||||
checkState(); |
||||
XXHashJNI.XXH64_free(state); |
||||
state = XXHashJNI.XXH64_init(seed); |
||||
} |
||||
|
||||
@Override |
||||
public long getValue() { |
||||
checkState(); |
||||
return XXHashJNI.XXH64_digest(state); |
||||
} |
||||
|
||||
@Override |
||||
public void update(byte[] bytes, int off, int len) { |
||||
checkState(); |
||||
XXHashJNI.XXH64_update(state, bytes, off, len); |
||||
} |
||||
|
||||
@Override |
||||
protected void finalize() throws Throwable { |
||||
super.finalize(); |
||||
// free memory
|
||||
XXHashJNI.XXH64_free(state); |
||||
state = 0; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,166 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import static com.fr.third.net.jpountz.xxhash.XXHashConstants.*; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.*; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.checkRange; |
||||
import static java.lang.Long.rotateLeft; |
||||
|
||||
/** |
||||
* Streaming xxhash. |
||||
*/ |
||||
final class StreamingXXHash64JavaSafe extends AbstractStreamingXXHash64Java { |
||||
|
||||
static class Factory implements StreamingXXHash64.Factory { |
||||
|
||||
public static final StreamingXXHash64.Factory INSTANCE = new Factory(); |
||||
|
||||
@Override |
||||
public StreamingXXHash64 newStreamingHash(long seed) { |
||||
return new StreamingXXHash64JavaSafe(seed); |
||||
} |
||||
|
||||
} |
||||
|
||||
StreamingXXHash64JavaSafe(long seed) { |
||||
super(seed); |
||||
} |
||||
|
||||
@Override |
||||
public long getValue() { |
||||
long h64; |
||||
if (totalLen >= 32) { |
||||
long v1 = this.v1; |
||||
long v2 = this.v2; |
||||
long v3 = this.v3; |
||||
long v4 = this.v4; |
||||
|
||||
h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
|
||||
v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; |
||||
h64 = h64*PRIME64_1 + PRIME64_4; |
||||
|
||||
v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; |
||||
h64 = h64*PRIME64_1 + PRIME64_4; |
||||
|
||||
v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; |
||||
h64 = h64*PRIME64_1 + PRIME64_4; |
||||
|
||||
v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; |
||||
h64 = h64*PRIME64_1 + PRIME64_4; |
||||
} else { |
||||
h64 = seed + PRIME64_5; |
||||
} |
||||
|
||||
h64 += totalLen; |
||||
|
||||
int off = 0; |
||||
while (off <= memSize - 8) { |
||||
long k1 = readLongLE(memory, off); |
||||
k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; |
||||
h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; |
||||
off += 8; |
||||
} |
||||
|
||||
if (off <= memSize - 4) { |
||||
h64 ^= (readIntLE(memory, off) & 0xFFFFFFFFL) * PRIME64_1; |
||||
h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < memSize) { |
||||
h64 ^= (memory[off] & 0xFF) * PRIME64_5; |
||||
h64 = rotateLeft(h64, 11) * PRIME64_1; |
||||
++off; |
||||
} |
||||
|
||||
h64 ^= h64 >>> 33; |
||||
h64 *= PRIME64_2; |
||||
h64 ^= h64 >>> 29; |
||||
h64 *= PRIME64_3; |
||||
h64 ^= h64 >>> 32; |
||||
|
||||
return h64; |
||||
} |
||||
|
||||
@Override |
||||
public void update(byte[] buf, int off, int len) { |
||||
checkRange(buf, off, len); |
||||
|
||||
totalLen += len; |
||||
|
||||
if (memSize + len < 32) { // fill in tmp buffer
|
||||
System.arraycopy(buf, off, memory, memSize, len); |
||||
memSize += len; |
||||
return; |
||||
} |
||||
|
||||
final int end = off + len; |
||||
|
||||
if (memSize > 0) { // data left from previous update
|
||||
System.arraycopy(buf, off, memory, memSize, 32 - memSize); |
||||
|
||||
v1 += readLongLE(memory, 0) * PRIME64_2; |
||||
v1 = rotateLeft(v1, 31); |
||||
v1 *= PRIME64_1; |
||||
|
||||
v2 += readLongLE(memory, 8) * PRIME64_2; |
||||
v2 = rotateLeft(v2, 31); |
||||
v2 *= PRIME64_1; |
||||
|
||||
v3 += readLongLE(memory, 16) * PRIME64_2; |
||||
v3 = rotateLeft(v3, 31); |
||||
v3 *= PRIME64_1; |
||||
|
||||
v4 += readLongLE(memory, 24) * PRIME64_2; |
||||
v4 = rotateLeft(v4, 31); |
||||
v4 *= PRIME64_1; |
||||
|
||||
off += 32 - memSize; |
||||
memSize = 0; |
||||
} |
||||
|
||||
{ |
||||
final int limit = end - 32; |
||||
long v1 = this.v1; |
||||
long v2 = this.v2; |
||||
long v3 = this.v3; |
||||
long v4 = this.v4; |
||||
|
||||
while (off <= limit) { |
||||
v1 += readLongLE(buf, off) * PRIME64_2; |
||||
v1 = rotateLeft(v1, 31); |
||||
v1 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v2 += readLongLE(buf, off) * PRIME64_2; |
||||
v2 = rotateLeft(v2, 31); |
||||
v2 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v3 += readLongLE(buf, off) * PRIME64_2; |
||||
v3 = rotateLeft(v3, 31); |
||||
v3 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v4 += readLongLE(buf, off) * PRIME64_2; |
||||
v4 = rotateLeft(v4, 31); |
||||
v4 *= PRIME64_1; |
||||
off += 8; |
||||
} |
||||
|
||||
this.v1 = v1; |
||||
this.v2 = v2; |
||||
this.v3 = v3; |
||||
this.v4 = v4; |
||||
} |
||||
|
||||
if (off < end) { |
||||
System.arraycopy(buf, off, memory, 0, end - off); |
||||
memSize = end - off; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,166 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import static com.fr.third.net.jpountz.xxhash.XXHashConstants.*; |
||||
import static com.fr.third.net.jpountz.util.UnsafeUtils.*; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.checkRange; |
||||
import static java.lang.Long.rotateLeft; |
||||
|
||||
/** |
||||
* Streaming xxhash. |
||||
*/ |
||||
final class StreamingXXHash64JavaUnsafe extends AbstractStreamingXXHash64Java { |
||||
|
||||
static class Factory implements StreamingXXHash64.Factory { |
||||
|
||||
public static final StreamingXXHash64.Factory INSTANCE = new Factory(); |
||||
|
||||
@Override |
||||
public StreamingXXHash64 newStreamingHash(long seed) { |
||||
return new StreamingXXHash64JavaUnsafe(seed); |
||||
} |
||||
|
||||
} |
||||
|
||||
StreamingXXHash64JavaUnsafe(long seed) { |
||||
super(seed); |
||||
} |
||||
|
||||
@Override |
||||
public long getValue() { |
||||
long h64; |
||||
if (totalLen >= 32) { |
||||
long v1 = this.v1; |
||||
long v2 = this.v2; |
||||
long v3 = this.v3; |
||||
long v4 = this.v4; |
||||
|
||||
h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
|
||||
v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; |
||||
h64 = h64*PRIME64_1 + PRIME64_4; |
||||
|
||||
v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; |
||||
h64 = h64*PRIME64_1 + PRIME64_4; |
||||
|
||||
v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; |
||||
h64 = h64*PRIME64_1 + PRIME64_4; |
||||
|
||||
v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; |
||||
h64 = h64*PRIME64_1 + PRIME64_4; |
||||
} else { |
||||
h64 = seed + PRIME64_5; |
||||
} |
||||
|
||||
h64 += totalLen; |
||||
|
||||
int off = 0; |
||||
while (off <= memSize - 8) { |
||||
long k1 = readLongLE(memory, off); |
||||
k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; |
||||
h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; |
||||
off += 8; |
||||
} |
||||
|
||||
if (off <= memSize - 4) { |
||||
h64 ^= (readIntLE(memory, off) & 0xFFFFFFFFL) * PRIME64_1; |
||||
h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < memSize) { |
||||
h64 ^= (memory[off] & 0xFF) * PRIME64_5; |
||||
h64 = rotateLeft(h64, 11) * PRIME64_1; |
||||
++off; |
||||
} |
||||
|
||||
h64 ^= h64 >>> 33; |
||||
h64 *= PRIME64_2; |
||||
h64 ^= h64 >>> 29; |
||||
h64 *= PRIME64_3; |
||||
h64 ^= h64 >>> 32; |
||||
|
||||
return h64; |
||||
} |
||||
|
||||
@Override |
||||
public void update(byte[] buf, int off, int len) { |
||||
checkRange(buf, off, len); |
||||
|
||||
totalLen += len; |
||||
|
||||
if (memSize + len < 32) { // fill in tmp buffer
|
||||
System.arraycopy(buf, off, memory, memSize, len); |
||||
memSize += len; |
||||
return; |
||||
} |
||||
|
||||
final int end = off + len; |
||||
|
||||
if (memSize > 0) { // data left from previous update
|
||||
System.arraycopy(buf, off, memory, memSize, 32 - memSize); |
||||
|
||||
v1 += readLongLE(memory, 0) * PRIME64_2; |
||||
v1 = rotateLeft(v1, 31); |
||||
v1 *= PRIME64_1; |
||||
|
||||
v2 += readLongLE(memory, 8) * PRIME64_2; |
||||
v2 = rotateLeft(v2, 31); |
||||
v2 *= PRIME64_1; |
||||
|
||||
v3 += readLongLE(memory, 16) * PRIME64_2; |
||||
v3 = rotateLeft(v3, 31); |
||||
v3 *= PRIME64_1; |
||||
|
||||
v4 += readLongLE(memory, 24) * PRIME64_2; |
||||
v4 = rotateLeft(v4, 31); |
||||
v4 *= PRIME64_1; |
||||
|
||||
off += 32 - memSize; |
||||
memSize = 0; |
||||
} |
||||
|
||||
{ |
||||
final int limit = end - 32; |
||||
long v1 = this.v1; |
||||
long v2 = this.v2; |
||||
long v3 = this.v3; |
||||
long v4 = this.v4; |
||||
|
||||
while (off <= limit) { |
||||
v1 += readLongLE(buf, off) * PRIME64_2; |
||||
v1 = rotateLeft(v1, 31); |
||||
v1 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v2 += readLongLE(buf, off) * PRIME64_2; |
||||
v2 = rotateLeft(v2, 31); |
||||
v2 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v3 += readLongLE(buf, off) * PRIME64_2; |
||||
v3 = rotateLeft(v3, 31); |
||||
v3 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v4 += readLongLE(buf, off) * PRIME64_2; |
||||
v4 = rotateLeft(v4, 31); |
||||
v4 *= PRIME64_1; |
||||
off += 8; |
||||
} |
||||
|
||||
this.v1 = v1; |
||||
this.v2 = v2; |
||||
this.v3 = v3; |
||||
this.v4 = v4; |
||||
} |
||||
|
||||
if (off < end) { |
||||
System.arraycopy(buf, off, memory, 0, end - off); |
||||
memSize = end - off; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,71 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* A 32-bits hash. |
||||
* <p> |
||||
* Instances of this class are thread-safe. |
||||
*/ |
||||
public abstract class XXHash32 { |
||||
|
||||
/** |
||||
* Computes the 32-bits hash of <code>buf[off:off+len]</code> using seed |
||||
* <code>seed</code>. |
||||
* |
||||
* @param buf the input data |
||||
* @param off the start offset in buf |
||||
* @param len the number of bytes to hash |
||||
* @param seed the seed to use |
||||
* @return the hash value |
||||
*/ |
||||
public abstract int hash(byte[] buf, int off, int len, int seed); |
||||
|
||||
/** |
||||
* Computes the hash of the given slice of the {@link ByteBuffer}. |
||||
* {@link ByteBuffer#position() position} and {@link ByteBuffer#limit() limit} |
||||
* are not modified. |
||||
* |
||||
* @param buf the input data |
||||
* @param off the start offset in buf |
||||
* @param len the number of bytes to hash |
||||
* @param seed the seed to use |
||||
* @return the hash value |
||||
*/ |
||||
public abstract int hash(ByteBuffer buf, int off, int len, int seed); |
||||
|
||||
/** |
||||
* Computes the hash of the given {@link ByteBuffer}. The |
||||
* {@link ByteBuffer#position() position} is moved in order to reflect bytes |
||||
* which have been read. |
||||
* |
||||
* @param buf the input data |
||||
* @param seed the seed to use |
||||
* @return the hash value |
||||
*/ |
||||
public final int hash(ByteBuffer buf, int seed) { |
||||
final int hash = hash(buf, buf.position(), buf.remaining(), seed); |
||||
buf.position(buf.limit()); |
||||
return hash; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.checkRange; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.checkRange; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
final class XXHash32JNI extends XXHash32 { |
||||
|
||||
public static final XXHash32 INSTANCE = new XXHash32JNI(); |
||||
private static XXHash32 SAFE_INSTANCE; |
||||
|
||||
@Override |
||||
public int hash(byte[] buf, int off, int len, int seed) { |
||||
SafeUtils.checkRange(buf, off, len); |
||||
return XXHashJNI.XXH32(buf, off, len, seed); |
||||
} |
||||
|
||||
@Override |
||||
public int hash(ByteBuffer buf, int off, int len, int seed) { |
||||
if (buf.isDirect()) { |
||||
ByteBufferUtils.checkRange(buf, off, len); |
||||
return XXHashJNI.XXH32BB(buf, off, len, seed); |
||||
} else if (buf.hasArray()) { |
||||
return hash(buf.array(), off + buf.arrayOffset(), len, seed); |
||||
} else { |
||||
XXHash32 safeInstance = SAFE_INSTANCE; |
||||
if (safeInstance == null) { |
||||
safeInstance = SAFE_INSTANCE = XXHashFactory.safeInstance().hash32(); |
||||
} |
||||
return safeInstance.hash(buf, off, len, seed); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,154 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import static com.fr.third.net.jpountz.xxhash.XXHashConstants.*; |
||||
import static java.lang.Integer.rotateLeft; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
|
||||
/** |
||||
* {@link XXHash32} implementation. |
||||
*/ |
||||
final class XXHash32JavaSafe extends XXHash32 { |
||||
|
||||
public static final XXHash32 INSTANCE = new XXHash32JavaSafe(); |
||||
|
||||
@Override |
||||
public int hash(byte[] buf, int off, int len, int seed) { |
||||
|
||||
SafeUtils.checkRange(buf, off, len); |
||||
|
||||
final int end = off + len; |
||||
int h32; |
||||
|
||||
if (len >= 16) { |
||||
final int limit = end - 16; |
||||
int v1 = seed + PRIME1 + PRIME2; |
||||
int v2 = seed + PRIME2; |
||||
int v3 = seed + 0; |
||||
int v4 = seed - PRIME1; |
||||
do { |
||||
v1 += SafeUtils.readIntLE(buf, off) * PRIME2; |
||||
v1 = rotateLeft(v1, 13); |
||||
v1 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v2 += SafeUtils.readIntLE(buf, off) * PRIME2; |
||||
v2 = rotateLeft(v2, 13); |
||||
v2 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v3 += SafeUtils.readIntLE(buf, off) * PRIME2; |
||||
v3 = rotateLeft(v3, 13); |
||||
v3 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v4 += SafeUtils.readIntLE(buf, off) * PRIME2; |
||||
v4 = rotateLeft(v4, 13); |
||||
v4 *= PRIME1; |
||||
off += 4; |
||||
} while (off <= limit); |
||||
|
||||
h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
} else { |
||||
h32 = seed + PRIME5; |
||||
} |
||||
|
||||
h32 += len; |
||||
|
||||
while (off <= end - 4) { |
||||
h32 += SafeUtils.readIntLE(buf, off) * PRIME3; |
||||
h32 = rotateLeft(h32, 17) * PRIME4; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < end) { |
||||
h32 += (SafeUtils.readByte(buf, off) & 0xFF) * PRIME5; |
||||
h32 = rotateLeft(h32, 11) * PRIME1; |
||||
++off; |
||||
} |
||||
|
||||
h32 ^= h32 >>> 15; |
||||
h32 *= PRIME2; |
||||
h32 ^= h32 >>> 13; |
||||
h32 *= PRIME3; |
||||
h32 ^= h32 >>> 16; |
||||
|
||||
return h32; |
||||
} |
||||
|
||||
@Override |
||||
public int hash(ByteBuffer buf, int off, int len, int seed) { |
||||
|
||||
if (buf.hasArray()) { |
||||
return hash(buf.array(), off + buf.arrayOffset(), len, seed); |
||||
} |
||||
ByteBufferUtils.checkRange(buf, off, len); |
||||
buf = ByteBufferUtils.inLittleEndianOrder(buf); |
||||
|
||||
final int end = off + len; |
||||
int h32; |
||||
|
||||
if (len >= 16) { |
||||
final int limit = end - 16; |
||||
int v1 = seed + PRIME1 + PRIME2; |
||||
int v2 = seed + PRIME2; |
||||
int v3 = seed + 0; |
||||
int v4 = seed - PRIME1; |
||||
do { |
||||
v1 += ByteBufferUtils.readIntLE(buf, off) * PRIME2; |
||||
v1 = rotateLeft(v1, 13); |
||||
v1 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v2 += ByteBufferUtils.readIntLE(buf, off) * PRIME2; |
||||
v2 = rotateLeft(v2, 13); |
||||
v2 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v3 += ByteBufferUtils.readIntLE(buf, off) * PRIME2; |
||||
v3 = rotateLeft(v3, 13); |
||||
v3 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v4 += ByteBufferUtils.readIntLE(buf, off) * PRIME2; |
||||
v4 = rotateLeft(v4, 13); |
||||
v4 *= PRIME1; |
||||
off += 4; |
||||
} while (off <= limit); |
||||
|
||||
h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
} else { |
||||
h32 = seed + PRIME5; |
||||
} |
||||
|
||||
h32 += len; |
||||
|
||||
while (off <= end - 4) { |
||||
h32 += ByteBufferUtils.readIntLE(buf, off) * PRIME3; |
||||
h32 = rotateLeft(h32, 17) * PRIME4; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < end) { |
||||
h32 += (ByteBufferUtils.readByte(buf, off) & 0xFF) * PRIME5; |
||||
h32 = rotateLeft(h32, 11) * PRIME1; |
||||
++off; |
||||
} |
||||
|
||||
h32 ^= h32 >>> 15; |
||||
h32 *= PRIME2; |
||||
h32 ^= h32 >>> 13; |
||||
h32 *= PRIME3; |
||||
h32 ^= h32 >>> 16; |
||||
|
||||
return h32; |
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,154 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import static com.fr.third.net.jpountz.xxhash.XXHashConstants.*; |
||||
import static java.lang.Integer.rotateLeft; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.UnsafeUtils; |
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
|
||||
/** |
||||
* {@link XXHash32} implementation. |
||||
*/ |
||||
final class XXHash32JavaUnsafe extends XXHash32 { |
||||
|
||||
public static final XXHash32 INSTANCE = new XXHash32JavaUnsafe(); |
||||
|
||||
@Override |
||||
public int hash(byte[] buf, int off, int len, int seed) { |
||||
|
||||
UnsafeUtils.checkRange(buf, off, len); |
||||
|
||||
final int end = off + len; |
||||
int h32; |
||||
|
||||
if (len >= 16) { |
||||
final int limit = end - 16; |
||||
int v1 = seed + PRIME1 + PRIME2; |
||||
int v2 = seed + PRIME2; |
||||
int v3 = seed + 0; |
||||
int v4 = seed - PRIME1; |
||||
do { |
||||
v1 += UnsafeUtils.readIntLE(buf, off) * PRIME2; |
||||
v1 = rotateLeft(v1, 13); |
||||
v1 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v2 += UnsafeUtils.readIntLE(buf, off) * PRIME2; |
||||
v2 = rotateLeft(v2, 13); |
||||
v2 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v3 += UnsafeUtils.readIntLE(buf, off) * PRIME2; |
||||
v3 = rotateLeft(v3, 13); |
||||
v3 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v4 += UnsafeUtils.readIntLE(buf, off) * PRIME2; |
||||
v4 = rotateLeft(v4, 13); |
||||
v4 *= PRIME1; |
||||
off += 4; |
||||
} while (off <= limit); |
||||
|
||||
h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
} else { |
||||
h32 = seed + PRIME5; |
||||
} |
||||
|
||||
h32 += len; |
||||
|
||||
while (off <= end - 4) { |
||||
h32 += UnsafeUtils.readIntLE(buf, off) * PRIME3; |
||||
h32 = rotateLeft(h32, 17) * PRIME4; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < end) { |
||||
h32 += (UnsafeUtils.readByte(buf, off) & 0xFF) * PRIME5; |
||||
h32 = rotateLeft(h32, 11) * PRIME1; |
||||
++off; |
||||
} |
||||
|
||||
h32 ^= h32 >>> 15; |
||||
h32 *= PRIME2; |
||||
h32 ^= h32 >>> 13; |
||||
h32 *= PRIME3; |
||||
h32 ^= h32 >>> 16; |
||||
|
||||
return h32; |
||||
} |
||||
|
||||
@Override |
||||
public int hash(ByteBuffer buf, int off, int len, int seed) { |
||||
|
||||
if (buf.hasArray()) { |
||||
return hash(buf.array(), off + buf.arrayOffset(), len, seed); |
||||
} |
||||
ByteBufferUtils.checkRange(buf, off, len); |
||||
buf = ByteBufferUtils.inLittleEndianOrder(buf); |
||||
|
||||
final int end = off + len; |
||||
int h32; |
||||
|
||||
if (len >= 16) { |
||||
final int limit = end - 16; |
||||
int v1 = seed + PRIME1 + PRIME2; |
||||
int v2 = seed + PRIME2; |
||||
int v3 = seed + 0; |
||||
int v4 = seed - PRIME1; |
||||
do { |
||||
v1 += ByteBufferUtils.readIntLE(buf, off) * PRIME2; |
||||
v1 = rotateLeft(v1, 13); |
||||
v1 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v2 += ByteBufferUtils.readIntLE(buf, off) * PRIME2; |
||||
v2 = rotateLeft(v2, 13); |
||||
v2 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v3 += ByteBufferUtils.readIntLE(buf, off) * PRIME2; |
||||
v3 = rotateLeft(v3, 13); |
||||
v3 *= PRIME1; |
||||
off += 4; |
||||
|
||||
v4 += ByteBufferUtils.readIntLE(buf, off) * PRIME2; |
||||
v4 = rotateLeft(v4, 13); |
||||
v4 *= PRIME1; |
||||
off += 4; |
||||
} while (off <= limit); |
||||
|
||||
h32 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
} else { |
||||
h32 = seed + PRIME5; |
||||
} |
||||
|
||||
h32 += len; |
||||
|
||||
while (off <= end - 4) { |
||||
h32 += ByteBufferUtils.readIntLE(buf, off) * PRIME3; |
||||
h32 = rotateLeft(h32, 17) * PRIME4; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < end) { |
||||
h32 += (ByteBufferUtils.readByte(buf, off) & 0xFF) * PRIME5; |
||||
h32 = rotateLeft(h32, 11) * PRIME1; |
||||
++off; |
||||
} |
||||
|
||||
h32 ^= h32 >>> 15; |
||||
h32 *= PRIME2; |
||||
h32 ^= h32 >>> 13; |
||||
h32 *= PRIME3; |
||||
h32 ^= h32 >>> 16; |
||||
|
||||
return h32; |
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,71 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
/** |
||||
* A 64-bits hash. |
||||
* <p> |
||||
* Instances of this class are thread-safe. |
||||
*/ |
||||
public abstract class XXHash64 { |
||||
|
||||
/** |
||||
* Computes the 64-bits hash of <code>buf[off:off+len]</code> using seed |
||||
* <code>seed</code>. |
||||
* |
||||
* @param buf the input data |
||||
* @param off the start offset in buf |
||||
* @param len the number of bytes to hash |
||||
* @param seed the seed to use |
||||
* @return the hash value |
||||
*/ |
||||
public abstract long hash(byte[] buf, int off, int len, long seed); |
||||
|
||||
/** |
||||
* Computes the hash of the given slice of the {@link ByteBuffer}. |
||||
* {@link ByteBuffer#position() position} and {@link ByteBuffer#limit() limit} |
||||
* are not modified. |
||||
* |
||||
* @param buf the input data |
||||
* @param off the start offset in buf |
||||
* @param len the number of bytes to hash |
||||
* @param seed the seed to use |
||||
* @return the hash value |
||||
*/ |
||||
public abstract long hash(ByteBuffer buf, int off, int len, long seed); |
||||
|
||||
/** |
||||
* Computes the hash of the given {@link ByteBuffer}. The |
||||
* {@link ByteBuffer#position() position} is moved in order to reflect bytes |
||||
* which have been read. |
||||
* |
||||
* @param buf the input data |
||||
* @param seed the seed to use |
||||
* @return the hash value |
||||
*/ |
||||
public final long hash(ByteBuffer buf, long seed) { |
||||
final long hash = hash(buf, buf.position(), buf.remaining(), seed); |
||||
buf.position(buf.limit()); |
||||
return hash; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,52 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
|
||||
import static com.fr.third.net.jpountz.util.ByteBufferUtils.checkRange; |
||||
import static com.fr.third.net.jpountz.util.SafeUtils.checkRange; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
final class XXHash64JNI extends XXHash64 { |
||||
|
||||
public static final XXHash64 INSTANCE = new XXHash64JNI(); |
||||
private static XXHash64 SAFE_INSTANCE; |
||||
|
||||
@Override |
||||
public long hash(byte[] buf, int off, int len, long seed) { |
||||
SafeUtils.checkRange(buf, off, len); |
||||
return XXHashJNI.XXH64(buf, off, len, seed); |
||||
} |
||||
|
||||
@Override |
||||
public long hash(ByteBuffer buf, int off, int len, long seed) { |
||||
if (buf.isDirect()) { |
||||
ByteBufferUtils.checkRange(buf, off, len); |
||||
return XXHashJNI.XXH64BB(buf, off, len, seed); |
||||
} else if (buf.hasArray()) { |
||||
return hash(buf.array(), off + buf.arrayOffset(), len, seed); |
||||
} else { |
||||
XXHash64 safeInstance = SAFE_INSTANCE; |
||||
if (safeInstance == null) { |
||||
safeInstance = SAFE_INSTANCE = XXHashFactory.safeInstance().hash64(); |
||||
} |
||||
return safeInstance.hash(buf, off, len, seed); |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,192 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import static com.fr.third.net.jpountz.xxhash.XXHashConstants.*; |
||||
import static java.lang.Long.rotateLeft; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.SafeUtils; |
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
|
||||
/** |
||||
* {@link XXHash64} implementation. |
||||
*/ |
||||
final class XXHash64JavaSafe extends XXHash64 { |
||||
|
||||
public static final XXHash64 INSTANCE = new XXHash64JavaSafe(); |
||||
|
||||
@Override |
||||
public long hash(byte[] buf, int off, int len, long seed) { |
||||
|
||||
SafeUtils.checkRange(buf, off, len); |
||||
|
||||
final int end = off + len; |
||||
long h64; |
||||
|
||||
if (len >= 32) { |
||||
final int limit = end - 32; |
||||
long v1 = seed + PRIME64_1 + PRIME64_2; |
||||
long v2 = seed + PRIME64_2; |
||||
long v3 = seed + 0; |
||||
long v4 = seed - PRIME64_1; |
||||
do { |
||||
v1 += SafeUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v1 = rotateLeft(v1, 31); |
||||
v1 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v2 += SafeUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v2 = rotateLeft(v2, 31); |
||||
v2 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v3 += SafeUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v3 = rotateLeft(v3, 31); |
||||
v3 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v4 += SafeUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v4 = rotateLeft(v4, 31); |
||||
v4 *= PRIME64_1; |
||||
off += 8; |
||||
} while (off <= limit); |
||||
|
||||
h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
|
||||
v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
} else { |
||||
h64 = seed + PRIME64_5; |
||||
} |
||||
|
||||
h64 += len; |
||||
|
||||
while (off <= end - 8) { |
||||
long k1 = SafeUtils.readLongLE(buf, off); |
||||
k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; |
||||
h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; |
||||
off += 8; |
||||
} |
||||
|
||||
if (off <= end - 4) { |
||||
h64 ^= (SafeUtils.readIntLE(buf, off) & 0xFFFFFFFFL) * PRIME64_1; |
||||
h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < end) { |
||||
h64 ^= (SafeUtils.readByte(buf, off) & 0xFF) * PRIME64_5; |
||||
h64 = rotateLeft(h64, 11) * PRIME64_1; |
||||
++off; |
||||
} |
||||
|
||||
h64 ^= h64 >>> 33; |
||||
h64 *= PRIME64_2; |
||||
h64 ^= h64 >>> 29; |
||||
h64 *= PRIME64_3; |
||||
h64 ^= h64 >>> 32; |
||||
|
||||
return h64; |
||||
} |
||||
|
||||
@Override |
||||
public long hash(ByteBuffer buf, int off, int len, long seed) { |
||||
|
||||
if (buf.hasArray()) { |
||||
return hash(buf.array(), off + buf.arrayOffset(), len, seed); |
||||
} |
||||
ByteBufferUtils.checkRange(buf, off, len); |
||||
buf = ByteBufferUtils.inLittleEndianOrder(buf); |
||||
|
||||
final int end = off + len; |
||||
long h64; |
||||
|
||||
if (len >= 32) { |
||||
final int limit = end - 32; |
||||
long v1 = seed + PRIME64_1 + PRIME64_2; |
||||
long v2 = seed + PRIME64_2; |
||||
long v3 = seed + 0; |
||||
long v4 = seed - PRIME64_1; |
||||
do { |
||||
v1 += ByteBufferUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v1 = rotateLeft(v1, 31); |
||||
v1 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v2 += ByteBufferUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v2 = rotateLeft(v2, 31); |
||||
v2 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v3 += ByteBufferUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v3 = rotateLeft(v3, 31); |
||||
v3 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v4 += ByteBufferUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v4 = rotateLeft(v4, 31); |
||||
v4 *= PRIME64_1; |
||||
off += 8; |
||||
} while (off <= limit); |
||||
|
||||
h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
|
||||
v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
} else { |
||||
h64 = seed + PRIME64_5; |
||||
} |
||||
|
||||
h64 += len; |
||||
|
||||
while (off <= end - 8) { |
||||
long k1 = ByteBufferUtils.readLongLE(buf, off); |
||||
k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; |
||||
h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; |
||||
off += 8; |
||||
} |
||||
|
||||
if (off <= end - 4) { |
||||
h64 ^= (ByteBufferUtils.readIntLE(buf, off) & 0xFFFFFFFFL) * PRIME64_1; |
||||
h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < end) { |
||||
h64 ^= (ByteBufferUtils.readByte(buf, off) & 0xFF) * PRIME64_5; |
||||
h64 = rotateLeft(h64, 11) * PRIME64_1; |
||||
++off; |
||||
} |
||||
|
||||
h64 ^= h64 >>> 33; |
||||
h64 *= PRIME64_2; |
||||
h64 ^= h64 >>> 29; |
||||
h64 *= PRIME64_3; |
||||
h64 ^= h64 >>> 32; |
||||
|
||||
return h64; |
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,192 @@
|
||||
// Auto-generated: DO NOT EDIT
|
||||
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
import static com.fr.third.net.jpountz.xxhash.XXHashConstants.*; |
||||
import static java.lang.Long.rotateLeft; |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.UnsafeUtils; |
||||
import com.fr.third.net.jpountz.util.ByteBufferUtils; |
||||
|
||||
/** |
||||
* {@link XXHash64} implementation. |
||||
*/ |
||||
final class XXHash64JavaUnsafe extends XXHash64 { |
||||
|
||||
public static final XXHash64 INSTANCE = new XXHash64JavaUnsafe(); |
||||
|
||||
@Override |
||||
public long hash(byte[] buf, int off, int len, long seed) { |
||||
|
||||
UnsafeUtils.checkRange(buf, off, len); |
||||
|
||||
final int end = off + len; |
||||
long h64; |
||||
|
||||
if (len >= 32) { |
||||
final int limit = end - 32; |
||||
long v1 = seed + PRIME64_1 + PRIME64_2; |
||||
long v2 = seed + PRIME64_2; |
||||
long v3 = seed + 0; |
||||
long v4 = seed - PRIME64_1; |
||||
do { |
||||
v1 += UnsafeUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v1 = rotateLeft(v1, 31); |
||||
v1 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v2 += UnsafeUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v2 = rotateLeft(v2, 31); |
||||
v2 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v3 += UnsafeUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v3 = rotateLeft(v3, 31); |
||||
v3 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v4 += UnsafeUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v4 = rotateLeft(v4, 31); |
||||
v4 *= PRIME64_1; |
||||
off += 8; |
||||
} while (off <= limit); |
||||
|
||||
h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
|
||||
v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
} else { |
||||
h64 = seed + PRIME64_5; |
||||
} |
||||
|
||||
h64 += len; |
||||
|
||||
while (off <= end - 8) { |
||||
long k1 = UnsafeUtils.readLongLE(buf, off); |
||||
k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; |
||||
h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; |
||||
off += 8; |
||||
} |
||||
|
||||
if (off <= end - 4) { |
||||
h64 ^= (UnsafeUtils.readIntLE(buf, off) & 0xFFFFFFFFL) * PRIME64_1; |
||||
h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < end) { |
||||
h64 ^= (UnsafeUtils.readByte(buf, off) & 0xFF) * PRIME64_5; |
||||
h64 = rotateLeft(h64, 11) * PRIME64_1; |
||||
++off; |
||||
} |
||||
|
||||
h64 ^= h64 >>> 33; |
||||
h64 *= PRIME64_2; |
||||
h64 ^= h64 >>> 29; |
||||
h64 *= PRIME64_3; |
||||
h64 ^= h64 >>> 32; |
||||
|
||||
return h64; |
||||
} |
||||
|
||||
@Override |
||||
public long hash(ByteBuffer buf, int off, int len, long seed) { |
||||
|
||||
if (buf.hasArray()) { |
||||
return hash(buf.array(), off + buf.arrayOffset(), len, seed); |
||||
} |
||||
ByteBufferUtils.checkRange(buf, off, len); |
||||
buf = ByteBufferUtils.inLittleEndianOrder(buf); |
||||
|
||||
final int end = off + len; |
||||
long h64; |
||||
|
||||
if (len >= 32) { |
||||
final int limit = end - 32; |
||||
long v1 = seed + PRIME64_1 + PRIME64_2; |
||||
long v2 = seed + PRIME64_2; |
||||
long v3 = seed + 0; |
||||
long v4 = seed - PRIME64_1; |
||||
do { |
||||
v1 += ByteBufferUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v1 = rotateLeft(v1, 31); |
||||
v1 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v2 += ByteBufferUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v2 = rotateLeft(v2, 31); |
||||
v2 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v3 += ByteBufferUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v3 = rotateLeft(v3, 31); |
||||
v3 *= PRIME64_1; |
||||
off += 8; |
||||
|
||||
v4 += ByteBufferUtils.readLongLE(buf, off) * PRIME64_2; |
||||
v4 = rotateLeft(v4, 31); |
||||
v4 *= PRIME64_1; |
||||
off += 8; |
||||
} while (off <= limit); |
||||
|
||||
h64 = rotateLeft(v1, 1) + rotateLeft(v2, 7) + rotateLeft(v3, 12) + rotateLeft(v4, 18); |
||||
|
||||
v1 *= PRIME64_2; v1 = rotateLeft(v1, 31); v1 *= PRIME64_1; h64 ^= v1; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v2 *= PRIME64_2; v2 = rotateLeft(v2, 31); v2 *= PRIME64_1; h64 ^= v2; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v3 *= PRIME64_2; v3 = rotateLeft(v3, 31); v3 *= PRIME64_1; h64 ^= v3; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
|
||||
v4 *= PRIME64_2; v4 = rotateLeft(v4, 31); v4 *= PRIME64_1; h64 ^= v4; |
||||
h64 = h64 * PRIME64_1 + PRIME64_4; |
||||
} else { |
||||
h64 = seed + PRIME64_5; |
||||
} |
||||
|
||||
h64 += len; |
||||
|
||||
while (off <= end - 8) { |
||||
long k1 = ByteBufferUtils.readLongLE(buf, off); |
||||
k1 *= PRIME64_2; k1 = rotateLeft(k1, 31); k1 *= PRIME64_1; h64 ^= k1; |
||||
h64 = rotateLeft(h64, 27) * PRIME64_1 + PRIME64_4; |
||||
off += 8; |
||||
} |
||||
|
||||
if (off <= end - 4) { |
||||
h64 ^= (ByteBufferUtils.readIntLE(buf, off) & 0xFFFFFFFFL) * PRIME64_1; |
||||
h64 = rotateLeft(h64, 23) * PRIME64_2 + PRIME64_3; |
||||
off += 4; |
||||
} |
||||
|
||||
while (off < end) { |
||||
h64 ^= (ByteBufferUtils.readByte(buf, off) & 0xFF) * PRIME64_5; |
||||
h64 = rotateLeft(h64, 11) * PRIME64_1; |
||||
++off; |
||||
} |
||||
|
||||
h64 ^= h64 >>> 33; |
||||
h64 *= PRIME64_2; |
||||
h64 ^= h64 >>> 29; |
||||
h64 *= PRIME64_3; |
||||
h64 ^= h64 >>> 32; |
||||
|
||||
return h64; |
||||
} |
||||
|
||||
|
||||
} |
||||
|
@ -0,0 +1,31 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
enum XXHashConstants { |
||||
; |
||||
|
||||
static final int PRIME1 = -1640531535; |
||||
static final int PRIME2 = -2048144777; |
||||
static final int PRIME3 = -1028477379; |
||||
static final int PRIME4 = 668265263; |
||||
static final int PRIME5 = 374761393; |
||||
|
||||
static final long PRIME64_1 = -7046029288634856825L; //11400714785074694791
|
||||
static final long PRIME64_2 = -4417276706812531889L; //14029467366897019727
|
||||
static final long PRIME64_3 = 1609587929392839161L; |
||||
static final long PRIME64_4 = -8796714831421723037L; //9650029242287828579
|
||||
static final long PRIME64_5 = 2870177450012600261L; |
||||
} |
@ -0,0 +1,257 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.lang.reflect.Field; |
||||
import java.util.Random; |
||||
|
||||
import com.fr.third.net.jpountz.util.Native; |
||||
import com.fr.third.net.jpountz.util.Utils; |
||||
|
||||
/** |
||||
* Entry point to get {@link XXHash32} and {@link StreamingXXHash32} instances. |
||||
* <p> |
||||
* This class has 3 instances<ul> |
||||
* <li>a {@link #nativeInstance() native} instance which is a JNI binding to |
||||
* <a href="http://code.google.com/p/xxhash/">the original LZ4 C implementation</a>. |
||||
* <li>a {@link #safeInstance() safe Java} instance which is a pure Java port |
||||
* of the original C library,</li> |
||||
* <li>an {@link #unsafeInstance() unsafe Java} instance which is a Java port |
||||
* using the unofficial {@link sun.misc.Unsafe} API. |
||||
* </ul> |
||||
* <p> |
||||
* Only the {@link #safeInstance() safe instance} is guaranteed to work on your |
||||
* JVM, as a consequence it is advised to use the {@link #fastestInstance()} or |
||||
* {@link #fastestJavaInstance()} to pull a {@link XXHashFactory} instance. |
||||
* <p> |
||||
* All methods from this class are very costly, so you should get an instance |
||||
* once, and then reuse it whenever possible. This is typically done by storing |
||||
* a {@link XXHashFactory} instance in a static field. |
||||
*/ |
||||
public final class XXHashFactory { |
||||
|
||||
private static XXHashFactory instance(String impl) { |
||||
try { |
||||
return new XXHashFactory(impl); |
||||
} catch (Exception e) { |
||||
throw new AssertionError(e); |
||||
} |
||||
} |
||||
|
||||
private static XXHashFactory NATIVE_INSTANCE, |
||||
JAVA_UNSAFE_INSTANCE, |
||||
JAVA_SAFE_INSTANCE; |
||||
|
||||
/** |
||||
* Returns a {@link XXHashFactory} that returns {@link XXHash32} instances that |
||||
* are native bindings to the original C API. |
||||
* <p> |
||||
* Please note that this instance has some traps you should be aware of:<ol> |
||||
* <li>Upon loading this instance, files will be written to the temporary |
||||
* directory of the system. Although these files are supposed to be deleted |
||||
* when the JVM exits, they might remain on systems that don't support |
||||
* removal of files being used such as Windows. |
||||
* <li>The instance can only be loaded once per JVM. This can be a problem |
||||
* if your application uses multiple class loaders (such as most servlet |
||||
* containers): this instance will only be available to the children of the |
||||
* class loader which has loaded it. As a consequence, it is advised to |
||||
* either not use this instance in webapps or to put this library in the lib |
||||
* directory of your servlet container so that it is loaded by the system |
||||
* class loader. |
||||
* </ol> |
||||
* |
||||
* @return a {@link XXHashFactory} that returns {@link XXHash32} instances that |
||||
* are native bindings to the original C API. |
||||
*/ |
||||
public static synchronized XXHashFactory nativeInstance() { |
||||
if (NATIVE_INSTANCE == null) { |
||||
NATIVE_INSTANCE = instance("JNI"); |
||||
} |
||||
return NATIVE_INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link XXHashFactory} that returns {@link XXHash32} instances that |
||||
* are written with Java's official API. |
||||
* |
||||
* @return a {@link XXHashFactory} that returns {@link XXHash32} instances that |
||||
* are written with Java's official API. |
||||
*/ |
||||
public static synchronized XXHashFactory safeInstance() { |
||||
if (JAVA_SAFE_INSTANCE == null) { |
||||
JAVA_SAFE_INSTANCE = instance("JavaSafe"); |
||||
} |
||||
return JAVA_SAFE_INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link XXHashFactory} that returns {@link XXHash32} instances that |
||||
* may use {@link sun.misc.Unsafe} to speed up hashing. |
||||
* |
||||
* @return a {@link XXHashFactory} that returns {@link XXHash32} instances that |
||||
* may use {@link sun.misc.Unsafe} to speed up hashing. |
||||
*/ |
||||
public static synchronized XXHashFactory unsafeInstance() { |
||||
if (JAVA_UNSAFE_INSTANCE == null) { |
||||
JAVA_UNSAFE_INSTANCE = instance("JavaUnsafe"); |
||||
} |
||||
return JAVA_UNSAFE_INSTANCE; |
||||
} |
||||
|
||||
/** |
||||
* Returns the fastest available {@link XXHashFactory} instance which does not |
||||
* rely on JNI bindings. It first tries to load the |
||||
* {@link #unsafeInstance() unsafe instance}, and then the |
||||
* {@link #safeInstance() safe Java instance} if the JVM doesn't have a |
||||
* working {@link sun.misc.Unsafe}. |
||||
* |
||||
* @return the fastest available {@link XXHashFactory} instance which does not |
||||
* rely on JNI bindings. |
||||
*/ |
||||
public static XXHashFactory fastestJavaInstance() { |
||||
if (Utils.isUnalignedAccessAllowed()) { |
||||
try { |
||||
return unsafeInstance(); |
||||
} catch (Throwable t) { |
||||
return safeInstance(); |
||||
} |
||||
} else { |
||||
return safeInstance(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the fastest available {@link XXHashFactory} instance. If the class
|
||||
* loader is the system class loader and if the |
||||
* {@link #nativeInstance() native instance} loads successfully, then the |
||||
* {@link #nativeInstance() native instance} is returned, otherwise the |
||||
* {@link #fastestJavaInstance() fastest Java instance} is returned. |
||||
* <p> |
||||
* Please read {@link #nativeInstance() javadocs of nativeInstance()} before |
||||
* using this method. |
||||
* |
||||
* @return the fastest available {@link XXHashFactory} instance. |
||||
*/ |
||||
public static XXHashFactory fastestInstance() { |
||||
if (Native.isLoaded() |
||||
|| Native.class.getClassLoader() == ClassLoader.getSystemClassLoader()) { |
||||
try { |
||||
return nativeInstance(); |
||||
} catch (Throwable t) { |
||||
return fastestJavaInstance(); |
||||
} |
||||
} else { |
||||
return fastestJavaInstance(); |
||||
} |
||||
} |
||||
|
||||
@SuppressWarnings("unchecked") |
||||
private static <T> T classInstance(String cls) throws NoSuchFieldException, SecurityException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException { |
||||
ClassLoader loader = XXHashFactory.class.getClassLoader(); |
||||
loader = loader == null ? ClassLoader.getSystemClassLoader() : loader; |
||||
final Class<?> c = loader.loadClass(cls); |
||||
Field f = c.getField("INSTANCE"); |
||||
return (T) f.get(null); |
||||
} |
||||
|
||||
private final String impl; |
||||
private final XXHash32 hash32; |
||||
private final XXHash64 hash64; |
||||
private final StreamingXXHash32.Factory streamingHash32Factory; |
||||
private final StreamingXXHash64.Factory streamingHash64Factory; |
||||
|
||||
private XXHashFactory(String impl) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { |
||||
this.impl = impl; |
||||
hash32 = classInstance("XXHash32" + impl); |
||||
streamingHash32Factory = classInstance("StreamingXXHash32" + impl + "$Factory"); |
||||
hash64 = classInstance("XXHash64" + impl); |
||||
streamingHash64Factory = classInstance("StreamingXXHash64" + impl + "$Factory"); |
||||
|
||||
// make sure it can run
|
||||
final byte[] bytes = new byte[100]; |
||||
final Random random = new Random(); |
||||
random.nextBytes(bytes); |
||||
final int seed = random.nextInt(); |
||||
|
||||
final int h1 = hash32.hash(bytes, 0, bytes.length, seed); |
||||
final StreamingXXHash32 streamingHash32 = newStreamingHash32(seed); |
||||
streamingHash32.update(bytes, 0, bytes.length); |
||||
final int h2 = streamingHash32.getValue(); |
||||
final long h3 = hash64.hash(bytes, 0, bytes.length, seed); |
||||
final StreamingXXHash64 streamingHash64 = newStreamingHash64(seed); |
||||
streamingHash64.update(bytes, 0, bytes.length); |
||||
final long h4 = streamingHash64.getValue(); |
||||
if (h1 != h2) { |
||||
throw new AssertionError(); |
||||
} |
||||
if (h3 != h4) { |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link XXHash32} instance. |
||||
* |
||||
* @return a {@link XXHash32} instance. |
||||
*/ |
||||
public XXHash32 hash32() { |
||||
return hash32; |
||||
} |
||||
|
||||
/** |
||||
* Returns a {@link XXHash64} instance. |
||||
* |
||||
* @return a {@link XXHash64} instance. |
||||
*/ |
||||
public XXHash64 hash64() { |
||||
return hash64; |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@link StreamingXXHash32} instance. |
||||
* |
||||
* @param seed the seed to use |
||||
* @return a {@link StreamingXXHash32} instance |
||||
*/ |
||||
public StreamingXXHash32 newStreamingHash32(int seed) { |
||||
return streamingHash32Factory.newStreamingHash(seed); |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@link StreamingXXHash64} instance. |
||||
* |
||||
* @param seed the seed to use |
||||
* @return a {@link StreamingXXHash64} instance |
||||
*/ |
||||
public StreamingXXHash64 newStreamingHash64(long seed) { |
||||
return streamingHash64Factory.newStreamingHash(seed); |
||||
} |
||||
|
||||
/** |
||||
* Prints the fastest instance. |
||||
* |
||||
* @param args no argument required |
||||
*/ |
||||
public static void main(String[] args) { |
||||
System.out.println("Fastest instance is " + fastestInstance()); |
||||
System.out.println("Fastest Java instance is " + fastestJavaInstance()); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName() + ":" + impl; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,43 @@
|
||||
package com.fr.third.net.jpountz.xxhash; |
||||
|
||||
/* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
import java.nio.ByteBuffer; |
||||
|
||||
import com.fr.third.net.jpountz.util.Native; |
||||
|
||||
enum XXHashJNI { |
||||
; |
||||
|
||||
static { |
||||
Native.load(); |
||||
init(); |
||||
} |
||||
|
||||
private static native void init(); |
||||
static native int XXH32(byte[] input, int offset, int len, int seed); |
||||
static native int XXH32BB(ByteBuffer input, int offset, int len, int seed); |
||||
static native long XXH32_init(int seed); |
||||
static native void XXH32_update(long state, byte[] input, int offset, int len); |
||||
static native int XXH32_digest(long state); |
||||
static native void XXH32_free(long state); |
||||
|
||||
static native long XXH64(byte[] input, int offset, int len, long seed); |
||||
static native long XXH64BB(ByteBuffer input, int offset, int len, long seed); |
||||
static native long XXH64_init(long seed); |
||||
static native void XXH64_update(long state, byte[] input, int offset, int len); |
||||
static native long XXH64_digest(long state); |
||||
static native void XXH64_free(long state); |
||||
} |
@ -0,0 +1,65 @@
|
||||
<!doctype html public "-//w3c//dtd html 4.0 transitional//en"> |
||||
<!-- |
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. |
||||
--> |
||||
<html> |
||||
<head> |
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
||||
</head> |
||||
<body> |
||||
<p>xxhash hashing. This package supports both block hashing via |
||||
{@link net.jpountz.xxhash.XXHash32} and streaming hashing via |
||||
{@link net.jpountz.xxhash.StreamingXXHash32}. Have a look at |
||||
{@link net.jpountz.xxhash.XXHashFactory} to know how to get instances of these |
||||
interfaces.</p> |
||||
|
||||
<p>Streaming hashing is a little slower but doesn't require to load the whole |
||||
stream into memory.</p> |
||||
|
||||
<p>Sample block usage:</p> |
||||
|
||||
<pre class="prettyprint"> |
||||
XXHashFactory factory = XXHashFactory.fastestInstance(); |
||||
|
||||
byte[] data = "12345345234572".getBytes("UTF-8"); |
||||
|
||||
XXHash32 hash32 = factory.hash32(); |
||||
int seed = 0x9747b28c; // used to initialize the hash value, use whatever |
||||
// value you want, but always the same |
||||
int hash = hash32.hash(data, 0, data.length, seed); |
||||
</pre> |
||||
|
||||
<p>Sample streaming usage:</p> |
||||
|
||||
<pre class="prettyprint"> |
||||
XXHashFactory factory = XXHashFactory.fastestInstance(); |
||||
|
||||
byte[] data = "12345345234572".getBytes("UTF-8"); |
||||
ByteArrayInputStream in = new ByteArrayInputStream(data); |
||||
|
||||
int seed = 0x9747b28c; // used to initialize the hash value, use whatever |
||||
// value you want, but always the same |
||||
StreamingXXHash32 hash32 = factory.newStreamingHash32(seed); |
||||
byte[] buf = new byte[8]; // for real-world usage, use a larger buffer, like 8192 bytes |
||||
for (;;) { |
||||
int read = in.read(buf); |
||||
if (read == -1) { |
||||
break; |
||||
} |
||||
hash32.update(buf, 0, read); |
||||
} |
||||
int hash = hash32.getValue(); |
||||
</pre> |
||||
|
||||
</body> |
||||
</html> |
Loading…
Reference in new issue