MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add brotli to compression and decompression

+316 -44
+23
examples/spec/streams-compression.js
··· 24 24 test('DS readable is ReadableStream', ds0.readable instanceof ReadableStream, true); 25 25 test('DS writable is WritableStream', ds0.writable instanceof WritableStream, true); 26 26 27 + const csBrotli = new CompressionStream('brotli'); 28 + test('CS brotli toStringTag', Object.prototype.toString.call(csBrotli), '[object CompressionStream]'); 29 + 27 30 async function collectStream(readable) { 28 31 const reader = readable.getReader(); 29 32 const chunks = []; ··· 96 99 test('deflate-raw roundtrip', new TextDecoder().decode(decompressed), 'Raw deflate test'); 97 100 } 98 101 102 + async function testBrotliRoundTrip() { 103 + const input = new TextEncoder().encode('brotli stream test data'); 104 + const cs = new CompressionStream('brotli'); 105 + const writer = cs.writable.getWriter(); 106 + writer.write(input); 107 + writer.close(); 108 + const compressed = await collectStream(cs.readable); 109 + test('brotli compressed length > 0', compressed.length > 0, true); 110 + 111 + const ds = new DecompressionStream('brotli'); 112 + const writer2 = ds.writable.getWriter(); 113 + writer2.write(compressed); 114 + writer2.close(); 115 + const reader = ds.readable.getReader(); 116 + const first = await reader.read(); 117 + test('brotli decompressed chunk is Uint8Array', first.value.constructor, Uint8Array); 118 + test('brotli roundtrip', new TextDecoder().decode(first.value), 'brotli stream test data'); 119 + } 120 + 99 121 async function testPipeThrough() { 100 122 const input = 'pipe through compression test'; 101 123 const encoded = new TextEncoder().encode(input); ··· 155 177 await testGzipRoundTrip(); 156 178 await testDeflateRoundTrip(); 157 179 await testDeflateRawRoundTrip(); 180 + await testBrotliRoundTrip(); 158 181 await testPipeThrough(); 159 182 await testMultipleChunks(); 160 183 await testLargeData();
+23
include/streams/brotli.h
··· 1 + #ifndef STREAMS_BROTLI_H 2 + #define STREAMS_BROTLI_H 3 + 4 + #include <stddef.h> 5 + #include <stdbool.h> 6 + #include "types.h" 7 + 8 + typedef struct brotli_stream_state brotli_stream_state_t; 9 + 10 + brotli_stream_state_t *brotli_stream_state_new(bool decompress); 11 + void brotli_stream_state_destroy(brotli_stream_state_t *st); 12 + 13 + ant_value_t brotli_stream_transform( 14 + ant_t *js, brotli_stream_state_t *st, 15 + ant_value_t ctrl_obj, const uint8_t *input, size_t input_len 16 + ); 17 + 18 + ant_value_t brotli_stream_flush( 19 + ant_t *js, brotli_stream_state_t *st, 20 + ant_value_t ctrl_obj 21 + ); 22 + 23 + #endif
+9 -15
meson/deps/meson.build
··· 76 76 zlib_ng_srcdir = subprojects_src / deps_info['zlib-ng']['dir'] 77 77 zlib_ng_builddir = subprojects_build / deps_info['zlib-ng']['dir'] 78 78 79 - brotli_common_dep = dependency('libbrotlicommon', 80 - fallback: ['google-brotli', 'libbrotlicommon'], 81 - required: true, 82 - static: is_static, 83 - ) 84 - brotli_dec_dep = dependency('libbrotlidec', 85 - fallback: ['google-brotli', 'libbrotlidec'], 86 - required: true, 87 - static: is_static, 88 - ) 89 - brotli_enc_dep = dependency('libbrotlienc', 90 - fallback: ['google-brotli', 'libbrotlienc'], 91 - required: true, 92 - static: is_static, 93 - ) 79 + brotli_sub = subproject('google-brotli', default_options: [ 80 + 'default_library=static', 81 + 'b_lto=false', 82 + 'warning_level=0', 83 + ]) 84 + 85 + brotli_common_dep = brotli_sub.get_variable('brotli_common_dep') 86 + brotli_dec_dep = brotli_sub.get_variable('brotli_decoder_dep') 87 + brotli_enc_dep = brotli_sub.get_variable('brotli_encoder_dep') 94 88 95 89 cmake_prefix = get_option('deps_prefix_cmake') 96 90
+23 -7
src/modules/buffer.c
··· 2057 2057 js_mkprop_fast(js, arraybuffer_ctor_obj, "name", 4, ANT_STRING("ArrayBuffer")); 2058 2058 js_set_descriptor(js, arraybuffer_ctor_obj, "name", 4, 0); 2059 2059 js_define_species_getter(js, arraybuffer_ctor_obj); 2060 - js_set(js, glob, "ArrayBuffer", js_obj_to_func(arraybuffer_ctor_obj)); 2060 + ant_value_t arraybuffer_ctor = js_obj_to_func(arraybuffer_ctor_obj); 2061 + js_set(js, arraybuffer_proto, "constructor", arraybuffer_ctor); 2062 + js_set_descriptor(js, arraybuffer_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 2063 + js_set(js, glob, "ArrayBuffer", arraybuffer_ctor); 2061 2064 2062 2065 ant_value_t typedarray_proto = js_mkobj(js); 2063 2066 js_set_proto_init(typedarray_proto, object_proto); ··· 2091 2094 ant_value_t name##_ctor_obj = js_mkobj(js); \ 2092 2095 ant_value_t name##_proto = js_mkobj(js); \ 2093 2096 js_set_proto_init(name##_proto, typedarray_proto); \ 2094 - js_setprop(js, name##_proto, ANT_STRING("constructor"), js_obj_to_func(name##_ctor_obj)); \ 2095 - js_set_descriptor(js, name##_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); \ 2096 2097 js_set_sym(js, name##_proto, get_toStringTag_sym(), js_mkstr(js, #name, sizeof(#name) - 1)); \ 2097 2098 js_set_slot(name##_ctor_obj, SLOT_CFUNC, js_mkfun(js_##name##_constructor)); \ 2098 2099 js_setprop(js, name##_ctor_obj, js_mkstr(js, "prototype", 9), name##_proto); \ 2099 2100 js_mkprop_fast(js, name##_ctor_obj, "name", 4, ANT_STRING(#name)); \ 2100 2101 js_set_descriptor(js, name##_ctor_obj, "name", 4, 0); \ 2101 2102 js_define_species_getter(js, name##_ctor_obj); \ 2102 - js_set(js, glob, #name, js_obj_to_func(name##_ctor_obj)); \ 2103 + ant_value_t name##_ctor = js_obj_to_func(name##_ctor_obj); \ 2104 + js_setprop(js, name##_proto, ANT_STRING("constructor"), name##_ctor); \ 2105 + js_set_descriptor(js, name##_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); \ 2106 + js_set(js, glob, #name, name##_ctor); \ 2103 2107 } while(0) 2104 2108 2105 2109 SETUP_TYPEDARRAY(Int8Array); ··· 2134 2138 js_mkprop_fast(js, dataview_ctor_obj, "prototype", 9, dataview_proto); 2135 2139 js_mkprop_fast(js, dataview_ctor_obj, "name", 4, ANT_STRING("DataView")); 2136 2140 js_set_descriptor(js, dataview_ctor_obj, "name", 4, 0); 2137 - js_set(js, glob, "DataView", js_obj_to_func(dataview_ctor_obj)); 2141 + 2142 + ant_value_t dataview_ctor = js_obj_to_func(dataview_ctor_obj); 2143 + js_set(js, dataview_proto, "constructor", dataview_ctor); 2144 + js_set_descriptor(js, dataview_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 2145 + js_set(js, glob, "DataView", dataview_ctor); 2138 2146 2139 2147 ant_value_t sharedarraybuffer_ctor_obj = js_mkobj(js); 2140 2148 ant_value_t sharedarraybuffer_proto = js_mkobj(js); ··· 2147 2155 js_mkprop_fast(js, sharedarraybuffer_ctor_obj, "prototype", 9, sharedarraybuffer_proto); 2148 2156 js_mkprop_fast(js, sharedarraybuffer_ctor_obj, "name", 4, ANT_STRING("SharedArrayBuffer")); 2149 2157 js_set_descriptor(js, sharedarraybuffer_ctor_obj, "name", 4, 0); 2150 - js_set(js, glob, "SharedArrayBuffer", js_obj_to_func(sharedarraybuffer_ctor_obj)); 2158 + 2159 + ant_value_t sharedarraybuffer_ctor = js_obj_to_func(sharedarraybuffer_ctor_obj); 2160 + js_set(js, sharedarraybuffer_proto, "constructor", sharedarraybuffer_ctor); 2161 + js_set_descriptor(js, sharedarraybuffer_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 2162 + js_set(js, glob, "SharedArrayBuffer", sharedarraybuffer_ctor); 2151 2163 2152 2164 ant_value_t buffer_ctor_obj = js_mkobj(js); 2153 2165 ant_value_t buffer_proto = js_mkobj(js); ··· 2180 2192 js_mkprop_fast(js, buffer_ctor_obj, "prototype", 9, buffer_proto); 2181 2193 js_mkprop_fast(js, buffer_ctor_obj, "name", 4, ANT_STRING("Buffer")); 2182 2194 js_set_descriptor(js, buffer_ctor_obj, "name", 4, 0); 2183 - js_set(js, glob, "Buffer", js_obj_to_func(buffer_ctor_obj)); 2195 + 2196 + ant_value_t buffer_ctor = js_obj_to_func(buffer_ctor_obj); 2197 + js_set(js, buffer_proto, "constructor", buffer_ctor); 2198 + js_set_descriptor(js, buffer_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); 2199 + js_set(js, glob, "Buffer", buffer_ctor); 2184 2200 } 2185 2201 2186 2202 void cleanup_buffer_module(void) {
+150
src/streams/brotli.c
··· 1 + #include <stdlib.h> 2 + #include <string.h> 3 + 4 + #include <brotli/encode.h> 5 + #include <brotli/decode.h> 6 + 7 + #include "ant.h" 8 + #include "errors.h" 9 + #include "internal.h" 10 + #include "modules/buffer.h" 11 + #include "streams/brotli.h" 12 + #include "streams/transform.h" 13 + 14 + #define BROTLI_CHUNK_SIZE 16384 15 + 16 + struct brotli_stream_state { 17 + bool decompress; 18 + union { 19 + BrotliEncoderState *enc; 20 + BrotliDecoderState *dec; 21 + } u; 22 + }; 23 + 24 + static ant_value_t brotli_enqueue_buffer( 25 + ant_t *js, ant_value_t ctrl_obj, const uint8_t *data, size_t len 26 + ) { 27 + ArrayBufferData *ab = create_array_buffer_data(len); 28 + if (!ab) return js_mkerr(js, "out of memory"); 29 + memcpy(ab->data, data, len); 30 + ant_value_t arr = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Uint8Array"); 31 + if (!ts_is_controller(ctrl_obj)) 32 + return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TransformStreamDefaultController"); 33 + return ts_ctrl_enqueue(js, ctrl_obj, arr); 34 + } 35 + 36 + static ant_value_t brotli_encoder_step( 37 + ant_t *js, brotli_stream_state_t *st, ant_value_t ctrl_obj, 38 + const uint8_t *input, size_t input_len, BrotliEncoderOperation op 39 + ) { 40 + size_t avail_in = input_len; 41 + const uint8_t *next_in = input; 42 + 43 + for (;;) { 44 + uint8_t out_buf[BROTLI_CHUNK_SIZE]; 45 + uint8_t *next_out = out_buf; 46 + size_t avail_out = sizeof(out_buf); 47 + 48 + if (!BrotliEncoderCompressStream( 49 + st->u.enc, op, &avail_in, &next_in, &avail_out, &next_out, NULL)) { 50 + return js_mkerr_typed(js, JS_ERR_TYPE, "Compression failed"); 51 + } 52 + 53 + size_t have = sizeof(out_buf) - avail_out; 54 + if (have > 0) { 55 + ant_value_t r = brotli_enqueue_buffer(js, ctrl_obj, out_buf, have); 56 + if (is_err(r)) return r; 57 + } 58 + 59 + if (op == BROTLI_OPERATION_FINISH) { 60 + if (BrotliEncoderIsFinished(st->u.enc) && 61 + !BrotliEncoderHasMoreOutput(st->u.enc)) 62 + break; 63 + } else if (avail_in == 0 && !BrotliEncoderHasMoreOutput(st->u.enc)) break; 64 + } 65 + 66 + return js_mkundef(); 67 + } 68 + 69 + static ant_value_t brotli_decoder_step( 70 + ant_t *js, brotli_stream_state_t *st, ant_value_t ctrl_obj, 71 + const uint8_t *input, size_t input_len 72 + ) { 73 + size_t avail_in = input_len; 74 + const uint8_t *next_in = input; 75 + 76 + for (;;) { 77 + uint8_t out_buf[BROTLI_CHUNK_SIZE]; 78 + uint8_t *next_out = out_buf; 79 + size_t avail_out = sizeof(out_buf); 80 + 81 + BrotliDecoderResult ret = BrotliDecoderDecompressStream( 82 + st->u.dec, &avail_in, &next_in, &avail_out, &next_out, NULL); 83 + 84 + size_t have = sizeof(out_buf) - avail_out; 85 + if (have > 0) { 86 + ant_value_t r = brotli_enqueue_buffer(js, ctrl_obj, out_buf, have); 87 + if (is_err(r)) return r; 88 + } 89 + 90 + if (ret == BROTLI_DECODER_RESULT_ERROR) 91 + return js_mkerr_typed(js, JS_ERR_TYPE, "Decompression failed"); 92 + if (ret == BROTLI_DECODER_RESULT_SUCCESS) break; 93 + if (ret == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT || 94 + BrotliDecoderHasMoreOutput(st->u.dec)) continue; 95 + if (ret == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) break; 96 + } 97 + 98 + return js_mkundef(); 99 + } 100 + 101 + brotli_stream_state_t *brotli_stream_state_new(bool decompress) { 102 + brotli_stream_state_t *st = calloc(1, sizeof(*st)); 103 + if (!st) return NULL; 104 + 105 + st->decompress = decompress; 106 + if (decompress) { 107 + st->u.dec = BrotliDecoderCreateInstance(NULL, NULL, NULL); 108 + if (!st->u.dec) { 109 + free(st); 110 + return NULL; 111 + }} else { 112 + st->u.enc = BrotliEncoderCreateInstance(NULL, NULL, NULL); 113 + if (!st->u.enc) { 114 + free(st); 115 + return NULL; 116 + }} 117 + 118 + return st; 119 + } 120 + 121 + void brotli_stream_state_destroy(brotli_stream_state_t *st) { 122 + if (!st) return; 123 + if (st->decompress) BrotliDecoderDestroyInstance(st->u.dec); 124 + else BrotliEncoderDestroyInstance(st->u.enc); 125 + free(st); 126 + } 127 + 128 + ant_value_t brotli_stream_transform( 129 + ant_t *js, brotli_stream_state_t *st, 130 + ant_value_t ctrl_obj, const uint8_t *input, size_t input_len 131 + ) { 132 + if (st->decompress) 133 + return brotli_decoder_step(js, st, ctrl_obj, input, input_len); 134 + return brotli_encoder_step(js, st, ctrl_obj, input, input_len, BROTLI_OPERATION_PROCESS); 135 + } 136 + 137 + ant_value_t brotli_stream_flush( 138 + ant_t *js, brotli_stream_state_t *st, 139 + ant_value_t ctrl_obj 140 + ) { 141 + if (st->decompress) { 142 + ant_value_t result = brotli_decoder_step(js, st, ctrl_obj, NULL, 0); 143 + if (is_err(result)) return result; 144 + if (!BrotliDecoderIsFinished(st->u.dec)) 145 + return js_mkerr_typed(js, JS_ERR_TYPE, "Decompression failed"); 146 + return js_mkundef(); 147 + } 148 + 149 + return brotli_encoder_step(js, st, ctrl_obj, NULL, 0, BROTLI_OPERATION_FINISH); 150 + }
+88 -22
src/streams/compression.c
··· 10 10 11 11 #include "modules/symbol.h" 12 12 #include "modules/buffer.h" 13 + #include "streams/brotli.h" 13 14 #include "streams/compression.h" 14 15 #include "streams/transform.h" 15 16 ··· 27 28 case ZFMT_GZIP: return decompress ? (15 + 32) : (15 + 16); 28 29 case ZFMT_DEFLATE: return 15; 29 30 case ZFMT_DEFLATE_RAW: return -15; 31 + case ZFMT_BROTLI: return 15; 30 32 } 31 33 return 15; 32 34 } ··· 39 41 if (len == 4 && !memcmp(s, "gzip", 4)) { *out = ZFMT_GZIP; return 0; } 40 42 if (len == 7 && !memcmp(s, "deflate", 7)) { *out = ZFMT_DEFLATE; return 0; } 41 43 if (len == 11 && !memcmp(s, "deflate-raw", 11)) { *out = ZFMT_DEFLATE_RAW; return 0; } 44 + if (len == 6 && !memcmp(s, "brotli", 6)) { *out = ZFMT_BROTLI; return 0; } 42 45 return -1; 43 46 } 44 47 45 48 static ant_value_t get_ts(ant_value_t obj) { 46 49 return js_get_slot(obj, SLOT_ENTRIES); 50 + } 51 + 52 + static zformat_t get_wrapper_format(ant_value_t wrapper) { 53 + ant_value_t fmt = js_get_slot(wrapper, SLOT_CTOR); 54 + return (zformat_t)(int)js_getnum(fmt); 47 55 } 48 56 49 57 bool cs_is_stream(ant_value_t obj) { ··· 98 106 }} 99 107 } 100 108 109 + static void brotli_state_finalize(ant_t *js, ant_object_t *obj) { 110 + if (!obj->extra_slots) return; 111 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 112 + 113 + for (uint8_t i = 0; i < obj->extra_count; i++) { 114 + if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 115 + brotli_stream_state_t *st = 116 + (brotli_stream_state_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 117 + brotli_stream_state_destroy(st); 118 + return; 119 + }} 120 + } 121 + 101 122 static bool get_chunk_bytes(ant_t *js, ant_value_t chunk, const uint8_t **out, size_t *len) { 102 123 ant_value_t slot = js_get_slot(chunk, SLOT_BUFFER); 103 124 TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); ··· 162 183 163 184 if (!input || input_len == 0) return js_mkundef(); 164 185 186 + if (get_wrapper_format(wrapper) == ZFMT_BROTLI) { 187 + brotli_stream_state_t *brotli_st = 188 + (brotli_stream_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 189 + return brotli_stream_transform(js, brotli_st, ctrl_obj, input, input_len); 190 + } 191 + 165 192 st->strm.next_in = (Bytef *)input; 166 193 st->strm.avail_in = (uInt)input_len; 167 194 ··· 185 212 static ant_value_t cs_flush(ant_t *js, ant_value_t *args, int nargs) { 186 213 ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 187 214 ant_value_t state_val = js_get_slot(wrapper, SLOT_DATA); 188 - zstate_t *st = (zstate_t *)(uintptr_t)(size_t)js_getnum(state_val); 189 215 ant_value_t ctrl_obj = (nargs > 0) ? args[0] : js_mkundef(); 190 216 217 + if (get_wrapper_format(wrapper) == ZFMT_BROTLI) { 218 + brotli_stream_state_t *brotli_st = 219 + (brotli_stream_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 220 + return brotli_stream_flush(js, brotli_st, ctrl_obj); 221 + } zstate_t *st = (zstate_t *)(uintptr_t)(size_t)js_getnum(state_val); 222 + 191 223 st->strm.next_in = NULL; 192 224 st->strm.avail_in = 0; 193 225 ··· 231 263 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'CompressionStream': Unsupported compression format"); 232 264 233 265 zstate_t *st = calloc(1, sizeof(zstate_t)); 234 - if (!st) return js_mkerr(js, "out of memory"); 235 - st->format = fmt; 236 - 237 - int ret = deflateInit2(&st->strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 238 - zfmt_window_bits(fmt, false), 8, Z_DEFAULT_STRATEGY); 239 - if (ret != Z_OK) { free(st); return js_mkerr(js, "Failed to initialize compression"); } 240 - st->initialized = true; 266 + brotli_stream_state_t *brotli = NULL; 267 + if (fmt == ZFMT_BROTLI) { 268 + brotli = brotli_stream_state_new(false); 269 + if (!brotli) return js_mkerr(js, "Failed to initialize compression"); 270 + } else { 271 + if (!st) return js_mkerr(js, "out of memory"); 272 + st->format = fmt; 273 + int ret = deflateInit2(&st->strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 274 + zfmt_window_bits(fmt, false), 8, Z_DEFAULT_STRATEGY); 275 + if (ret != Z_OK) { free(st); return js_mkerr(js, "Failed to initialize compression"); } 276 + st->initialized = true; 277 + } 241 278 242 279 ant_value_t obj = js_mkobj(js); 243 280 ant_value_t proto = js_instance_proto_from_new_target(js, g_cs_proto); 244 281 if (is_object_type(proto)) js_set_proto_init(obj, proto); 245 - js_set_slot(obj, SLOT_DATA, ANT_PTR(st)); 246 - js_set_finalizer(obj, zstate_finalize); 282 + js_set_slot(obj, SLOT_DATA, fmt == ZFMT_BROTLI ? ANT_PTR(brotli) : ANT_PTR(st)); 283 + js_set_finalizer(obj, fmt == ZFMT_BROTLI ? brotli_state_finalize : zstate_finalize); 247 284 248 285 ant_value_t wrapper = js_mkobj(js); 249 - js_set_slot(wrapper, SLOT_DATA, ANT_PTR(st)); 286 + js_set_slot(wrapper, SLOT_DATA, fmt == ZFMT_BROTLI ? ANT_PTR(brotli) : ANT_PTR(st)); 287 + js_set_slot(wrapper, SLOT_CTOR, js_mknum((double)fmt)); 250 288 251 289 ant_value_t transformer = js_mkobj(js); 252 290 ant_value_t transform_fn = js_heavy_mkfun(js, cs_transform, wrapper); ··· 263 301 js->new_target = saved_new_target; 264 302 js->this_val = saved_this; 265 303 266 - if (is_err(ts_obj)) { deflateEnd(&st->strm); free(st); return ts_obj; } 304 + if (is_err(ts_obj)) { 305 + if (fmt == ZFMT_BROTLI) brotli_stream_state_destroy(brotli); 306 + else { deflateEnd(&st->strm); free(st); } 307 + return ts_obj; 308 + } 267 309 js_set_slot(obj, SLOT_ENTRIES, ts_obj); 268 310 js_set_slot(wrapper, SLOT_ENTRIES, ts_obj); 269 311 ··· 284 326 return js_mkerr_typed(js, JS_ERR_TYPE, "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"); 285 327 286 328 if (!input || input_len == 0) return js_mkundef(); 329 + if (get_wrapper_format(wrapper) == ZFMT_BROTLI) { 330 + brotli_stream_state_t *brotli_st = 331 + (brotli_stream_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 332 + return brotli_stream_transform(js, brotli_st, ctrl_obj, input, input_len); 333 + } 334 + 287 335 st->strm.next_in = (Bytef *)input; 288 336 st->strm.avail_in = (uInt)input_len; 289 337 ··· 306 354 } 307 355 308 356 static ant_value_t ds_flush(ant_t *js, ant_value_t *args, int nargs) { 357 + ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 358 + ant_value_t state_val = js_get_slot(wrapper, SLOT_DATA); 359 + ant_value_t ctrl_obj = (nargs > 0) ? args[0] : js_mkundef(); 360 + if (get_wrapper_format(wrapper) == ZFMT_BROTLI) { 361 + brotli_stream_state_t *st = 362 + (brotli_stream_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 363 + return brotli_stream_flush(js, st, ctrl_obj); 364 + } 309 365 return js_mkundef(); 310 366 } 311 367 ··· 333 389 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'DecompressionStream': Unsupported compression format"); 334 390 335 391 zstate_t *st = calloc(1, sizeof(zstate_t)); 336 - if (!st) return js_mkerr(js, "out of memory"); 337 - st->format = fmt; 338 - 339 - int ret = inflateInit2(&st->strm, zfmt_window_bits(fmt, true)); 340 - if (ret != Z_OK) { free(st); return js_mkerr(js, "Failed to initialize decompression"); } 341 - st->initialized = true; 392 + brotli_stream_state_t *brotli = NULL; 393 + if (fmt == ZFMT_BROTLI) { 394 + brotli = brotli_stream_state_new(true); 395 + if (!brotli) return js_mkerr(js, "Failed to initialize decompression"); 396 + } else { 397 + if (!st) return js_mkerr(js, "out of memory"); 398 + st->format = fmt; 399 + int ret = inflateInit2(&st->strm, zfmt_window_bits(fmt, true)); 400 + if (ret != Z_OK) { free(st); return js_mkerr(js, "Failed to initialize decompression"); } 401 + st->initialized = true; 402 + } 342 403 343 404 ant_value_t obj = js_mkobj(js); 344 405 ant_value_t proto = js_instance_proto_from_new_target(js, g_ds_proto); 345 406 if (is_object_type(proto)) js_set_proto_init(obj, proto); 346 - js_set_slot(obj, SLOT_DATA, ANT_PTR(st)); 347 - js_set_finalizer(obj, zstate_inflate_finalize); 407 + js_set_slot(obj, SLOT_DATA, fmt == ZFMT_BROTLI ? ANT_PTR(brotli) : ANT_PTR(st)); 408 + js_set_finalizer(obj, fmt == ZFMT_BROTLI ? brotli_state_finalize : zstate_inflate_finalize); 348 409 349 410 ant_value_t wrapper = js_mkobj(js); 350 - js_set_slot(wrapper, SLOT_DATA, ANT_PTR(st)); 411 + js_set_slot(wrapper, SLOT_DATA, fmt == ZFMT_BROTLI ? ANT_PTR(brotli) : ANT_PTR(st)); 412 + js_set_slot(wrapper, SLOT_CTOR, js_mknum((double)fmt)); 351 413 352 414 ant_value_t transformer = js_mkobj(js); 353 415 ant_value_t transform_fn = js_heavy_mkfun(js, ds_transform, wrapper); ··· 365 427 366 428 js->new_target = saved_new_target; 367 429 js->this_val = saved_this; 368 - if (is_err(ts_obj)) { inflateEnd(&st->strm); free(st); return ts_obj; } 430 + if (is_err(ts_obj)) { 431 + if (fmt == ZFMT_BROTLI) brotli_stream_state_destroy(brotli); 432 + else { inflateEnd(&st->strm); free(st); } 433 + return ts_obj; 434 + } 369 435 370 436 js_set_slot(obj, SLOT_ENTRIES, ts_obj); 371 437 js_set_slot(wrapper, SLOT_ENTRIES, ts_obj);