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 434 lines 16 kB view raw
1#include <stdlib.h> 2#include <string.h> 3#include <zlib.h> 4 5#include "ant.h" 6#include "ptr.h" 7#include "errors.h" 8#include "runtime.h" 9#include "internal.h" 10#include "descriptors.h" 11 12#include "modules/symbol.h" 13#include "modules/buffer.h" 14#include "streams/brotli.h" 15#include "streams/compression.h" 16#include "streams/transform.h" 17 18ant_value_t g_cs_proto; 19ant_value_t g_ds_proto; 20 21enum { 22 CS_Z_NATIVE_TAG = 0x43535a53u, // CSZS 23 DS_Z_NATIVE_TAG = 0x44535a53u, // DSZS 24 25 CS_BROTLI_NATIVE_TAG = 0x43534252u, // CSBR 26 DS_BROTLI_NATIVE_TAG = 0x44534252u // DSBR 27}; 28 29typedef struct { 30 z_stream strm; 31 zformat_t format; 32 bool initialized; 33} zstate_t; 34 35static int zfmt_window_bits(zformat_t fmt, bool decompress) { 36 switch (fmt) { 37 case ZFMT_GZIP: return decompress ? (15 + 32) : (15 + 16); 38 case ZFMT_DEFLATE: return 15; 39 case ZFMT_DEFLATE_RAW: return -15; 40 case ZFMT_BROTLI: return 15; 41 } 42 return 15; 43} 44 45static int parse_format(ant_t *js, ant_value_t arg, zformat_t *out) { 46 if (vtype(arg) != T_STR) return -1; 47 size_t len; 48 const char *s = js_getstr(js, arg, &len); 49 if (!s) return -1; 50 if (len == 4 && !memcmp(s, "gzip", 4)) { *out = ZFMT_GZIP; return 0; } 51 if (len == 7 && !memcmp(s, "deflate", 7)) { *out = ZFMT_DEFLATE; return 0; } 52 if (len == 11 && !memcmp(s, "deflate-raw", 11)) { *out = ZFMT_DEFLATE_RAW; return 0; } 53 if (len == 6 && !memcmp(s, "brotli", 6)) { *out = ZFMT_BROTLI; return 0; } 54 return -1; 55} 56 57static ant_value_t get_ts(ant_value_t obj) { 58 return js_get_slot(obj, SLOT_ENTRIES); 59} 60 61bool cs_is_stream(ant_value_t obj) { 62 return is_object_type(obj) 63 && (js_check_native_tag(obj, CS_Z_NATIVE_TAG) || js_check_native_tag(obj, CS_BROTLI_NATIVE_TAG)) 64 && ts_is_stream(get_ts(obj)); 65} 66 67bool ds_is_stream(ant_value_t obj) { 68 return is_object_type(obj) 69 && (js_check_native_tag(obj, DS_Z_NATIVE_TAG) || js_check_native_tag(obj, DS_BROTLI_NATIVE_TAG)) 70 && ts_is_stream(get_ts(obj)); 71} 72 73ant_value_t cs_stream_readable(ant_value_t obj) { 74 return ts_stream_readable(get_ts(obj)); 75} 76 77ant_value_t cs_stream_writable(ant_value_t obj) { 78 return ts_stream_writable(get_ts(obj)); 79} 80 81ant_value_t ds_stream_readable(ant_value_t obj) { 82 return ts_stream_readable(get_ts(obj)); 83} 84 85ant_value_t ds_stream_writable(ant_value_t obj) { 86 return ts_stream_writable(get_ts(obj)); 87} 88 89static void zstate_finalize(ant_t *js, ant_object_t *obj) { 90 ant_value_t value = js_obj_from_ptr(obj); 91 zstate_t *st = (zstate_t *)js_get_native(value, CS_Z_NATIVE_TAG); 92 if (!st) return; 93 if (st->initialized) deflateEnd(&st->strm); 94 free(st); 95 js_clear_native(value, CS_Z_NATIVE_TAG); 96} 97 98static void zstate_inflate_finalize(ant_t *js, ant_object_t *obj) { 99 ant_value_t value = js_obj_from_ptr(obj); 100 zstate_t *st = (zstate_t *)js_get_native(value, DS_Z_NATIVE_TAG); 101 if (!st) return; 102 if (st->initialized) inflateEnd(&st->strm); 103 free(st); 104 js_clear_native(value, DS_Z_NATIVE_TAG); 105} 106 107static void brotli_state_finalize(ant_t *js, ant_object_t *obj) { 108 ant_value_t value = js_obj_from_ptr(obj); 109 uint32_t tag = CS_BROTLI_NATIVE_TAG; 110 brotli_stream_state_t *st = (brotli_stream_state_t *)js_get_native(value, tag); 111 if (!st) { 112 tag = DS_BROTLI_NATIVE_TAG; 113 st = (brotli_stream_state_t *)js_get_native(value, tag); 114 } 115 if (!st) return; 116 brotli_stream_state_destroy(st); 117 js_clear_native(value, tag); 118} 119 120static ant_value_t enqueue_buffer(ant_t *js, ant_value_t ctrl_obj, const uint8_t *data, size_t len) { 121 ArrayBufferData *ab = create_array_buffer_data(len); 122 if (!ab) return js_mkerr(js, "out of memory"); 123 memcpy(ab->data, data, len); 124 ant_value_t arr = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Uint8Array"); 125 if (!ts_is_controller(ctrl_obj)) 126 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TransformStreamDefaultController"); 127 return ts_ctrl_enqueue(js, ctrl_obj, arr); 128} 129 130#define ZCHUNK_SIZE 16384 131 132static ant_value_t cs_transform(ant_t *js, ant_value_t *args, int nargs) { 133 ant_value_t ctrl_obj = (nargs > 1) ? args[1] : js_mkundef(); 134 ant_value_t chunk = (nargs > 0) ? args[0] : js_mkundef(); 135 136 const uint8_t *input = NULL; 137 size_t input_len = 0; 138 139 if (!is_object_type(chunk) || !buffer_source_get_bytes(js, chunk, &input, &input_len)) 140 return js_mkerr_typed(js, JS_ERR_TYPE, "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"); 141 142 if (!input || input_len == 0) return js_mkundef(); 143 144 brotli_stream_state_t *brotli_st = (brotli_stream_state_t *)js_get_native(js->current_func, CS_BROTLI_NATIVE_TAG); 145 if (brotli_st) 146 return brotli_stream_transform(js, brotli_st, ctrl_obj, input, input_len); 147 148 zstate_t *st = (zstate_t *)js_get_native(js->current_func, CS_Z_NATIVE_TAG); 149 if (!st) 150 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid CompressionStream"); 151 152 st->strm.next_in = (Bytef *)input; 153 st->strm.avail_in = (uInt)input_len; 154 155 uint8_t out_buf[ZCHUNK_SIZE]; 156 do { 157 st->strm.next_out = out_buf; 158 st->strm.avail_out = ZCHUNK_SIZE; 159 int ret = deflate(&st->strm, Z_NO_FLUSH); 160 if (ret == Z_STREAM_ERROR) 161 return js_mkerr_typed(js, JS_ERR_TYPE, "Compression failed"); 162 size_t have = ZCHUNK_SIZE - st->strm.avail_out; 163 if (have > 0) { 164 ant_value_t r = enqueue_buffer(js, ctrl_obj, out_buf, have); 165 if (is_err(r)) return r; 166 } 167 } while (st->strm.avail_out == 0); 168 169 return js_mkundef(); 170} 171 172static ant_value_t cs_flush(ant_t *js, ant_value_t *args, int nargs) { 173 ant_value_t ctrl_obj = (nargs > 0) ? args[0] : js_mkundef(); 174 175 brotli_stream_state_t *brotli_st = (brotli_stream_state_t *)js_get_native(js->current_func, CS_BROTLI_NATIVE_TAG); 176 if (brotli_st) 177 return brotli_stream_flush(js, brotli_st, ctrl_obj); 178 179 zstate_t *st = (zstate_t *)js_get_native(js->current_func, CS_Z_NATIVE_TAG); 180 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid CompressionStream"); 181 182 st->strm.next_in = NULL; 183 st->strm.avail_in = 0; 184 185 uint8_t out_buf[ZCHUNK_SIZE]; 186 int ret; 187 do { 188 st->strm.next_out = out_buf; 189 st->strm.avail_out = ZCHUNK_SIZE; 190 ret = deflate(&st->strm, Z_FINISH); 191 size_t have = ZCHUNK_SIZE - st->strm.avail_out; 192 if (have > 0) { 193 ant_value_t r = enqueue_buffer(js, ctrl_obj, out_buf, have); 194 if (is_err(r)) return r; 195 } 196 } while (ret != Z_STREAM_END); 197 198 return js_mkundef(); 199} 200 201static ant_value_t js_cs_get_readable(ant_t *js, ant_value_t *args, int nargs) { 202 ant_value_t ts_obj = get_ts(js->this_val); 203 if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid CompressionStream"); 204 return ts_stream_readable(ts_obj); 205} 206 207static ant_value_t js_cs_get_writable(ant_t *js, ant_value_t *args, int nargs) { 208 ant_value_t ts_obj = get_ts(js->this_val); 209 if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid CompressionStream"); 210 return ts_stream_writable(ts_obj); 211} 212 213static ant_value_t js_cs_ctor(ant_t *js, ant_value_t *args, int nargs) { 214 if (vtype(js->new_target) == T_UNDEF) 215 return js_mkerr_typed(js, JS_ERR_TYPE, "CompressionStream constructor requires 'new'"); 216 217 if (nargs < 1) 218 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'CompressionStream': 1 argument required"); 219 220 zformat_t fmt; 221 if (parse_format(js, args[0], &fmt) < 0) 222 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'CompressionStream': Unsupported compression format"); 223 224 zstate_t *st = calloc(1, sizeof(zstate_t)); 225 brotli_stream_state_t *brotli = NULL; 226 if (fmt == ZFMT_BROTLI) { 227 brotli = brotli_stream_state_new(false); 228 if (!brotli) return js_mkerr(js, "Failed to initialize compression"); 229 } else { 230 if (!st) return js_mkerr(js, "out of memory"); 231 st->format = fmt; 232 int ret = deflateInit2(&st->strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 233 zfmt_window_bits(fmt, false), 8, Z_DEFAULT_STRATEGY); 234 if (ret != Z_OK) { free(st); return js_mkerr(js, "Failed to initialize compression"); } 235 st->initialized = true; 236 } 237 238 ant_value_t obj = js_mkobj(js); 239 ant_value_t proto = js_instance_proto_from_new_target(js, g_cs_proto); 240 if (is_object_type(proto)) js_set_proto_init(obj, proto); 241 242 js_set_native(obj, fmt == ZFMT_BROTLI ? (void *)brotli : (void *)st, fmt == ZFMT_BROTLI ? CS_BROTLI_NATIVE_TAG : CS_Z_NATIVE_TAG); 243 js_set_finalizer(obj, fmt == ZFMT_BROTLI ? brotli_state_finalize : zstate_finalize); 244 245 ant_value_t transformer = js_mkobj(js); 246 247 ant_value_t transform_fn = js_heavy_mkfun_native(js, cs_transform, fmt == ZFMT_BROTLI 248 ? (void *)brotli : (void *)st, fmt == ZFMT_BROTLI 249 ? CS_BROTLI_NATIVE_TAG : CS_Z_NATIVE_TAG 250 ); 251 252 ant_value_t flush_fn = js_heavy_mkfun_native(js, cs_flush, fmt == ZFMT_BROTLI 253 ? (void *)brotli : (void *)st, fmt == ZFMT_BROTLI 254 ? CS_BROTLI_NATIVE_TAG : CS_Z_NATIVE_TAG 255 ); 256 257 js_set(js, transformer, "transform", transform_fn); 258 js_set(js, transformer, "flush", flush_fn); 259 260 ant_value_t ctor_args[1] = { transformer }; 261 ant_value_t saved_new_target = js->new_target; 262 ant_value_t saved_this = js->this_val; 263 js->new_target = js_mknum(1); 264 265 ant_value_t ts_obj = js_ts_ctor(js, ctor_args, 1); 266 js->new_target = saved_new_target; 267 js->this_val = saved_this; 268 269 if (is_err(ts_obj)) { 270 if (fmt == ZFMT_BROTLI) brotli_stream_state_destroy(brotli); 271 else { deflateEnd(&st->strm); free(st); } 272 return ts_obj; 273 } 274 js_set_slot(obj, SLOT_ENTRIES, ts_obj); 275 js_set_slot_wb(js, transform_fn, SLOT_ENTRIES, obj); 276 js_set_slot_wb(js, flush_fn, SLOT_ENTRIES, obj); 277 278 return obj; 279} 280 281static ant_value_t ds_transform(ant_t *js, ant_value_t *args, int nargs) { 282 ant_value_t ctrl_obj = (nargs > 1) ? args[1] : js_mkundef(); 283 284 ant_value_t chunk = (nargs > 0) ? args[0] : js_mkundef(); 285 const uint8_t *input = NULL; 286 size_t input_len = 0; 287 288 if (!is_object_type(chunk) || !buffer_source_get_bytes(js, chunk, &input, &input_len)) 289 return js_mkerr_typed(js, JS_ERR_TYPE, "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"); 290 291 if (!input || input_len == 0) return js_mkundef(); 292 brotli_stream_state_t *brotli_st = (brotli_stream_state_t *)js_get_native(js->current_func, DS_BROTLI_NATIVE_TAG); 293 if (brotli_st) 294 return brotli_stream_transform(js, brotli_st, ctrl_obj, input, input_len); 295 296 zstate_t *st = (zstate_t *)js_get_native(js->current_func, DS_Z_NATIVE_TAG); 297 if (!st) 298 return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid DecompressionStream"); 299 300 st->strm.next_in = (Bytef *)input; 301 st->strm.avail_in = (uInt)input_len; 302 303 uint8_t out_buf[ZCHUNK_SIZE]; 304 do { 305 st->strm.next_out = out_buf; 306 st->strm.avail_out = ZCHUNK_SIZE; 307 int ret = inflate(&st->strm, Z_NO_FLUSH); 308 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) 309 return js_mkerr_typed(js, JS_ERR_TYPE, "Decompression failed"); 310 size_t have = ZCHUNK_SIZE - st->strm.avail_out; 311 if (have > 0) { 312 ant_value_t r = enqueue_buffer(js, ctrl_obj, out_buf, have); 313 if (is_err(r)) return r; 314 } 315 if (ret == Z_STREAM_END) break; 316 } while (st->strm.avail_out == 0); 317 318 return js_mkundef(); 319} 320 321static ant_value_t ds_flush(ant_t *js, ant_value_t *args, int nargs) { 322 ant_value_t ctrl_obj = (nargs > 0) ? args[0] : js_mkundef(); 323 brotli_stream_state_t *st = (brotli_stream_state_t *)js_get_native(js->current_func, DS_BROTLI_NATIVE_TAG); 324 if (st) 325 return brotli_stream_flush(js, st, ctrl_obj); 326 return js_mkundef(); 327} 328 329static ant_value_t js_ds_get_readable(ant_t *js, ant_value_t *args, int nargs) { 330 ant_value_t ts_obj = get_ts(js->this_val); 331 if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid DecompressionStream"); 332 return ts_stream_readable(ts_obj); 333} 334 335static ant_value_t js_ds_get_writable(ant_t *js, ant_value_t *args, int nargs) { 336 ant_value_t ts_obj = get_ts(js->this_val); 337 if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid DecompressionStream"); 338 return ts_stream_writable(ts_obj); 339} 340 341static ant_value_t js_ds_ctor(ant_t *js, ant_value_t *args, int nargs) { 342 if (vtype(js->new_target) == T_UNDEF) 343 return js_mkerr_typed(js, JS_ERR_TYPE, "DecompressionStream constructor requires 'new'"); 344 345 if (nargs < 1) 346 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'DecompressionStream': 1 argument required"); 347 348 zformat_t fmt; 349 if (parse_format(js, args[0], &fmt) < 0) 350 return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'DecompressionStream': Unsupported compression format"); 351 352 zstate_t *st = calloc(1, sizeof(zstate_t)); 353 brotli_stream_state_t *brotli = NULL; 354 if (fmt == ZFMT_BROTLI) { 355 brotli = brotli_stream_state_new(true); 356 if (!brotli) return js_mkerr(js, "Failed to initialize decompression"); 357 } else { 358 if (!st) return js_mkerr(js, "out of memory"); 359 st->format = fmt; 360 int ret = inflateInit2(&st->strm, zfmt_window_bits(fmt, true)); 361 if (ret != Z_OK) { free(st); return js_mkerr(js, "Failed to initialize decompression"); } 362 st->initialized = true; 363 } 364 365 ant_value_t obj = js_mkobj(js); 366 ant_value_t proto = js_instance_proto_from_new_target(js, g_ds_proto); 367 if (is_object_type(proto)) js_set_proto_init(obj, proto); 368 369 js_set_native(obj, fmt == ZFMT_BROTLI ? (void *)brotli : (void *)st, fmt == ZFMT_BROTLI ? DS_BROTLI_NATIVE_TAG : DS_Z_NATIVE_TAG); 370 js_set_finalizer(obj, fmt == ZFMT_BROTLI ? brotli_state_finalize : zstate_inflate_finalize); 371 372 ant_value_t transformer = js_mkobj(js); 373 374 ant_value_t transform_fn = js_heavy_mkfun_native(js, ds_transform, fmt == ZFMT_BROTLI 375 ? (void *)brotli : (void *)st, fmt == ZFMT_BROTLI 376 ? DS_BROTLI_NATIVE_TAG : DS_Z_NATIVE_TAG 377 ); 378 379 ant_value_t flush_fn = js_heavy_mkfun_native(js, ds_flush, fmt == ZFMT_BROTLI 380 ? (void *)brotli : (void *)st, fmt == ZFMT_BROTLI 381 ? DS_BROTLI_NATIVE_TAG : DS_Z_NATIVE_TAG 382 ); 383 384 js_set(js, transformer, "transform", transform_fn); 385 js_set(js, transformer, "flush", flush_fn); 386 387 ant_value_t ctor_args[1] = { transformer }; 388 ant_value_t saved_new_target = js->new_target; 389 ant_value_t saved_this = js->this_val; 390 js->new_target = js_mknum(1); 391 392 ant_value_t ts_obj = js_ts_ctor(js, ctor_args, 1); 393 394 js->new_target = saved_new_target; 395 js->this_val = saved_this; 396 if (is_err(ts_obj)) { 397 if (fmt == ZFMT_BROTLI) brotli_stream_state_destroy(brotli); 398 else { inflateEnd(&st->strm); free(st); } 399 return ts_obj; 400 } 401 402 js_set_slot(obj, SLOT_ENTRIES, ts_obj); 403 js_set_slot_wb(js, transform_fn, SLOT_ENTRIES, obj); 404 js_set_slot_wb(js, flush_fn, SLOT_ENTRIES, obj); 405 return obj; 406} 407 408void init_compression_stream_module(void) { 409 ant_t *js = rt->js; 410 ant_value_t g = js_glob(js); 411 412 g_cs_proto = js_mkobj(js); 413 js_set_getter_desc(js, g_cs_proto, "readable", 8, js_mkfun(js_cs_get_readable), JS_DESC_C); 414 js_set_getter_desc(js, g_cs_proto, "writable", 8, js_mkfun(js_cs_get_writable), JS_DESC_C); 415 js_set_sym(js, g_cs_proto, get_toStringTag_sym(), js_mkstr(js, "CompressionStream", 17)); 416 417 ant_value_t cs_ctor = js_make_ctor(js, js_cs_ctor, g_cs_proto, "CompressionStream", 17); 418 js_set(js, g, "CompressionStream", cs_ctor); 419 js_set_descriptor(js, g, "CompressionStream", 17, JS_DESC_W | JS_DESC_C); 420 421 g_ds_proto = js_mkobj(js); 422 js_set_getter_desc(js, g_ds_proto, "readable", 8, js_mkfun(js_ds_get_readable), JS_DESC_C); 423 js_set_getter_desc(js, g_ds_proto, "writable", 8, js_mkfun(js_ds_get_writable), JS_DESC_C); 424 js_set_sym(js, g_ds_proto, get_toStringTag_sym(), js_mkstr(js, "DecompressionStream", 19)); 425 426 ant_value_t ds_ctor = js_make_ctor(js, js_ds_ctor, g_ds_proto, "DecompressionStream", 19); 427 js_set(js, g, "DecompressionStream", ds_ctor); 428 js_set_descriptor(js, g, "DecompressionStream", 19, JS_DESC_W | JS_DESC_C); 429} 430 431void gc_mark_compression_streams(ant_t *js, void (*mark)(ant_t *, ant_value_t)) { 432 mark(js, g_cs_proto); 433 mark(js, g_ds_proto); 434}