diff options
Diffstat (limited to 'java/org/brotli/wrapper/enc/encoder_jni.cc')
-rwxr-xr-x | java/org/brotli/wrapper/enc/encoder_jni.cc | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/java/org/brotli/wrapper/enc/encoder_jni.cc b/java/org/brotli/wrapper/enc/encoder_jni.cc new file mode 100755 index 0000000..adc7c12 --- /dev/null +++ b/java/org/brotli/wrapper/enc/encoder_jni.cc @@ -0,0 +1,224 @@ +/* Copyright 2017 Google Inc. All Rights Reserved. + + Distributed under MIT license. + See file LICENSE for detail or copy at https://opensource.org/licenses/MIT +*/ + +#include <jni.h> + +#include <new> + +#include <brotli/encode.h> + +namespace { +/* A structure used to persist the encoder's state in between calls. */ +typedef struct EncoderHandle { + BrotliEncoderState* state; + + jobject custom_dictionary_ref; + + uint8_t* input_start; + size_t input_offset; + size_t input_last; +} EncoderHandle; + +/* Obtain handle from opaque pointer. */ +EncoderHandle* getHandle(void* opaque) { + return static_cast<EncoderHandle*>(opaque); +} + +} /* namespace */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Creates a new Encoder. + * + * Cookie to address created encoder is stored in out_cookie. In case of failure + * cookie is 0. + * + * @param ctx {out_cookie, in_directBufferSize, in_quality, in_lgwin} tuple + * @returns direct ByteBuffer if directBufferSize is not 0; otherwise null + */ +JNIEXPORT jobject JNICALL +Java_org_brotli_wrapper_enc_EncoderJNI_nativeCreate( + JNIEnv* env, jobject /*jobj*/, jlongArray ctx, jobject custom_dictionary) { + bool ok = true; + EncoderHandle* handle = nullptr; + jlong context[4]; + env->GetLongArrayRegion(ctx, 0, 4, context); + size_t input_size = context[1]; + context[0] = 0; + handle = new (std::nothrow) EncoderHandle(); + ok = !!handle; + + if (ok) { + handle->custom_dictionary_ref = nullptr; + handle->input_offset = 0; + handle->input_last = 0; + handle->input_start = nullptr; + + if (input_size == 0) { + ok = false; + } else { + handle->input_start = new (std::nothrow) uint8_t[input_size]; + ok = !!handle->input_start; + } + } + + if (ok) { + handle->state = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr); + ok = !!handle->state; + } + + if (ok) { + int quality = context[2]; + if (quality >= 0) { + BrotliEncoderSetParameter(handle->state, BROTLI_PARAM_QUALITY, quality); + } + int lgwin = context[3]; + if (lgwin >= 0) { + BrotliEncoderSetParameter(handle->state, BROTLI_PARAM_LGWIN, lgwin); + } + } + + if (ok && !!custom_dictionary) { + handle->custom_dictionary_ref = env->NewGlobalRef(custom_dictionary); + if (!!handle->custom_dictionary_ref) { + uint8_t* custom_dictionary_address = static_cast<uint8_t*>( + env->GetDirectBufferAddress(handle->custom_dictionary_ref)); + if (!!custom_dictionary_address) { + jlong capacity = + env->GetDirectBufferCapacity(handle->custom_dictionary_ref); + ok = (capacity > 0) && (capacity < (1 << 24)); + if (ok) { + size_t custom_dictionary_size = static_cast<size_t>(capacity); + BrotliEncoderSetCustomDictionary( + handle->state, custom_dictionary_size, custom_dictionary_address); + } + } else { + ok = false; + } + } else { + ok = false; + } + } + + if (ok) { + /* TODO: future versions (e.g. when 128-bit architecture comes) + might require thread-safe cookie<->handle mapping. */ + context[0] = reinterpret_cast<jlong>(handle); + } else if (!!handle) { + if (!!handle->custom_dictionary_ref) { + env->DeleteGlobalRef(handle->custom_dictionary_ref); + } + if (!!handle->input_start) delete[] handle->input_start; + delete handle; + } + + env->SetLongArrayRegion(ctx, 0, 1, context); + + if (!ok) { + return nullptr; + } + + return env->NewDirectByteBuffer(handle->input_start, input_size); +} + +/** + * Push data to encoder. + * + * @param ctx {in_cookie, in_operation_out_success, out_has_more_output, + * out_has_remaining_input} tuple + * @param input_length number of bytes provided in input or direct input; + * 0 to process further previous input + */ +JNIEXPORT void JNICALL +Java_org_brotli_wrapper_enc_EncoderJNI_nativePush( + JNIEnv* env, jobject /*jobj*/, jlongArray ctx, jint input_length) { + jlong context[4]; + env->GetLongArrayRegion(ctx, 0, 4, context); + EncoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0])); + int operation = context[1]; + context[1] = 0; /* ERROR */ + env->SetLongArrayRegion(ctx, 0, 4, context); + + BrotliEncoderOperation op; + switch (operation) { + case 0: op = BROTLI_OPERATION_PROCESS; break; + case 1: op = BROTLI_OPERATION_FLUSH; break; + case 2: op = BROTLI_OPERATION_FINISH; break; + default: return; /* ERROR */ + } + + if (input_length != 0) { + /* Still have unconsumed data. Workflow is broken. */ + if (handle->input_offset < handle->input_last) { + return; + } + handle->input_offset = 0; + handle->input_last = input_length; + } + + /* Actual compression. */ + const uint8_t* in = handle->input_start + handle->input_offset; + size_t in_size = handle->input_last - handle->input_offset; + size_t out_size = 0; + BROTLI_BOOL status = BrotliEncoderCompressStream( + handle->state, op, &in_size, &in, &out_size, nullptr, nullptr); + handle->input_offset = handle->input_last - in_size; + if (!!status) { + context[1] = 1; + context[2] = BrotliEncoderHasMoreOutput(handle->state) ? 1 : 0; + context[3] = (handle->input_offset != handle->input_last) ? 1 : 0; + } + env->SetLongArrayRegion(ctx, 0, 4, context); +} + +/** + * Pull decompressed data from encoder. + * + * @param ctx {in_cookie, out_success, out_has_more_output, + * out_has_remaining_input} tuple + * @returns direct ByteBuffer; all the produced data MUST be consumed before + * any further invocation; null in case of error + */ +JNIEXPORT jobject JNICALL +Java_org_brotli_wrapper_enc_EncoderJNI_nativePull( + JNIEnv* env, jobject /*jobj*/, jlongArray ctx) { + jlong context[4]; + env->GetLongArrayRegion(ctx, 0, 4, context); + EncoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0])); + size_t data_length = 0; + const uint8_t* data = BrotliEncoderTakeOutput(handle->state, &data_length); + context[1] = 1; + context[2] = BrotliEncoderHasMoreOutput(handle->state) ? 1 : 0; + context[3] = (handle->input_offset != handle->input_last) ? 1 : 0; + env->SetLongArrayRegion(ctx, 0, 4, context); + return env->NewDirectByteBuffer(const_cast<uint8_t*>(data), data_length); +} + +/** + * Releases all used resources. + * + * @param ctx {in_cookie} tuple + */ +JNIEXPORT void JNICALL +Java_org_brotli_wrapper_enc_EncoderJNI_nativeDestroy( + JNIEnv* env, jobject /*jobj*/, jlongArray ctx) { + jlong context[2]; + env->GetLongArrayRegion(ctx, 0, 2, context); + EncoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0])); + BrotliEncoderDestroyInstance(handle->state); + if (!!handle->custom_dictionary_ref) { + env->DeleteGlobalRef(handle->custom_dictionary_ref); + } + delete[] handle->input_start; + delete handle; +} + +#ifdef __cplusplus +} +#endif |