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