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 #include <jni.h>
      8 
      9 #include <new>
     10 
     11 #include <brotli/encode.h>
     12 
     13 namespace {
     14 /* A structure used to persist the encoder's state in between calls. */
     15 typedef struct EncoderHandle {
     16   BrotliEncoderState* state;
     17 
     18   uint8_t* input_start;
     19   size_t input_offset;
     20   size_t input_last;
     21 } EncoderHandle;
     22 
     23 /* Obtain handle from opaque pointer. */
     24 EncoderHandle* getHandle(void* opaque) {
     25   return static_cast<EncoderHandle*>(opaque);
     26 }
     27 
     28 }  /* namespace */
     29 
     30 #ifdef __cplusplus
     31 extern "C" {
     32 #endif
     33 
     34 /**
     35  * Creates a new Encoder.
     36  *
     37  * Cookie to address created encoder is stored in out_cookie. In case of failure
     38  * cookie is 0.
     39  *
     40  * @param ctx {out_cookie, in_directBufferSize, in_quality, in_lgwin} tuple
     41  * @returns direct ByteBuffer if directBufferSize is not 0; otherwise null
     42  */
     43 JNIEXPORT jobject JNICALL
     44 Java_org_brotli_wrapper_enc_EncoderJNI_nativeCreate(
     45     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
     46   bool ok = true;
     47   EncoderHandle* handle = nullptr;
     48   jlong context[5];
     49   env->GetLongArrayRegion(ctx, 0, 5, context);
     50   size_t input_size = context[1];
     51   context[0] = 0;
     52   handle = new (std::nothrow) EncoderHandle();
     53   ok = !!handle;
     54 
     55   if (ok) {
     56     handle->input_offset = 0;
     57     handle->input_last = 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 = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
     70     ok = !!handle->state;
     71   }
     72 
     73   if (ok) {
     74     int quality = context[2];
     75     if (quality >= 0) {
     76       BrotliEncoderSetParameter(handle->state, BROTLI_PARAM_QUALITY, quality);
     77     }
     78     int lgwin = context[3];
     79     if (lgwin >= 0) {
     80       BrotliEncoderSetParameter(handle->state, BROTLI_PARAM_LGWIN, lgwin);
     81     }
     82   }
     83 
     84   if (ok) {
     85     /* TODO: future versions (e.g. when 128-bit architecture comes)
     86                      might require thread-safe cookie<->handle mapping. */
     87     context[0] = reinterpret_cast<jlong>(handle);
     88   } else if (!!handle) {
     89     if (!!handle->input_start) delete[] handle->input_start;
     90     delete handle;
     91   }
     92 
     93   env->SetLongArrayRegion(ctx, 0, 1, context);
     94 
     95   if (!ok) {
     96     return nullptr;
     97   }
     98 
     99   return env->NewDirectByteBuffer(handle->input_start, input_size);
    100 }
    101 
    102 /**
    103  * Push data to encoder.
    104  *
    105  * @param ctx {in_cookie, in_operation_out_success, out_has_more_output,
    106  *             out_has_remaining_input} tuple
    107  * @param input_length number of bytes provided in input or direct input;
    108  *                     0 to process further previous input
    109  */
    110 JNIEXPORT void JNICALL
    111 Java_org_brotli_wrapper_enc_EncoderJNI_nativePush(
    112     JNIEnv* env, jobject /*jobj*/, jlongArray ctx, jint input_length) {
    113   jlong context[5];
    114   env->GetLongArrayRegion(ctx, 0, 5, context);
    115   EncoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
    116   int operation = context[1];
    117   context[1] = 0;  /* ERROR */
    118   env->SetLongArrayRegion(ctx, 0, 5, context);
    119 
    120   BrotliEncoderOperation op;
    121   switch (operation) {
    122     case 0: op = BROTLI_OPERATION_PROCESS; break;
    123     case 1: op = BROTLI_OPERATION_FLUSH; break;
    124     case 2: op = BROTLI_OPERATION_FINISH; break;
    125     default: return;  /* ERROR */
    126   }
    127 
    128   if (input_length != 0) {
    129     /* Still have unconsumed data. Workflow is broken. */
    130     if (handle->input_offset < handle->input_last) {
    131       return;
    132     }
    133     handle->input_offset = 0;
    134     handle->input_last = input_length;
    135   }
    136 
    137   /* Actual compression. */
    138   const uint8_t* in = handle->input_start + handle->input_offset;
    139   size_t in_size = handle->input_last - handle->input_offset;
    140   size_t out_size = 0;
    141   BROTLI_BOOL status = BrotliEncoderCompressStream(
    142       handle->state, op, &in_size, &in, &out_size, nullptr, nullptr);
    143   handle->input_offset = handle->input_last - in_size;
    144   if (!!status) {
    145     context[1] = 1;
    146     context[2] = BrotliEncoderHasMoreOutput(handle->state) ? 1 : 0;
    147     context[3] = (handle->input_offset != handle->input_last) ? 1 : 0;
    148     context[4] = BrotliEncoderIsFinished(handle->state) ? 1 : 0;
    149   }
    150   env->SetLongArrayRegion(ctx, 0, 5, context);
    151 }
    152 
    153 /**
    154  * Pull decompressed data from encoder.
    155  *
    156  * @param ctx {in_cookie, out_success, out_has_more_output,
    157  *             out_has_remaining_input} tuple
    158  * @returns direct ByteBuffer; all the produced data MUST be consumed before
    159  *          any further invocation; null in case of error
    160  */
    161 JNIEXPORT jobject JNICALL
    162 Java_org_brotli_wrapper_enc_EncoderJNI_nativePull(
    163     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
    164   jlong context[5];
    165   env->GetLongArrayRegion(ctx, 0, 5, context);
    166   EncoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
    167   size_t data_length = 0;
    168   const uint8_t* data = BrotliEncoderTakeOutput(handle->state, &data_length);
    169   context[1] = 1;
    170   context[2] = BrotliEncoderHasMoreOutput(handle->state) ? 1 : 0;
    171   context[3] = (handle->input_offset != handle->input_last) ? 1 : 0;
    172   context[4] = BrotliEncoderIsFinished(handle->state) ? 1 : 0;
    173   env->SetLongArrayRegion(ctx, 0, 5, context);
    174   return env->NewDirectByteBuffer(const_cast<uint8_t*>(data), data_length);
    175 }
    176 
    177 /**
    178  * Releases all used resources.
    179  *
    180  * @param ctx {in_cookie} tuple
    181  */
    182 JNIEXPORT void JNICALL
    183 Java_org_brotli_wrapper_enc_EncoderJNI_nativeDestroy(
    184     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
    185   jlong context[2];
    186   env->GetLongArrayRegion(ctx, 0, 2, context);
    187   EncoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
    188   BrotliEncoderDestroyInstance(handle->state);
    189   delete[] handle->input_start;
    190   delete handle;
    191 }
    192 
    193 #ifdef __cplusplus
    194 }
    195 #endif
    196