xref: /aosp_15_r20/external/brotli/java/org/brotli/wrapper/enc/encoder_jni.cc (revision f4ee7fba7774faf2a30f13154332c0a06550dbc4)
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