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. */
getHandle(void * opaque)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
Java_org_brotli_wrapper_enc_EncoderJNI_nativeCreate(JNIEnv * env,jobject,jlongArray ctx)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
Java_org_brotli_wrapper_enc_EncoderJNI_nativePush(JNIEnv * env,jobject,jlongArray ctx,jint input_length)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
Java_org_brotli_wrapper_enc_EncoderJNI_nativePull(JNIEnv * env,jobject,jlongArray ctx)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
Java_org_brotli_wrapper_enc_EncoderJNI_nativeDestroy(JNIEnv * env,jobject,jlongArray ctx)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