MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <compat.h>
2
3#include <stdlib.h>
4#include <string.h>
5#include <zlib.h>
6
7#include "ant.h"
8#include "ptr.h"
9#include "errors.h"
10#include "internal.h"
11
12#include "silver/engine.h"
13#include "streams/brotli.h"
14#include "modules/buffer.h"
15#include "modules/events.h"
16#include "modules/symbol.h"
17#include "modules/zlib.h"
18
19#define ZLIB_CHUNK 16384
20#define ZLIB_STREAM_TAG 0x5A4C4942u
21
22typedef enum {
23 ZLIB_KIND_GZIP,
24 ZLIB_KIND_GUNZIP,
25 ZLIB_KIND_DEFLATE,
26 ZLIB_KIND_INFLATE,
27 ZLIB_KIND_DEFLATE_RAW,
28 ZLIB_KIND_INFLATE_RAW,
29 ZLIB_KIND_UNZIP,
30 ZLIB_KIND_BROTLI_COMPRESS,
31 ZLIB_KIND_BROTLI_DECOMPRESS,
32} zlib_kind_t;
33
34typedef struct zlib_stream_s {
35 z_stream strm;
36 brotli_stream_state_t *brotli;
37 ant_value_t obj;
38 ant_t *js;
39 zlib_kind_t kind;
40 uint32_t bytes_written;
41 bool initialized;
42 bool ended;
43 bool destroyed;
44 struct zlib_stream_s *next_active;
45} zlib_stream_t;
46
47static ant_value_t g_transform_proto = 0;
48static zlib_stream_t *g_active_streams = NULL;
49
50static bool zlib_kind_is_compress(zlib_kind_t k) {
51 return
52 k == ZLIB_KIND_GZIP
53 || k == ZLIB_KIND_DEFLATE
54 || k == ZLIB_KIND_DEFLATE_RAW
55 || k == ZLIB_KIND_BROTLI_COMPRESS;
56}
57
58static int zlib_window_bits(zlib_kind_t k) {
59 switch (k) {
60 case ZLIB_KIND_GZIP: return 15 + 16;
61 case ZLIB_KIND_GUNZIP: return 15 + 32;
62 case ZLIB_KIND_UNZIP: return 15 + 32;
63 case ZLIB_KIND_DEFLATE: return 15;
64 case ZLIB_KIND_INFLATE: return 15;
65 case ZLIB_KIND_DEFLATE_RAW: return -15;
66 case ZLIB_KIND_INFLATE_RAW: return -15;
67 default: return 15;
68 }
69}
70
71static ant_value_t pick_callback(ant_value_t *args, int nargs) {
72 if (nargs > 2 && is_callable(args[2])) return args[2];
73 if (nargs > 1 && is_callable(args[1])) return args[1];
74 return js_mkundef();
75}
76
77static zlib_stream_t *zlib_stream_ptr(ant_value_t obj) {
78 if (!js_check_native_tag(obj, ZLIB_STREAM_TAG)) return NULL;
79 return (zlib_stream_t *)js_get_native_ptr(obj);
80}
81
82static void zlib_add_active(zlib_stream_t *st) {
83 st->next_active = g_active_streams;
84 g_active_streams = st;
85}
86
87static void zlib_remove_active(zlib_stream_t *st) {
88 zlib_stream_t **it;
89 for (it = &g_active_streams; *it; it = &(*it)->next_active) {
90 if (*it == st) {
91 *it = st->next_active;
92 st->next_active = NULL;
93 return;
94 }}
95}
96
97static void zlib_stream_release(zlib_stream_t *st) {
98 if (!st) return;
99 if (st->brotli) {
100 brotli_stream_state_destroy(st->brotli);
101 st->brotli = NULL;
102 } else if (st->initialized) {
103 if (zlib_kind_is_compress(st->kind)) deflateEnd(&st->strm);
104 else inflateEnd(&st->strm);
105 st->initialized = false;
106 }
107}
108
109static ant_value_t zlib_make_buffer(ant_t *js, const uint8_t *data, size_t len) {
110 ArrayBufferData *ab = create_array_buffer_data(len);
111 if (!ab) return js_mkerr(js, "out of memory");
112 if (data && len > 0) memcpy(ab->data, data, len);
113 return create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Buffer");
114}
115
116static void zlib_emit_data(ant_t *js, ant_value_t obj, const uint8_t *data, size_t len) {
117 ant_value_t buf = zlib_make_buffer(js, data, len);
118 if (!is_err(buf)) eventemitter_emit_args(js, obj, "data", &buf, 1);
119}
120
121typedef struct { ant_t *js; ant_value_t obj; } brotli_emit_ctx_t;
122typedef struct { ant_t *js; ant_value_t arr; bool error; } brotli_collect_ctx_t;
123
124static int brotli_emit_cb(void *ctx, const uint8_t *chunk, size_t len) {
125 brotli_emit_ctx_t *ec = (brotli_emit_ctx_t *)ctx;
126 zlib_emit_data(ec->js, ec->obj, chunk, len);
127 return 0;
128}
129
130static int brotli_collect_cb(void *ctx, const uint8_t *chunk, size_t len) {
131 brotli_collect_ctx_t *ec = (brotli_collect_ctx_t *)ctx;
132 ant_value_t buf = zlib_make_buffer(ec->js, chunk, len);
133 if (is_err(buf)) { ec->error = true; return -1; }
134 js_arr_push(ec->js, ec->arr, buf);
135 return 0;
136}
137
138static ant_value_t zlib_do_process(
139 ant_t *js, zlib_stream_t *st,
140 const uint8_t *input, size_t input_len,
141 bool finish
142) {
143 if (st->destroyed || st->ended) return js_mkundef();
144
145 if (st->brotli) {
146 brotli_emit_ctx_t ctx = { js, st->obj };
147 int rc;
148 if (finish) {
149 rc = brotli_stream_finish(st->brotli, brotli_emit_cb, &ctx);
150 } else {
151 rc = brotli_stream_process(st->brotli, input, input_len, brotli_emit_cb, &ctx);
152 }
153 if (rc < 0) return js_mkerr(js, "brotli operation failed");
154 return js_mkundef();
155 }
156
157 bool compress = zlib_kind_is_compress(st->kind);
158 int flush = finish ? Z_FINISH : Z_NO_FLUSH;
159 uint8_t out[ZLIB_CHUNK];
160
161 if (!finish) {
162 st->strm.next_in = (Bytef *)input;
163 st->strm.avail_in = (uInt)input_len;
164 } else {
165 st->strm.next_in = NULL;
166 st->strm.avail_in = 0;
167 }
168
169 int ret;
170 do {
171 st->strm.next_out = out;
172 st->strm.avail_out = ZLIB_CHUNK;
173
174 if (compress) {
175 ret = deflate(&st->strm, flush);
176 if (ret == Z_STREAM_ERROR) return js_mkerr(js, "zlib deflate error");
177 } else {
178 ret = inflate(&st->strm, flush == Z_FINISH ? Z_SYNC_FLUSH : flush);
179 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
180 return js_mkerr(js, "zlib inflate error");
181 }
182
183 size_t have = ZLIB_CHUNK - st->strm.avail_out;
184 if (have > 0) zlib_emit_data(js, st->obj, out, have);
185 if (ret == Z_STREAM_END) break;
186 } while (st->strm.avail_out == 0);
187
188 return js_mkundef();
189}
190
191static ant_value_t zlib_process_chunk_sync(
192 ant_t *js, zlib_stream_t *st,
193 const uint8_t *input, size_t input_len,
194 int flush_flag
195) {
196 ant_value_t arr = js_mkarr(js);
197
198 if (st->brotli) {
199 brotli_collect_ctx_t ctx = { js, arr, false };
200 int rc = 0;
201 if (input && input_len > 0)
202 rc = brotli_stream_process(st->brotli, input, input_len, brotli_collect_cb, &ctx);
203 if (rc >= 0 && flush_flag == Z_FINISH)
204 rc = brotli_stream_finish(st->brotli, brotli_collect_cb, &ctx);
205 if (rc < 0 || ctx.error) return js_mkerr(js, "brotli operation failed");
206 return arr;
207 }
208
209 bool compress = zlib_kind_is_compress(st->kind);
210 uint8_t out[ZLIB_CHUNK];
211
212 st->strm.next_in = input ? (Bytef *)input : NULL;
213 st->strm.avail_in = input ? (uInt)input_len : 0;
214
215 int ret;
216 do {
217 st->strm.next_out = out;
218 st->strm.avail_out = ZLIB_CHUNK;
219
220 if (compress) {
221 ret = deflate(&st->strm, flush_flag);
222 if (ret == Z_STREAM_ERROR) return js_mkerr(js, "zlib deflate error");
223 } else {
224 ret = inflate(&st->strm, flush_flag);
225 if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR)
226 return js_mkerr(js, "zlib inflate error");
227 }
228
229 size_t have = ZLIB_CHUNK - st->strm.avail_out;
230 if (have > 0) {
231 ant_value_t buf = zlib_make_buffer(js, out, have);
232 if (is_err(buf)) return buf;
233 js_arr_push(js, arr, buf);
234 }
235 if (ret == Z_STREAM_END) break;
236 } while (st->strm.avail_out == 0);
237
238 return arr;
239}
240
241static ant_value_t js_zlib_process_chunk(ant_t *js, ant_value_t *args, int nargs) {
242 zlib_stream_t *st = zlib_stream_ptr(js_getthis(js));
243 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid zlib stream");
244 if (st->destroyed || st->ended) return js_mkarr(js);
245
246 const uint8_t *input = NULL;
247 size_t input_len = 0;
248 if (nargs >= 1 && is_object_type(args[0]))
249 buffer_source_get_bytes(js, args[0], &input, &input_len);
250
251 int flush_flag = Z_NO_FLUSH;
252 if (nargs >= 2 && vtype(args[1]) == T_NUM)
253 flush_flag = (int)js_getnum(args[1]);
254
255 return zlib_process_chunk_sync(js, st, input, input_len, flush_flag);
256}
257
258static ant_value_t js_zlib_write(ant_t *js, ant_value_t *args, int nargs) {
259 zlib_stream_t *st = zlib_stream_ptr(js_getthis(js));
260 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid zlib stream");
261 if (st->destroyed) return js_false;
262 if (st->ended) return js_false;
263
264 if (nargs < 1 || vtype(args[0]) == T_UNDEF || vtype(args[0]) == T_NULL)
265 return js_true;
266
267 const uint8_t *bytes = NULL;
268 size_t len = 0;
269
270 if (is_object_type(args[0])) {
271 buffer_source_get_bytes(js, args[0], &bytes, &len);
272 }
273
274 if (!bytes) {
275 size_t slen = 0;
276 char *s = js_getstr(js, args[0], &slen);
277 if (s) { bytes = (const uint8_t *)s; len = slen; }
278 }
279
280 if (!bytes || len == 0) return js_true;
281 st->bytes_written += (uint32_t)len;
282
283 ant_value_t r = zlib_do_process(js, st, bytes, len, false);
284 if (is_err(r)) {
285 eventemitter_emit_args(js, st->obj, "error", &r, 1);
286 return js_false;
287 }
288
289 ant_value_t cb = pick_callback(args, nargs);
290 if (is_callable(cb)) {
291 ant_value_t null_val = js_mknull();
292 sv_vm_call(js->vm, js, cb, js_mkundef(), &null_val, 1, NULL, false);
293 }
294
295 return js_true;
296}
297
298static ant_value_t js_zlib_end(ant_t *js, ant_value_t *args, int nargs) {
299 zlib_stream_t *st = zlib_stream_ptr(js_getthis(js));
300 ant_value_t self = js_getthis(js);
301
302 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid zlib stream");
303 if (st->destroyed || st->ended) return self;
304
305 if (nargs > 0 && vtype(args[0]) != T_UNDEF && vtype(args[0]) != T_NULL) {
306 ant_value_t write_args[1] = { args[0] };
307 js_zlib_write(js, write_args, 1);
308 }
309
310 st->ended = true;
311 ant_value_t r = zlib_do_process(js, st, NULL, 0, true);
312
313 if (is_err(r)) {
314 eventemitter_emit_args(js, st->obj, "error", &r, 1);
315 return self;
316 }
317
318 eventemitter_emit_args(js, st->obj, "end", NULL, 0);
319 eventemitter_emit_args(js, st->obj, "finish", NULL, 0);
320 eventemitter_emit_args(js, st->obj, "close", NULL, 0);
321
322 ant_value_t cb = pick_callback(args, nargs);
323 if (is_callable(cb))
324 sv_vm_call(js->vm, js, cb, js_mkundef(), NULL, 0, NULL, false);
325
326 return self;
327}
328
329static ant_value_t js_zlib_destroy(ant_t *js, ant_value_t *args, int nargs) {
330 zlib_stream_t *st = zlib_stream_ptr(js_getthis(js));
331 ant_value_t self = js_getthis(js);
332
333 if (!st || st->destroyed) return self;
334 st->destroyed = true;
335 zlib_stream_release(st);
336
337 if (nargs > 0 && !js_truthy(js, args[0])) {
338 ant_value_t null_val = js_mknull();
339 eventemitter_emit_args(js, st->obj, "error", &null_val, 1);
340 }
341
342 eventemitter_emit_args(js, st->obj, "close", NULL, 0);
343 return self;
344}
345
346static ant_value_t js_zlib_pause(ant_t *js, ant_value_t *args, int nargs) {
347 return js_getthis(js);
348}
349
350static ant_value_t js_zlib_resume(ant_t *js, ant_value_t *args, int nargs) {
351 return js_getthis(js);
352}
353
354static ant_value_t js_zlib_unpipe(ant_t *js, ant_value_t *args, int nargs) {
355 return js_getthis(js);
356}
357
358static ant_value_t pipe_on_data(ant_t *js, ant_value_t *args, int nargs) {
359 ant_value_t fn = js_getcurrentfunc(js);
360 ant_value_t dest = js_get_slot(fn, SLOT_DATA);
361 ant_value_t write_fn = js_get(js, dest, "write");
362
363 if (is_callable(write_fn) && nargs > 0)
364 sv_vm_call(js->vm, js, write_fn, dest, args, 1, NULL, false);
365
366 return js_mkundef();
367}
368
369static ant_value_t pipe_on_end(ant_t *js, ant_value_t *args, int nargs) {
370 ant_value_t fn = js_getcurrentfunc(js);
371 ant_value_t dest = js_get_slot(fn, SLOT_DATA);
372 ant_value_t end_fn = js_get(js, dest, "end");
373
374 if (is_callable(end_fn))
375 sv_vm_call(js->vm, js, end_fn, dest, NULL, 0, NULL, false);
376
377 return js_mkundef();
378}
379
380static ant_value_t js_zlib_pipe(ant_t *js, ant_value_t *args, int nargs) {
381 if (nargs < 1 || !is_object_type(args[0])) return js_mkundef();
382 ant_value_t self = js_getthis(js);
383 ant_value_t dest = args[0];
384
385 ant_value_t data_handler = js_heavy_mkfun(js, pipe_on_data, dest);
386 ant_value_t end_handler = js_heavy_mkfun(js, pipe_on_end, dest);
387
388 eventemitter_add_listener(js, self, "data", data_handler, false);
389 eventemitter_add_listener(js, self, "end", end_handler, true);
390
391 return dest;
392}
393
394static ant_value_t js_zlib_get_bytes_written(ant_t *js, ant_value_t *args, int nargs) {
395 zlib_stream_t *st = zlib_stream_ptr(js_getthis(js));
396 if (!st) return js_mknum(0);
397 return js_mknum((double)st->bytes_written);
398}
399
400static ant_value_t zlib_create_stream(ant_t *js, zlib_kind_t kind) {
401 bool compress = zlib_kind_is_compress(kind);
402
403 zlib_stream_t *st = calloc(1, sizeof(zlib_stream_t));
404 if (!st) return js_mkerr(js, "out of memory");
405
406 st->kind = kind;
407 st->js = js;
408
409 if (kind == ZLIB_KIND_BROTLI_COMPRESS || kind == ZLIB_KIND_BROTLI_DECOMPRESS) {
410 st->brotli = brotli_stream_state_new(kind == ZLIB_KIND_BROTLI_DECOMPRESS);
411 if (!st->brotli) { free(st); return js_mkerr(js, "brotli init failed"); }
412 st->initialized = true;
413 } else {
414 int wbits = zlib_window_bits(kind);
415 int ret;
416 if (compress) {
417 ret = deflateInit2(&st->strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY);
418 } else ret = inflateInit2(&st->strm, wbits);
419
420 if (ret != Z_OK) { free(st); return js_mkerr(js, "zlib init failed"); }
421 st->initialized = true;
422 }
423
424 ant_value_t obj = js_mkobj(js);
425 if (is_object_type(g_transform_proto)) js_set_proto_init(obj, g_transform_proto);
426
427 js_set_native_ptr(obj, st);
428 js_set_native_tag(obj, ZLIB_STREAM_TAG);
429
430 js_set(js, obj, "readable", js_true);
431 js_set(js, obj, "writable", js_true);
432 js_set(js, obj, "_processChunk", js_mkfun(js_zlib_process_chunk));
433
434 ant_value_t handle_obj = js_mkobj(js);
435 js_set(js, handle_obj, "close", js_mkfun(js_zlib_destroy));
436 js_set(js, obj, "_handle", handle_obj);
437
438 st->obj = obj;
439 zlib_add_active(st);
440 return obj;
441}
442
443static ant_value_t js_create_gzip(ant_t *js, ant_value_t *args, int nargs) {
444 return zlib_create_stream(js, ZLIB_KIND_GZIP);
445}
446
447static ant_value_t js_create_gunzip(ant_t *js, ant_value_t *args, int nargs) {
448 return zlib_create_stream(js, ZLIB_KIND_GUNZIP);
449}
450
451static ant_value_t js_create_deflate(ant_t *js, ant_value_t *args, int nargs) {
452 return zlib_create_stream(js, ZLIB_KIND_DEFLATE);
453}
454
455static ant_value_t js_create_inflate(ant_t *js, ant_value_t *args, int nargs) {
456 return zlib_create_stream(js, ZLIB_KIND_INFLATE);
457}
458
459static ant_value_t js_create_deflate_raw(ant_t *js, ant_value_t *args, int nargs) {
460 return zlib_create_stream(js, ZLIB_KIND_DEFLATE_RAW);
461}
462
463static ant_value_t js_create_inflate_raw(ant_t *js, ant_value_t *args, int nargs) {
464 return zlib_create_stream(js, ZLIB_KIND_INFLATE_RAW);
465}
466
467static ant_value_t js_create_unzip(ant_t *js, ant_value_t *args, int nargs) {
468 return zlib_create_stream(js, ZLIB_KIND_UNZIP);
469}
470
471static ant_value_t js_create_brotli_compress(ant_t *js, ant_value_t *args, int nargs) {
472 return zlib_create_stream(js, ZLIB_KIND_BROTLI_COMPRESS);
473}
474
475static ant_value_t js_create_brotli_decompress(ant_t *js, ant_value_t *args, int nargs) {
476 return zlib_create_stream(js, ZLIB_KIND_BROTLI_DECOMPRESS);
477}
478
479typedef struct {
480 uint8_t *data;
481 size_t len;
482 size_t cap;
483 int error;
484} zbuf_t;
485
486static int zbuf_append(zbuf_t *b, const uint8_t *chunk, size_t n) {
487 if (b->len + n > b->cap) {
488 size_t newcap = b->cap ? b->cap * 2 : 4096;
489 while (newcap < b->len + n) newcap *= 2;
490 uint8_t *p = realloc(b->data, newcap);
491
492 if (!p) { b->error = 1; return -1; }
493 b->data = p;
494 b->cap = newcap;
495 }
496
497 memcpy(b->data + b->len, chunk, n);
498 b->len += n;
499
500 return 0;
501}
502
503static int zbuf_brotli_cb(void *ctx, const uint8_t *chunk, size_t n) {
504 return zbuf_append((zbuf_t *)ctx, chunk, n);
505}
506
507static ant_value_t zlib_sync_op(
508 ant_t *js, zlib_kind_t kind,
509 const uint8_t *input, size_t input_len
510) {
511 zbuf_t out = {0};
512 uint8_t tmp[ZLIB_CHUNK];
513 bool compress = zlib_kind_is_compress(kind);
514
515 if (kind == ZLIB_KIND_BROTLI_COMPRESS || kind == ZLIB_KIND_BROTLI_DECOMPRESS) {
516 brotli_stream_state_t *bs = brotli_stream_state_new(kind == ZLIB_KIND_BROTLI_DECOMPRESS);
517 if (!bs) return js_mkerr(js, "brotli init failed");
518
519 int rc = brotli_stream_process(bs, input, input_len, zbuf_brotli_cb, &out);
520 if (rc >= 0) rc = brotli_stream_finish(bs, zbuf_brotli_cb, &out);
521
522 brotli_stream_state_destroy(bs);
523 if (rc < 0 || out.error) { free(out.data); return js_mkerr(js, "brotli operation failed"); }
524 ant_value_t buf = zlib_make_buffer(js, out.data, out.len);
525 free(out.data);
526
527 return buf;
528 }
529
530 z_stream strm = {0};
531 int wbits = zlib_window_bits(kind);
532 int ret;
533
534 if (compress) ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, wbits, 8, Z_DEFAULT_STRATEGY);
535 else ret = inflateInit2(&strm, wbits);
536 if (ret != Z_OK) return js_mkerr(js, "zlib init failed");
537
538 strm.next_in = (Bytef *)input;
539 strm.avail_in = (uInt)input_len;
540
541 do {
542 strm.next_out = tmp;
543 strm.avail_out = ZLIB_CHUNK;
544 if (compress) ret = deflate(&strm, Z_FINISH);
545 else ret = inflate(&strm, Z_SYNC_FLUSH);
546 if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
547 if (compress) deflateEnd(&strm);
548 else inflateEnd(&strm);
549 free(out.data);
550 return js_mkerr(js, "zlib operation failed");
551 }
552 size_t have = ZLIB_CHUNK - strm.avail_out;
553 if (have > 0 && zbuf_append(&out, tmp, have) < 0) {
554 if (compress) deflateEnd(&strm);
555 else inflateEnd(&strm);
556 free(out.data);
557 return js_mkerr(js, "out of memory");
558 }
559 if (ret == Z_STREAM_END) break;
560 } while (strm.avail_out == 0 || strm.avail_in > 0);
561
562 if (compress) deflateEnd(&strm);
563 else inflateEnd(&strm);
564
565 ant_value_t buf = zlib_make_buffer(js, out.data, out.len);
566 free(out.data);
567 return buf;
568}
569
570static bool get_input_bytes(ant_t *js, ant_value_t val, const uint8_t **out_bytes, size_t *out_len) {
571 if (is_object_type(val) && buffer_source_get_bytes(js, val, out_bytes, out_len)) return true;
572 size_t slen = 0;
573 char *s = js_getstr(js, val, &slen);
574 if (s) { *out_bytes = (const uint8_t *)s; *out_len = slen; return true; }
575 return false;
576}
577
578static ant_value_t zlib_sync_fn(ant_t *js, ant_value_t *args, int nargs, zlib_kind_t kind) {
579 if (nargs < 1) return js_mkerr(js, "argument required");
580 const uint8_t *bytes = NULL;
581 size_t len = 0;
582 if (!get_input_bytes(js, args[0], &bytes, &len))
583 return js_mkerr_typed(js, JS_ERR_TYPE, "argument must be a string or Buffer");
584 return zlib_sync_op(js, kind, bytes, len);
585}
586
587static ant_value_t zlib_async_fn(ant_t *js, ant_value_t *args, int nargs, zlib_kind_t kind) {
588 if (nargs < 1) return js_mkerr(js, "argument required");
589
590 const uint8_t *bytes = NULL;
591 size_t len = 0;
592 ant_value_t cb = pick_callback(args, nargs);
593
594 if (!get_input_bytes(js, args[0], &bytes, &len))
595 return js_mkerr_typed(js, JS_ERR_TYPE, "argument must be a string or Buffer");
596
597 ant_value_t result = zlib_sync_op(js, kind, bytes, len);
598
599 if (is_callable(cb)) {
600 if (is_err(result)) {
601 ant_value_t argv[1] = { result };
602 sv_vm_call(js->vm, js, cb, js_mkundef(), argv, 1, NULL, false);
603 } else {
604 ant_value_t null_val = js_mknull();
605 ant_value_t argv[2] = { null_val, result };
606 sv_vm_call(js->vm, js, cb, js_mkundef(), argv, 2, NULL, false);
607 }
608 return js_mkundef();
609 }
610
611 return result;
612}
613
614#define ZLIB_SYNC_FN(name, kind) \
615 static ant_value_t js_##name##Sync(ant_t *js, ant_value_t *a, int n) { return zlib_sync_fn(js, a, n, kind); }
616
617#define ZLIB_ASYNC_FN(name, kind) \
618 static ant_value_t js_##name(ant_t *js, ant_value_t *a, int n) { return zlib_async_fn(js, a, n, kind); }
619
620ZLIB_SYNC_FN(gzip, ZLIB_KIND_GZIP)
621ZLIB_SYNC_FN(gunzip, ZLIB_KIND_GUNZIP)
622ZLIB_SYNC_FN(deflate, ZLIB_KIND_DEFLATE)
623ZLIB_SYNC_FN(inflate, ZLIB_KIND_INFLATE)
624ZLIB_SYNC_FN(deflateRaw, ZLIB_KIND_DEFLATE_RAW)
625ZLIB_SYNC_FN(inflateRaw, ZLIB_KIND_INFLATE_RAW)
626ZLIB_SYNC_FN(unzip, ZLIB_KIND_UNZIP)
627ZLIB_SYNC_FN(brotliCompress, ZLIB_KIND_BROTLI_COMPRESS)
628ZLIB_SYNC_FN(brotliDecompress, ZLIB_KIND_BROTLI_DECOMPRESS)
629
630ZLIB_ASYNC_FN(gzip, ZLIB_KIND_GZIP)
631ZLIB_ASYNC_FN(gunzip, ZLIB_KIND_GUNZIP)
632ZLIB_ASYNC_FN(deflate, ZLIB_KIND_DEFLATE)
633ZLIB_ASYNC_FN(inflate, ZLIB_KIND_INFLATE)
634ZLIB_ASYNC_FN(deflateRaw, ZLIB_KIND_DEFLATE_RAW)
635ZLIB_ASYNC_FN(inflateRaw, ZLIB_KIND_INFLATE_RAW)
636ZLIB_ASYNC_FN(unzip, ZLIB_KIND_UNZIP)
637ZLIB_ASYNC_FN(brotliCompress, ZLIB_KIND_BROTLI_COMPRESS)
638ZLIB_ASYNC_FN(brotliDecompress, ZLIB_KIND_BROTLI_DECOMPRESS)
639
640static ant_value_t js_zlib_crc32(ant_t *js, ant_value_t *args, int nargs) {
641 if (nargs < 1) return js_mkerr(js, "argument required");
642
643 const uint8_t *bytes = NULL;
644 size_t len = 0;
645 uLong init_val = 0;
646
647 if (nargs >= 2 && vtype(args[1]) == T_NUM)
648 init_val = (uLong)js_getnum(args[1]);
649
650 if (!get_input_bytes(js, args[0], &bytes, &len))
651 return js_mkerr_typed(js, JS_ERR_TYPE, "argument must be a string or Buffer");
652
653 uLong crc = crc32(init_val, bytes, (uInt)len);
654 return js_mknum((double)(uint32_t)crc);
655}
656
657static ant_value_t make_constants(ant_t *js) {
658 ant_value_t c = js_mkobj(js);
659
660 js_set(js, c, "Z_NO_FLUSH", js_mknum(Z_NO_FLUSH));
661 js_set(js, c, "Z_PARTIAL_FLUSH", js_mknum(Z_PARTIAL_FLUSH));
662 js_set(js, c, "Z_SYNC_FLUSH", js_mknum(Z_SYNC_FLUSH));
663 js_set(js, c, "Z_FULL_FLUSH", js_mknum(Z_FULL_FLUSH));
664 js_set(js, c, "Z_FINISH", js_mknum(Z_FINISH));
665 js_set(js, c, "Z_BLOCK", js_mknum(Z_BLOCK));
666
667 js_set(js, c, "Z_OK", js_mknum(Z_OK));
668 js_set(js, c, "Z_STREAM_END", js_mknum(Z_STREAM_END));
669 js_set(js, c, "Z_NEED_DICT", js_mknum(Z_NEED_DICT));
670 js_set(js, c, "Z_ERRNO", js_mknum(Z_ERRNO));
671 js_set(js, c, "Z_STREAM_ERROR", js_mknum(Z_STREAM_ERROR));
672 js_set(js, c, "Z_DATA_ERROR", js_mknum(Z_DATA_ERROR));
673 js_set(js, c, "Z_MEM_ERROR", js_mknum(Z_MEM_ERROR));
674 js_set(js, c, "Z_BUF_ERROR", js_mknum(Z_BUF_ERROR));
675 js_set(js, c, "Z_VERSION_ERROR", js_mknum(Z_VERSION_ERROR));
676
677 js_set(js, c, "Z_NO_COMPRESSION", js_mknum(Z_NO_COMPRESSION));
678 js_set(js, c, "Z_BEST_SPEED", js_mknum(Z_BEST_SPEED));
679 js_set(js, c, "Z_BEST_COMPRESSION", js_mknum(Z_BEST_COMPRESSION));
680 js_set(js, c, "Z_DEFAULT_COMPRESSION", js_mknum(Z_DEFAULT_COMPRESSION));
681
682 js_set(js, c, "Z_FILTERED", js_mknum(Z_FILTERED));
683 js_set(js, c, "Z_HUFFMAN_ONLY", js_mknum(Z_HUFFMAN_ONLY));
684 js_set(js, c, "Z_RLE", js_mknum(Z_RLE));
685 js_set(js, c, "Z_FIXED", js_mknum(Z_FIXED));
686 js_set(js, c, "Z_DEFAULT_STRATEGY", js_mknum(Z_DEFAULT_STRATEGY));
687
688 js_set(js, c, "BROTLI_OPERATION_PROCESS", js_mknum(0));
689 js_set(js, c, "BROTLI_OPERATION_FLUSH", js_mknum(1));
690 js_set(js, c, "BROTLI_OPERATION_FINISH", js_mknum(2));
691 js_set(js, c, "BROTLI_OPERATION_EMIT_METADATA", js_mknum(3));
692
693 js_set(js, c, "BROTLI_PARAM_MODE", js_mknum(0));
694 js_set(js, c, "BROTLI_MODE_GENERIC", js_mknum(0));
695 js_set(js, c, "BROTLI_MODE_TEXT", js_mknum(1));
696 js_set(js, c, "BROTLI_MODE_FONT", js_mknum(2));
697 js_set(js, c, "BROTLI_PARAM_QUALITY", js_mknum(1));
698 js_set(js, c, "BROTLI_MIN_QUALITY", js_mknum(0));
699 js_set(js, c, "BROTLI_MAX_QUALITY", js_mknum(11));
700 js_set(js, c, "BROTLI_DEFAULT_QUALITY", js_mknum(11));
701 js_set(js, c, "BROTLI_PARAM_LGWIN", js_mknum(2));
702 js_set(js, c, "BROTLI_MIN_WINDOW_BITS", js_mknum(10));
703 js_set(js, c, "BROTLI_MAX_WINDOW_BITS", js_mknum(24));
704 js_set(js, c, "BROTLI_DEFAULT_WINDOW", js_mknum(22));
705 js_set(js, c, "BROTLI_PARAM_SIZE_HINT", js_mknum(3));
706 js_set(js, c, "BROTLI_PARAM_LARGE_WINDOW", js_mknum(4));
707 js_set(js, c, "BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION", js_mknum(0));
708 js_set(js, c, "BROTLI_DECODER_PARAM_LARGE_WINDOW", js_mknum(1));
709
710 js_set(js, c, "ZSTD_e_continue", js_mknum(0));
711 js_set(js, c, "ZSTD_e_flush", js_mknum(1));
712 js_set(js, c, "ZSTD_e_end", js_mknum(2));
713
714 return c;
715}
716
717static void zlib_init_proto(ant_t *js) {
718 if (g_transform_proto) return;
719
720 ant_value_t events = events_library(js);
721 ant_value_t ee_ctor = js_get(js, events, "EventEmitter");
722 ant_value_t ee_proto = js_get(js, ee_ctor, "prototype");
723
724 g_transform_proto = js_mkobj(js);
725 js_set_proto_init(g_transform_proto, ee_proto);
726
727 js_set(js, g_transform_proto, "write", js_mkfun(js_zlib_write));
728 js_set(js, g_transform_proto, "end", js_mkfun(js_zlib_end));
729 js_set(js, g_transform_proto, "destroy", js_mkfun(js_zlib_destroy));
730 js_set(js, g_transform_proto, "pause", js_mkfun(js_zlib_pause));
731 js_set(js, g_transform_proto, "resume", js_mkfun(js_zlib_resume));
732 js_set(js, g_transform_proto, "unpipe", js_mkfun(js_zlib_unpipe));
733 js_set(js, g_transform_proto, "pipe", js_mkfun(js_zlib_pipe));
734
735 js_set_getter_desc(js, g_transform_proto, "bytesWritten", 12,
736 js_mkfun(js_zlib_get_bytes_written), JS_DESC_C);
737}
738
739static ant_value_t zlib_mkctor(ant_t *js, ant_value_t (*fn)(ant_t *, ant_value_t *, int)) {
740 ant_value_t ctor = js_heavy_mkfun(js, fn, js_mkundef());
741 js_mark_constructor(ctor, true);
742 return ctor;
743}
744
745ant_value_t zlib_library(ant_t *js) {
746 zlib_init_proto(js);
747
748 ant_value_t lib = js_mkobj(js);
749 ant_value_t consts = make_constants(js);
750
751 js_set(js, lib, "gzip", js_mkfun(js_gzip));
752 js_set(js, lib, "gzipSync", js_mkfun(js_gzipSync));
753 js_set(js, lib, "Gzip", zlib_mkctor(js, js_create_gzip));
754 js_set(js, lib, "createGzip", js_mkfun(js_create_gzip));
755
756 js_set(js, lib, "gunzip", js_mkfun(js_gunzip));
757 js_set(js, lib, "gunzipSync", js_mkfun(js_gunzipSync));
758 js_set(js, lib, "Gunzip", zlib_mkctor(js, js_create_gunzip));
759 js_set(js, lib, "createGunzip", js_mkfun(js_create_gunzip));
760
761 js_set(js, lib, "deflate", js_mkfun(js_deflate));
762 js_set(js, lib, "deflateSync", js_mkfun(js_deflateSync));
763 js_set(js, lib, "Deflate", zlib_mkctor(js, js_create_deflate));
764 js_set(js, lib, "createDeflate", js_mkfun(js_create_deflate));
765
766 js_set(js, lib, "inflate", js_mkfun(js_inflate));
767 js_set(js, lib, "inflateSync", js_mkfun(js_inflateSync));
768 js_set(js, lib, "Inflate", zlib_mkctor(js, js_create_inflate));
769 js_set(js, lib, "createInflate", js_mkfun(js_create_inflate));
770
771 js_set(js, lib, "deflateRaw", js_mkfun(js_deflateRaw));
772 js_set(js, lib, "deflateRawSync", js_mkfun(js_deflateRawSync));
773 js_set(js, lib, "DeflateRaw", zlib_mkctor(js, js_create_deflate_raw));
774 js_set(js, lib, "createDeflateRaw", js_mkfun(js_create_deflate_raw));
775
776 js_set(js, lib, "inflateRaw", js_mkfun(js_inflateRaw));
777 js_set(js, lib, "inflateRawSync", js_mkfun(js_inflateRawSync));
778 js_set(js, lib, "InflateRaw", zlib_mkctor(js, js_create_inflate_raw));
779 js_set(js, lib, "createInflateRaw", js_mkfun(js_create_inflate_raw));
780
781 js_set(js, lib, "unzip", js_mkfun(js_unzip));
782 js_set(js, lib, "unzipSync", js_mkfun(js_unzipSync));
783 js_set(js, lib, "Unzip", zlib_mkctor(js, js_create_unzip));
784 js_set(js, lib, "createUnzip", js_mkfun(js_create_unzip));
785
786 js_set(js, lib, "brotliCompress", js_mkfun(js_brotliCompress));
787 js_set(js, lib, "brotliCompressSync", js_mkfun(js_brotliCompressSync));
788 js_set(js, lib, "BrotliCompress", zlib_mkctor(js, js_create_brotli_compress));
789 js_set(js, lib, "createBrotliCompress", js_mkfun(js_create_brotli_compress));
790
791 js_set(js, lib, "brotliDecompress", js_mkfun(js_brotliDecompress));
792 js_set(js, lib, "brotliDecompressSync", js_mkfun(js_brotliDecompressSync));
793 js_set(js, lib, "BrotliDecompress", zlib_mkctor(js, js_create_brotli_decompress));
794 js_set(js, lib, "createBrotliDecompress", js_mkfun(js_create_brotli_decompress));
795
796 js_set(js, lib, "default", lib);
797 js_set(js, lib, "constants", consts);
798 js_set(js, lib, "crc32", js_mkfun(js_zlib_crc32));
799 js_set_sym(js, lib, get_toStringTag_sym(), js_mkstr(js, "zlib", 4));
800
801 return lib;
802}
803
804void gc_mark_zlib(ant_t *js, void (*mark)(ant_t *, ant_value_t)) {
805 if (g_transform_proto) mark(js, g_transform_proto);
806 for (zlib_stream_t *st = g_active_streams; st; st = st->next_active) mark(js, st->obj);
807}