119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov/* Copyright 2017 Google Inc. All Rights Reserved. 219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov Distributed under MIT license. 419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov*/ 619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikovpackage org.brotli.wrapper.dec; 819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikovimport java.io.IOException; 1019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikovimport java.nio.ByteBuffer; 1119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 1219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov/** 1319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov * JNI wrapper for brotli decoder. 1419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov */ 1519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikovclass DecoderJNI { 16d63e8f75f5c16a6d7c8308bfd28c43cbdb6ad390Eugene Kliuchnikov private static native ByteBuffer nativeCreate(long[] context); 1719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov private static native void nativePush(long[] context, int length); 1819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov private static native ByteBuffer nativePull(long[] context); 1919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov private static native void nativeDestroy(long[] context); 2019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 2119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov enum Status { 2219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov ERROR, 2319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov DONE, 2419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov NEEDS_MORE_INPUT, 2519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov NEEDS_MORE_OUTPUT, 2619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov OK 2719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov }; 2819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 2919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov static class Wrapper { 3019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov private final long[] context = new long[2]; 3119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov private final ByteBuffer inputBuffer; 3219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov private Status lastStatus = Status.NEEDS_MORE_INPUT; 3319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 34d63e8f75f5c16a6d7c8308bfd28c43cbdb6ad390Eugene Kliuchnikov Wrapper(int inputBufferSize) throws IOException { 3519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov this.context[1] = inputBufferSize; 36d63e8f75f5c16a6d7c8308bfd28c43cbdb6ad390Eugene Kliuchnikov this.inputBuffer = nativeCreate(this.context); 3719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (this.context[0] == 0) { 3819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov throw new IOException("failed to initialize native brotli decoder"); 3919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 4019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 4119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 4219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov void push(int length) { 4319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (length < 0) { 4419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov throw new IllegalArgumentException("negative block length"); 4519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 4619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (context[0] == 0) { 4719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov throw new IllegalStateException("brotli decoder is already destroyed"); 4819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 4919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (lastStatus != Status.NEEDS_MORE_INPUT && lastStatus != Status.OK) { 5019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov throw new IllegalStateException("pushing input to decoder in " + lastStatus + " state"); 5119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 5219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (lastStatus == Status.OK && length != 0) { 5319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov throw new IllegalStateException("pushing input to decoder in OK state"); 5419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 5519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov nativePush(context, length); 5619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov parseStatus(); 5719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 5819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 5919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov private void parseStatus() { 6019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov long status = context[1]; 6119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (status == 1) { 6219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov lastStatus = Status.DONE; 6319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } else if (status == 2) { 6419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov lastStatus = Status.NEEDS_MORE_INPUT; 6519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } else if (status == 3) { 6619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov lastStatus = Status.NEEDS_MORE_OUTPUT; 6719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } else if (status == 4) { 6819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov lastStatus = Status.OK; 6919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } else { 7019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov lastStatus = Status.ERROR; 7119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 7219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 7319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 7419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov Status getStatus() { 7519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov return lastStatus; 7619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 7719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 7819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov ByteBuffer getInputBuffer() { 7919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov return inputBuffer; 8019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 8119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 8219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov ByteBuffer pull() { 8319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (context[0] == 0) { 8419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov throw new IllegalStateException("brotli decoder is already destroyed"); 8519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 8619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (lastStatus != Status.NEEDS_MORE_OUTPUT) { 8719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov throw new IllegalStateException("pulling output from decoder in " + lastStatus + " state"); 8819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 8919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov ByteBuffer result = nativePull(context); 9019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov parseStatus(); 9119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov return result; 9219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 9319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 9419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov /** 9519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov * Releases native resources. 9619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov */ 9719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov void destroy() { 9819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (context[0] == 0) { 9919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov throw new IllegalStateException("brotli decoder is already destroyed"); 10019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 10119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov nativeDestroy(context); 10219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov context[0] = 0; 10319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 10419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov 10519dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov @Override 10619dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov protected void finalize() throws Throwable { 10719dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov if (context[0] != 0) { 10819dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov /* TODO: log resource leak? */ 10919dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov destroy(); 11019dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 11119dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov super.finalize(); 11219dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 11319dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov } 11419dc934e391752b78f78eca60c2c1ca5f6ac647bEugene Kliuchnikov} 115