1 /* Copyright 2017 Google Inc. All Rights Reserved. 2 3 Distributed under MIT license. 4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 */ 6 7 package org.brotli.wrapper.enc; 8 9 import java.io.IOException; 10 import java.nio.ByteBuffer; 11 12 /** 13 * JNI wrapper for brotli encoder. 14 */ 15 class EncoderJNI { 16 private static native ByteBuffer nativeCreate(long[] context); 17 private static native void nativePush(long[] context, int length); 18 private static native ByteBuffer nativePull(long[] context); 19 private static native void nativeDestroy(long[] context); 20 21 enum Operation { 22 PROCESS, 23 FLUSH, 24 FINISH 25 } 26 27 static class Wrapper { 28 protected final long[] context = new long[5]; 29 private final ByteBuffer inputBuffer; 30 31 Wrapper(int inputBufferSize, int quality, int lgwin) 32 throws IOException { 33 this.context[1] = inputBufferSize; 34 this.context[2] = quality; 35 this.context[3] = lgwin; 36 this.inputBuffer = nativeCreate(this.context); 37 if (this.context[0] == 0) { 38 throw new IOException("failed to initialize native brotli encoder"); 39 } 40 this.context[1] = 1; 41 this.context[2] = 0; 42 this.context[3] = 0; 43 } 44 45 void push(Operation op, int length) { 46 if (length < 0) { 47 throw new IllegalArgumentException("negative block length"); 48 } 49 if (context[0] == 0) { 50 throw new IllegalStateException("brotli encoder is already destroyed"); 51 } 52 if (!isSuccess() || hasMoreOutput()) { 53 throw new IllegalStateException("pushing input to encoder in unexpected state"); 54 } 55 if (hasRemainingInput() && length != 0) { 56 throw new IllegalStateException("pushing input to encoder over previous input"); 57 } 58 context[1] = op.ordinal(); 59 nativePush(context, length); 60 } 61 62 boolean isSuccess() { 63 return context[1] != 0; 64 } 65 66 boolean hasMoreOutput() { 67 return context[2] != 0; 68 } 69 70 boolean hasRemainingInput() { 71 return context[3] != 0; 72 } 73 74 boolean isFinished() { 75 return context[4] != 0; 76 } 77 78 ByteBuffer getInputBuffer() { 79 return inputBuffer; 80 } 81 82 ByteBuffer pull() { 83 if (context[0] == 0) { 84 throw new IllegalStateException("brotli encoder is already destroyed"); 85 } 86 if (!isSuccess() || !hasMoreOutput()) { 87 throw new IllegalStateException("pulling while data is not ready"); 88 } 89 return nativePull(context); 90 } 91 92 /** 93 * Releases native resources. 94 */ 95 void destroy() { 96 if (context[0] == 0) { 97 throw new IllegalStateException("brotli encoder is already destroyed"); 98 } 99 nativeDestroy(context); 100 context[0] = 0; 101 } 102 103 @Override 104 protected void finalize() throws Throwable { 105 if (context[0] != 0) { 106 /* TODO: log resource leak? */ 107 destroy(); 108 } 109 super.finalize(); 110 } 111 } 112 } 113