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