aboutsummaryrefslogtreecommitdiff
path: root/java/org/brotli/wrapper/enc/encoder_jni.cc
diff options
context:
space:
mode:
Diffstat (limited to 'java/org/brotli/wrapper/enc/encoder_jni.cc')
-rwxr-xr-xjava/org/brotli/wrapper/enc/encoder_jni.cc224
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