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 #include <jni.h>
      8 
      9 #include <new>
     10 
     11 #include <brotli/decode.h>
     12 
     13 namespace {
     14 /* A structure used to persist the decoder's state in between calls. */
     15 typedef struct DecoderHandle {
     16   BrotliDecoderState* state;
     17 
     18   uint8_t* input_start;
     19   size_t input_offset;
     20   size_t input_length;
     21 } DecoderHandle;
     22 
     23 /* Obtain handle from opaque pointer. */
     24 DecoderHandle* getHandle(void* opaque) {
     25   return static_cast<DecoderHandle*>(opaque);
     26 }
     27 
     28 }  /* namespace */
     29 
     30 #ifdef __cplusplus
     31 extern "C" {
     32 #endif
     33 
     34 /**
     35  * Creates a new Decoder.
     36  *
     37  * Cookie to address created decoder is stored in out_cookie. In case of failure
     38  * cookie is 0.
     39  *
     40  * @param ctx {out_cookie, in_directBufferSize} tuple
     41  * @returns direct ByteBuffer if directBufferSize is not 0; otherwise null
     42  */
     43 JNIEXPORT jobject JNICALL
     44 Java_org_brotli_wrapper_dec_DecoderJNI_nativeCreate(
     45     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
     46   bool ok = true;
     47   DecoderHandle* handle = nullptr;
     48   jlong context[2];
     49   env->GetLongArrayRegion(ctx, 0, 2, context);
     50   size_t input_size = context[1];
     51   context[0] = 0;
     52   handle = new (std::nothrow) DecoderHandle();
     53   ok = !!handle;
     54 
     55   if (ok) {
     56     handle->input_offset = 0;
     57     handle->input_length = 0;
     58     handle->input_start = nullptr;
     59 
     60     if (input_size == 0) {
     61       ok = false;
     62     } else {
     63       handle->input_start = new (std::nothrow) uint8_t[input_size];
     64       ok = !!handle->input_start;
     65     }
     66   }
     67 
     68   if (ok) {
     69     handle->state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
     70     ok = !!handle->state;
     71   }
     72 
     73   if (ok) {
     74     /* TODO: future versions (e.g. when 128-bit architecture comes)
     75                      might require thread-safe cookie<->handle mapping. */
     76     context[0] = reinterpret_cast<jlong>(handle);
     77   } else if (!!handle) {
     78     if (!!handle->input_start) delete[] handle->input_start;
     79     delete handle;
     80   }
     81 
     82   env->SetLongArrayRegion(ctx, 0, 2, context);
     83 
     84   if (!ok) {
     85     return nullptr;
     86   }
     87 
     88   return env->NewDirectByteBuffer(handle->input_start, input_size);
     89 }
     90 
     91 /**
     92  * Push data to decoder.
     93  *
     94  * status codes:
     95  *  - 0 error happened
     96  *  - 1 stream is finished, no more input / output expected
     97  *  - 2 needs more input to process further
     98  *  - 3 needs more output to process further
     99  *  - 4 ok, can proceed further without additional input
    100  *
    101  * @param ctx {in_cookie, out_status} tuple
    102  * @param input_length number of bytes provided in input or direct input;
    103  *                     0 to process further previous input
    104  */
    105 JNIEXPORT void JNICALL
    106 Java_org_brotli_wrapper_dec_DecoderJNI_nativePush(
    107     JNIEnv* env, jobject /*jobj*/, jlongArray ctx, jint input_length) {
    108   jlong context[2];
    109   env->GetLongArrayRegion(ctx, 0, 2, context);
    110   DecoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
    111   context[1] = 0;  /* ERROR */
    112   env->SetLongArrayRegion(ctx, 0, 2, context);
    113 
    114   if (input_length != 0) {
    115     /* Still have unconsumed data. Workflow is broken. */
    116     if (handle->input_offset < handle->input_length) {
    117       return;
    118     }
    119     handle->input_offset = 0;
    120     handle->input_length = input_length;
    121   }
    122 
    123   /* Actual decompression. */
    124   const uint8_t* in = handle->input_start + handle->input_offset;
    125   size_t in_size = handle->input_length - handle->input_offset;
    126   size_t out_size = 0;
    127   BrotliDecoderResult status = BrotliDecoderDecompressStream(
    128       handle->state, &in_size, &in, &out_size, nullptr, nullptr);
    129   handle->input_offset = handle->input_length - in_size;
    130   switch (status) {
    131     case BROTLI_DECODER_RESULT_SUCCESS:
    132       /* Bytes after stream end are not allowed. */
    133       context[1] = (handle->input_offset == handle->input_length) ? 1 : 0;
    134       break;
    135 
    136     case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
    137       context[1] = 2;
    138       break;
    139 
    140     case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
    141       context[1] = 3;
    142       break;
    143 
    144     default:
    145       context[1] = 0;
    146       break;
    147   }
    148   env->SetLongArrayRegion(ctx, 0, 2, context);
    149 }
    150 
    151 /**
    152  * Pull decompressed data from decoder.
    153  *
    154  * @param ctx {in_cookie, out_status} tuple
    155  * @returns direct ByteBuffer; all the produced data MUST be consumed before
    156  *          any further invocation; null in case of error
    157  */
    158 JNIEXPORT jobject JNICALL
    159 Java_org_brotli_wrapper_dec_DecoderJNI_nativePull(
    160     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
    161   jlong context[2];
    162   env->GetLongArrayRegion(ctx, 0, 2, context);
    163   DecoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
    164   size_t data_length = 0;
    165   const uint8_t* data = BrotliDecoderTakeOutput(handle->state, &data_length);
    166   if (BrotliDecoderHasMoreOutput(handle->state)) {
    167     context[1] = 3;
    168   } else if (BrotliDecoderIsFinished(handle->state)) {
    169     /* Bytes after stream end are not allowed. */
    170     context[1] = (handle->input_offset == handle->input_length) ? 1 : 0;
    171   } else {
    172     /* Can proceed, or more data is required? */
    173     context[1] = (handle->input_offset == handle->input_length) ? 2 : 4;
    174   }
    175   env->SetLongArrayRegion(ctx, 0, 2, context);
    176   return env->NewDirectByteBuffer(const_cast<uint8_t*>(data), data_length);
    177 }
    178 
    179 /**
    180  * Releases all used resources.
    181  *
    182  * @param ctx {in_cookie} tuple
    183  */
    184 JNIEXPORT void JNICALL
    185 Java_org_brotli_wrapper_dec_DecoderJNI_nativeDestroy(
    186     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
    187   jlong context[2];
    188   env->GetLongArrayRegion(ctx, 0, 2, context);
    189   DecoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
    190   BrotliDecoderDestroyInstance(handle->state);
    191   delete[] handle->input_start;
    192   delete handle;
    193 }
    194 
    195 #ifdef __cplusplus
    196 }
    197 #endif
    198