aboutsummaryrefslogtreecommitdiff
path: root/java/org/brotli/wrapper/dec/DecoderJNI.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/org/brotli/wrapper/dec/DecoderJNI.java')
-rwxr-xr-xjava/org/brotli/wrapper/dec/DecoderJNI.java117
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();
+ }
+ }
+}