diff options
Diffstat (limited to 'java/org/brotli/wrapper/dec/DecoderJNI.java')
-rwxr-xr-x | java/org/brotli/wrapper/dec/DecoderJNI.java | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/java/org/brotli/wrapper/dec/DecoderJNI.java b/java/org/brotli/wrapper/dec/DecoderJNI.java new file mode 100755 index 0000000..ffd3ce9 --- /dev/null +++ b/java/org/brotli/wrapper/dec/DecoderJNI.java @@ -0,0 +1,117 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +package org.brotli.wrapper.dec; + +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * JNI wrapper for brotli decoder. + */ +class DecoderJNI { + private static native ByteBuffer nativeCreate(long[] context, ByteBuffer customDictionary); + private static native void nativePush(long[] context, int length); + private static native ByteBuffer nativePull(long[] context); + private static native void nativeDestroy(long[] context); + + enum Status { + ERROR, + DONE, + NEEDS_MORE_INPUT, + NEEDS_MORE_OUTPUT, + OK + }; + + static class Wrapper { + private final long[] context = new long[2]; + private final ByteBuffer inputBuffer; + private Status lastStatus = Status.NEEDS_MORE_INPUT; + + Wrapper(int inputBufferSize, ByteBuffer customDictionary) throws IOException { + if (customDictionary != null && !customDictionary.isDirect()) { + throw new IllegalArgumentException("LZ77 dictionary must be direct ByteBuffer"); + } + this.context[1] = inputBufferSize; + this.inputBuffer = nativeCreate(this.context, customDictionary); + if (this.context[0] == 0) { + throw new IOException("failed to initialize native brotli decoder"); + } + } + + void push(int length) { + if (length < 0) { + throw new IllegalArgumentException("negative block length"); + } + if (context[0] == 0) { + throw new IllegalStateException("brotli decoder is already destroyed"); + } + if (lastStatus != Status.NEEDS_MORE_INPUT && lastStatus != Status.OK) { + throw new IllegalStateException("pushing input to decoder in " + lastStatus + " state"); + } + if (lastStatus == Status.OK && length != 0) { + throw new IllegalStateException("pushing input to decoder in OK state"); + } + nativePush(context, length); + parseStatus(); + } + + private void parseStatus() { + long status = context[1]; + if (status == 1) { + lastStatus = Status.DONE; + } else if (status == 2) { + lastStatus = Status.NEEDS_MORE_INPUT; + } else if (status == 3) { + lastStatus = Status.NEEDS_MORE_OUTPUT; + } else if (status == 4) { + lastStatus = Status.OK; + } else { + lastStatus = Status.ERROR; + } + } + + Status getStatus() { + return lastStatus; + } + + ByteBuffer getInputBuffer() { + return inputBuffer; + } + + ByteBuffer pull() { + if (context[0] == 0) { + throw new IllegalStateException("brotli decoder is already destroyed"); + } + if (lastStatus != Status.NEEDS_MORE_OUTPUT) { + throw new IllegalStateException("pulling output from decoder in " + lastStatus + " state"); + } + ByteBuffer result = nativePull(context); + parseStatus(); + return result; + } + + /** + * Releases native resources. + */ + void destroy() { + if (context[0] == 0) { + throw new IllegalStateException("brotli decoder is already destroyed"); + } + nativeDestroy(context); + context[0] = 0; + } + + @Override + protected void finalize() throws Throwable { + if (context[0] != 0) { + /* TODO: log resource leak? */ + destroy(); + } + super.finalize(); + } + } +} |