xref: /aosp_15_r20/external/brotli/java/org/brotli/wrapper/dec/decoder_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/decode.h>
12 
13 namespace {
14 /* A structure used to persist the decoder's state in between calls. */
15 typedef struct DecoderHandle {
16   BrotliDecoderState* state;
17 
18   uint8_t* input_start;
19   size_t input_offset;
20   size_t input_length;
21 } DecoderHandle;
22 
23 /* Obtain handle from opaque pointer. */
getHandle(void * opaque)24 DecoderHandle* getHandle(void* opaque) {
25   return static_cast<DecoderHandle*>(opaque);
26 }
27 
28 }  /* namespace */
29 
30 #ifdef __cplusplus
31 extern "C" {
32 #endif
33 
34 /**
35  * Creates a new Decoder.
36  *
37  * Cookie to address created decoder is stored in out_cookie. In case of failure
38  * cookie is 0.
39  *
40  * @param ctx {out_cookie, in_directBufferSize} tuple
41  * @returns direct ByteBuffer if directBufferSize is not 0; otherwise null
42  */
43 JNIEXPORT jobject JNICALL
Java_org_brotli_wrapper_dec_DecoderJNI_nativeCreate(JNIEnv * env,jobject,jlongArray ctx)44 Java_org_brotli_wrapper_dec_DecoderJNI_nativeCreate(
45     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
46   bool ok = true;
47   DecoderHandle* handle = nullptr;
48   jlong context[3];
49   env->GetLongArrayRegion(ctx, 0, 3, context);
50   size_t input_size = context[1];
51   context[0] = 0;
52   context[2] = 0;
53   handle = new (std::nothrow) DecoderHandle();
54   ok = !!handle;
55 
56   if (ok) {
57     handle->input_offset = 0;
58     handle->input_length = 0;
59     handle->input_start = nullptr;
60 
61     if (input_size == 0) {
62       ok = false;
63     } else {
64       handle->input_start = new (std::nothrow) uint8_t[input_size];
65       ok = !!handle->input_start;
66     }
67   }
68 
69   if (ok) {
70     handle->state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
71     ok = !!handle->state;
72   }
73 
74   if (ok) {
75     /* TODO: future versions (e.g. when 128-bit architecture comes)
76                      might require thread-safe cookie<->handle mapping. */
77     context[0] = reinterpret_cast<jlong>(handle);
78   } else if (!!handle) {
79     if (!!handle->input_start) delete[] handle->input_start;
80     delete handle;
81   }
82 
83   env->SetLongArrayRegion(ctx, 0, 3, context);
84 
85   if (!ok) {
86     return nullptr;
87   }
88 
89   return env->NewDirectByteBuffer(handle->input_start, input_size);
90 }
91 
92 /**
93  * Push data to decoder.
94  *
95  * status codes:
96  *  - 0 error happened
97  *  - 1 stream is finished, no more input / output expected
98  *  - 2 needs more input to process further
99  *  - 3 needs more output to process further
100  *  - 4 ok, can proceed further without additional input
101  *
102  * @param ctx {in_cookie, out_status} tuple
103  * @param input_length number of bytes provided in input or direct input;
104  *                     0 to process further previous input
105  */
106 JNIEXPORT void JNICALL
Java_org_brotli_wrapper_dec_DecoderJNI_nativePush(JNIEnv * env,jobject,jlongArray ctx,jint input_length)107 Java_org_brotli_wrapper_dec_DecoderJNI_nativePush(
108     JNIEnv* env, jobject /*jobj*/, jlongArray ctx, jint input_length) {
109   jlong context[3];
110   env->GetLongArrayRegion(ctx, 0, 3, context);
111   DecoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
112   context[1] = 0;  /* ERROR */
113   context[2] = 0;
114   env->SetLongArrayRegion(ctx, 0, 3, context);
115 
116   if (input_length != 0) {
117     /* Still have unconsumed data. Workflow is broken. */
118     if (handle->input_offset < handle->input_length) {
119       return;
120     }
121     handle->input_offset = 0;
122     handle->input_length = input_length;
123   }
124 
125   /* Actual decompression. */
126   const uint8_t* in = handle->input_start + handle->input_offset;
127   size_t in_size = handle->input_length - handle->input_offset;
128   size_t out_size = 0;
129   BrotliDecoderResult status = BrotliDecoderDecompressStream(
130       handle->state, &in_size, &in, &out_size, nullptr, nullptr);
131   handle->input_offset = handle->input_length - in_size;
132   switch (status) {
133     case BROTLI_DECODER_RESULT_SUCCESS:
134       /* Bytes after stream end are not allowed. */
135       context[1] = (handle->input_offset == handle->input_length) ? 1 : 0;
136       break;
137 
138     case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
139       context[1] = 2;
140       break;
141 
142     case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
143       context[1] = 3;
144       break;
145 
146     default:
147       context[1] = 0;
148       break;
149   }
150   context[2] = BrotliDecoderHasMoreOutput(handle->state) ? 1 : 0;
151   env->SetLongArrayRegion(ctx, 0, 3, context);
152 }
153 
154 /**
155  * Pull decompressed data from decoder.
156  *
157  * @param ctx {in_cookie, out_status} 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_dec_DecoderJNI_nativePull(JNIEnv * env,jobject,jlongArray ctx)162 Java_org_brotli_wrapper_dec_DecoderJNI_nativePull(
163     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
164   jlong context[3];
165   env->GetLongArrayRegion(ctx, 0, 3, context);
166   DecoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
167   size_t data_length = 0;
168   const uint8_t* data = BrotliDecoderTakeOutput(handle->state, &data_length);
169   bool hasMoreOutput = !!BrotliDecoderHasMoreOutput(handle->state);
170   if (hasMoreOutput) {
171     context[1] = 3;
172   } else if (BrotliDecoderIsFinished(handle->state)) {
173     /* Bytes after stream end are not allowed. */
174     context[1] = (handle->input_offset == handle->input_length) ? 1 : 0;
175   } else {
176     /* Can proceed, or more data is required? */
177     context[1] = (handle->input_offset == handle->input_length) ? 2 : 4;
178   }
179   context[2] = hasMoreOutput ? 1 : 0;
180   env->SetLongArrayRegion(ctx, 0, 3, context);
181   return env->NewDirectByteBuffer(const_cast<uint8_t*>(data), data_length);
182 }
183 
184 /**
185  * Releases all used resources.
186  *
187  * @param ctx {in_cookie} tuple
188  */
189 JNIEXPORT void JNICALL
Java_org_brotli_wrapper_dec_DecoderJNI_nativeDestroy(JNIEnv * env,jobject,jlongArray ctx)190 Java_org_brotli_wrapper_dec_DecoderJNI_nativeDestroy(
191     JNIEnv* env, jobject /*jobj*/, jlongArray ctx) {
192   jlong context[3];
193   env->GetLongArrayRegion(ctx, 0, 3, context);
194   DecoderHandle* handle = getHandle(reinterpret_cast<void*>(context[0]));
195   BrotliDecoderDestroyInstance(handle->state);
196   delete[] handle->input_start;
197   delete handle;
198 }
199 
200 #ifdef __cplusplus
201 }
202 #endif
203