Home | History | Annotate | Download | only in dec
      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.dec;
      8 
      9 import java.io.IOException;
     10 import java.nio.ByteBuffer;
     11 
     12 /**
     13  * JNI wrapper for brotli decoder.
     14  */
     15 class DecoderJNI {
     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 Status {
     22     ERROR,
     23     DONE,
     24     NEEDS_MORE_INPUT,
     25     NEEDS_MORE_OUTPUT,
     26     OK
     27   };
     28 
     29   static class Wrapper {
     30     private final long[] context = new long[2];
     31     private final ByteBuffer inputBuffer;
     32     private Status lastStatus = Status.NEEDS_MORE_INPUT;
     33 
     34     Wrapper(int inputBufferSize) throws IOException {
     35       this.context[1] = inputBufferSize;
     36       this.inputBuffer = nativeCreate(this.context);
     37       if (this.context[0] == 0) {
     38         throw new IOException("failed to initialize native brotli decoder");
     39       }
     40     }
     41 
     42     void push(int length) {
     43       if (length < 0) {
     44         throw new IllegalArgumentException("negative block length");
     45       }
     46       if (context[0] == 0) {
     47         throw new IllegalStateException("brotli decoder is already destroyed");
     48       }
     49       if (lastStatus != Status.NEEDS_MORE_INPUT && lastStatus != Status.OK) {
     50         throw new IllegalStateException("pushing input to decoder in " + lastStatus + " state");
     51       }
     52       if (lastStatus == Status.OK && length != 0) {
     53         throw new IllegalStateException("pushing input to decoder in OK state");
     54       }
     55       nativePush(context, length);
     56       parseStatus();
     57     }
     58 
     59     private void parseStatus() {
     60       long status = context[1];
     61       if (status == 1) {
     62         lastStatus = Status.DONE;
     63       } else if (status == 2) {
     64         lastStatus = Status.NEEDS_MORE_INPUT;
     65       } else if (status == 3) {
     66         lastStatus = Status.NEEDS_MORE_OUTPUT;
     67       } else if (status == 4) {
     68         lastStatus = Status.OK;
     69       } else {
     70         lastStatus = Status.ERROR;
     71       }
     72     }
     73 
     74     Status getStatus() {
     75       return lastStatus;
     76     }
     77 
     78     ByteBuffer getInputBuffer() {
     79       return inputBuffer;
     80     }
     81 
     82     ByteBuffer pull() {
     83       if (context[0] == 0) {
     84         throw new IllegalStateException("brotli decoder is already destroyed");
     85       }
     86       if (lastStatus != Status.NEEDS_MORE_OUTPUT) {
     87         throw new IllegalStateException("pulling output from decoder in " + lastStatus + " state");
     88       }
     89       ByteBuffer result = nativePull(context);
     90       parseStatus();
     91       return result;
     92     }
     93 
     94     /**
     95      * Releases native resources.
     96      */
     97     void destroy() {
     98       if (context[0] == 0) {
     99         throw new IllegalStateException("brotli decoder is already destroyed");
    100       }
    101       nativeDestroy(context);
    102       context[0] = 0;
    103     }
    104 
    105     @Override
    106     protected void finalize() throws Throwable {
    107       if (context[0] != 0) {
    108         /* TODO: log resource leak? */
    109         destroy();
    110       }
    111       super.finalize();
    112     }
    113   }
    114 }
    115