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.

at master 207 lines 5.8 kB view raw
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 (32 * 1024) 15 16struct brotli_stream_state { 17 bool decompress; 18 union { 19 BrotliEncoderState *enc; 20 BrotliDecoderState *dec; 21 } u; 22}; 23 24static int brotli_emit_chunk( 25 brotli_stream_chunk_cb cb, void *ctx, 26 const uint8_t *data, size_t len 27) { 28 if (!cb || len == 0) return 0; 29 return cb(ctx, data, len); 30} 31 32static ant_value_t brotli_enqueue_buffer( 33 ant_t *js, ant_value_t ctrl_obj, const uint8_t *data, size_t len 34) { 35 ArrayBufferData *ab = create_array_buffer_data(len); 36 if (!ab) return js_mkerr(js, "out of memory"); 37 memcpy(ab->data, data, len); 38 ant_value_t arr = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Uint8Array"); 39 if (!ts_is_controller(ctrl_obj)) 40 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TransformStreamDefaultController"); 41 return ts_ctrl_enqueue(js, ctrl_obj, arr); 42} 43 44static int brotli_encoder_step( 45 brotli_stream_state_t *st, 46 const uint8_t *input, 47 size_t input_len, 48 BrotliEncoderOperation op, 49 brotli_stream_chunk_cb cb, 50 void *ctx 51) { 52 size_t avail_in = input_len; 53 const uint8_t *next_in = input; 54 55 for (;;) { 56 uint8_t out_buf[BROTLI_CHUNK_SIZE]; 57 uint8_t *next_out = out_buf; 58 size_t avail_out = sizeof(out_buf); 59 60 if (!BrotliEncoderCompressStream( 61 st->u.enc, op, &avail_in, &next_in, &avail_out, &next_out, NULL)) { 62 return -1; 63 } 64 65 size_t have = sizeof(out_buf) - avail_out; 66 if (brotli_emit_chunk(cb, ctx, out_buf, have) != 0) return -1; 67 68 if (op == BROTLI_OPERATION_FINISH) { 69 if (BrotliEncoderIsFinished(st->u.enc) && 70 !BrotliEncoderHasMoreOutput(st->u.enc)) 71 break; 72 } else if (avail_in == 0 && !BrotliEncoderHasMoreOutput(st->u.enc)) break; 73 } 74 75 return 0; 76} 77 78static int brotli_decoder_step( 79 brotli_stream_state_t *st, 80 const uint8_t *input, 81 size_t input_len, 82 brotli_stream_chunk_cb cb, 83 void *ctx 84) { 85 size_t avail_in = input_len; 86 const uint8_t *next_in = input; 87 88 for (;;) { 89 uint8_t out_buf[BROTLI_CHUNK_SIZE]; 90 uint8_t *next_out = out_buf; 91 size_t avail_out = sizeof(out_buf); 92 93 BrotliDecoderResult ret = BrotliDecoderDecompressStream( 94 st->u.dec, &avail_in, &next_in, &avail_out, &next_out, NULL); 95 96 size_t have = sizeof(out_buf) - avail_out; 97 if (brotli_emit_chunk(cb, ctx, out_buf, have) != 0) return -1; 98 99 if (ret == BROTLI_DECODER_RESULT_ERROR) return -1; 100 if (ret == BROTLI_DECODER_RESULT_SUCCESS) break; 101 if (ret == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT || 102 BrotliDecoderHasMoreOutput(st->u.dec)) continue; 103 if (ret == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) break; 104 } 105 106 return 0; 107} 108 109brotli_stream_state_t *brotli_stream_state_new(bool decompress) { 110 brotli_stream_state_t *st = calloc(1, sizeof(*st)); 111 if (!st) return NULL; 112 113 st->decompress = decompress; 114 if (decompress) { 115 st->u.dec = BrotliDecoderCreateInstance(NULL, NULL, NULL); 116 if (!st->u.dec) { 117 free(st); 118 return NULL; 119 }} else { 120 st->u.enc = BrotliEncoderCreateInstance(NULL, NULL, NULL); 121 if (!st->u.enc) { 122 free(st); 123 return NULL; 124 }} 125 126 return st; 127} 128 129void brotli_stream_state_destroy(brotli_stream_state_t *st) { 130 if (!st) return; 131 if (st->decompress) BrotliDecoderDestroyInstance(st->u.dec); 132 else BrotliEncoderDestroyInstance(st->u.enc); 133 free(st); 134} 135 136int brotli_stream_process( 137 brotli_stream_state_t *st, 138 const uint8_t *input, 139 size_t input_len, 140 brotli_stream_chunk_cb cb, 141 void *ctx 142) { 143 if (!st) return -1; 144 if (st->decompress) return brotli_decoder_step(st, input, input_len, cb, ctx); 145 return brotli_encoder_step(st, input, input_len, BROTLI_OPERATION_PROCESS, cb, ctx); 146} 147 148int brotli_stream_finish( 149 brotli_stream_state_t *st, 150 brotli_stream_chunk_cb cb, 151 void *ctx 152) { 153 if (!st) return -1; 154 155 if (st->decompress) { 156 if (brotli_decoder_step(st, NULL, 0, cb, ctx) != 0) return -1; 157 return BrotliDecoderIsFinished(st->u.dec) ? 0 : -1; 158 } 159 160 return brotli_encoder_step(st, NULL, 0, BROTLI_OPERATION_FINISH, cb, ctx); 161} 162 163bool brotli_stream_is_finished(brotli_stream_state_t *st) { 164 if (!st) return false; 165 if (st->decompress) return BrotliDecoderIsFinished(st->u.dec); 166 return BrotliEncoderIsFinished(st->u.enc); 167} 168 169typedef struct { 170 ant_t *js; 171 ant_value_t ctrl_obj; 172 ant_value_t error; 173} brotli_js_emit_ctx_t; 174 175static int brotli_enqueue_chunk_cb(void *ctx, const uint8_t *chunk, size_t len) { 176 brotli_js_emit_ctx_t *emit = (brotli_js_emit_ctx_t *)ctx; 177 ant_value_t result = brotli_enqueue_buffer(emit->js, emit->ctrl_obj, chunk, len); 178 if (is_err(result)) { 179 emit->error = result; 180 return -1; 181 } 182 return 0; 183} 184 185ant_value_t brotli_stream_transform( 186 ant_t *js, brotli_stream_state_t *st, 187 ant_value_t ctrl_obj, const uint8_t *input, size_t input_len 188) { 189 brotli_js_emit_ctx_t emit = { .js = js, .ctrl_obj = ctrl_obj, .error = js_mkundef() }; 190 if (brotli_stream_process(st, input, input_len, brotli_enqueue_chunk_cb, &emit) != 0) { 191 if (is_err(emit.error)) return emit.error; 192 return js_mkerr_typed(js, JS_ERR_TYPE, st && st->decompress ? "Decompression failed" : "Compression failed"); 193 } 194 return js_mkundef(); 195} 196 197ant_value_t brotli_stream_flush( 198 ant_t *js, brotli_stream_state_t *st, 199 ant_value_t ctrl_obj 200) { 201 brotli_js_emit_ctx_t emit = { .js = js, .ctrl_obj = ctrl_obj, .error = js_mkundef() }; 202 if (brotli_stream_finish(st, brotli_enqueue_chunk_cb, &emit) != 0) { 203 if (is_err(emit.error)) return emit.error; 204 return js_mkerr_typed(js, JS_ERR_TYPE, st && st->decompress ? "Decompression failed" : "Compression failed"); 205 } 206 return js_mkundef(); 207}