com.github.luben.zstd.ZstdInputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zstd-jni Show documentation
Show all versions of zstd-jni Show documentation
JNI bindings for Zstd native library that provides fast and high compression lossless algorithm for Java and all JVM languages.
package com.github.luben.zstd;
import java.io.InputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.lang.IndexOutOfBoundsException;
import com.github.luben.zstd.util.Native;
import com.github.luben.zstd.Zstd;
/**
* InputStream filter that decompresses the data provided
* by the underlying InputStream using Zstd compression.
*
* It does not support mark/reset methods
*
*/
public class ZstdInputStream extends FilterInputStream {
static {
Native.load();
}
// Opaque pointer to Zstd context object
private long stream;
private long dstPos = 0;
private long srcPos = 0;
private long srcSize = 0;
private byte[] src = null;
private static final int srcBuffSize = (int) recommendedDInSize();
private boolean isContinuous = false;
private boolean frameFinished = true;
private boolean isClosed = false;
/* JNI methods */
private static native long recommendedDInSize();
private static native long recommendedDOutSize();
private static native long createDStream();
private static native int freeDStream(long stream);
private native int initDStream(long stream);
private native int decompressStream(long stream, byte[] dst, int dst_size, byte[] src, int src_size);
// The main constuctor / legacy version dispatcher
public ZstdInputStream(InputStream inStream) throws IOException {
// FilterInputStream constructor
super(inStream);
// allocate input buffer with max frame header size
src = new byte[srcBuffSize];
if (src == null) {
throw new IOException("Error allocating the input buffer of size " + srcBuffSize);
}
stream = createDStream();
int size = initDStream(stream);
if (Zstd.isError(size)) {
throw new IOException("Decompression error: " + Zstd.getErrorName(size));
}
}
/**
* Don't break on unfinished frames
*
* Use case: decompressing files that are not
* yet finished writing and compressing
*/
public ZstdInputStream setContinuous(boolean b) {
isContinuous = b;
return this;
}
public boolean getContinuous() {
return this.isContinuous;
}
public int read(byte[] dst, int offset, int len) throws IOException {
if (isClosed) {
throw new IOException("Stream closed");
}
// guard agains buffer overflows
if (offset < 0 || len > dst.length - offset) {
throw new IndexOutOfBoundsException("Requested lenght " + len
+ " from offset " + offset + " in buffer of size " + dst.length);
}
int dstSize = offset + len;
dstPos = offset;
while (dstPos < dstSize) {
if (srcSize - srcPos == 0) {
srcSize = in.read(src, 0, srcBuffSize);
srcPos = 0;
if (srcSize < 0) {
srcSize = 0;
if (frameFinished) {
return -1;
} else if (isContinuous) {
return (int)(dstPos - offset);
} else {
throw new IOException("Read error or truncated source");
}
}
frameFinished = false;
}
int size = decompressStream(stream, dst, dstSize, src, (int) srcSize);
if (Zstd.isError(size)) {
throw new IOException("Decompression error: " + Zstd.getErrorName(size));
}
// we have completed a frame
if (size == 0) {
frameFinished = true;
// re-init the codec so it can start decoding next frame
size = initDStream(stream);
if (Zstd.isError(size)) {
throw new IOException("Decompression error: " + Zstd.getErrorName(size));
}
return (int)(dstPos - offset);
}
}
return len;
}
public int read() throws IOException {
byte[] oneByte = new byte[1];
int result = read(oneByte, 0, 1);
if (result > 0) {
return oneByte[0] & 0xff;
} else {
return result;
}
}
public int available() throws IOException {
if (isClosed) {
throw new IOException("Stream closed");
}
if (srcSize - srcPos > 0) {
return (int)(srcSize - srcPos);
} else {
return in.available();
}
}
/* we don't support mark/reset */
public boolean markSupported() {
return false;
}
/* we can skip forward */
public long skip(long numBytes) throws IOException {
if (isClosed) {
throw new IOException("Stream closed");
}
if (numBytes <= 0) {
return 0;
}
long toSkip = numBytes;
int bufferLen = (int) Math.min(recommendedDOutSize(), toSkip);
byte data[] = new byte[bufferLen];
while (toSkip > 0) {
int read = read(data, 0, (int) Math.min((long) bufferLen, toSkip));
if (read < 0) {
break;
}
toSkip -= read;
}
return numBytes - toSkip;
}
public void close() throws IOException {
if (isClosed) {
return;
}
freeDStream(stream);
in.close();
isClosed = true;
}
@Override
protected void finalize() throws Throwable {
close();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy