Home | History | Annotate | Download | only in enc
      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