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.

stream-dependent APIs

+1304 -10
+162
examples/spec/streams-compression.js
··· 1 + import { test, testThrows, summary } from './helpers.js'; 2 + 3 + console.log('CompressionStream / DecompressionStream Tests\n'); 4 + 5 + test('CS typeof', typeof CompressionStream, 'function'); 6 + test('CS toStringTag', Object.prototype.toString.call(new CompressionStream('gzip')), '[object CompressionStream]'); 7 + 8 + testThrows('CS requires new', () => CompressionStream('gzip')); 9 + testThrows('CS requires format', () => new CompressionStream()); 10 + testThrows('CS rejects invalid format', () => new CompressionStream('invalid')); 11 + 12 + const cs0 = new CompressionStream('gzip'); 13 + test('CS readable is ReadableStream', cs0.readable instanceof ReadableStream, true); 14 + test('CS writable is WritableStream', cs0.writable instanceof WritableStream, true); 15 + 16 + test('DS typeof', typeof DecompressionStream, 'function'); 17 + test('DS toStringTag', Object.prototype.toString.call(new DecompressionStream('gzip')), '[object DecompressionStream]'); 18 + 19 + testThrows('DS requires new', () => DecompressionStream('gzip')); 20 + testThrows('DS requires format', () => new DecompressionStream()); 21 + testThrows('DS rejects invalid format', () => new DecompressionStream('invalid')); 22 + 23 + const ds0 = new DecompressionStream('gzip'); 24 + test('DS readable is ReadableStream', ds0.readable instanceof ReadableStream, true); 25 + test('DS writable is WritableStream', ds0.writable instanceof WritableStream, true); 26 + 27 + async function collectStream(readable) { 28 + const reader = readable.getReader(); 29 + const chunks = []; 30 + while (true) { 31 + const { value, done } = await reader.read(); 32 + if (done) break; 33 + chunks.push(value); 34 + } 35 + let total = 0; 36 + for (const c of chunks) total += c.byteLength; 37 + const result = new Uint8Array(total); 38 + let offset = 0; 39 + for (const c of chunks) { 40 + result.set(c, offset); 41 + offset += c.byteLength; 42 + } 43 + return result; 44 + } 45 + 46 + async function testGzipRoundTrip() { 47 + const input = new TextEncoder().encode('Hello, compression world!'); 48 + const cs = new CompressionStream('gzip'); 49 + const writer = cs.writable.getWriter(); 50 + writer.write(input); 51 + writer.close(); 52 + const compressed = await collectStream(cs.readable); 53 + test('gzip compressed is smaller or has gzip header', compressed[0], 0x1f); 54 + test('gzip compressed header byte 2', compressed[1], 0x8b); 55 + 56 + const ds = new DecompressionStream('gzip'); 57 + const writer2 = ds.writable.getWriter(); 58 + writer2.write(compressed); 59 + writer2.close(); 60 + const decompressed = await collectStream(ds.readable); 61 + test('gzip roundtrip length', decompressed.length, input.length); 62 + test('gzip roundtrip value', new TextDecoder().decode(decompressed), 'Hello, compression world!'); 63 + } 64 + 65 + async function testDeflateRoundTrip() { 66 + const input = new TextEncoder().encode('Deflate test data'); 67 + const cs = new CompressionStream('deflate'); 68 + const writer = cs.writable.getWriter(); 69 + writer.write(input); 70 + writer.close(); 71 + const compressed = await collectStream(cs.readable); 72 + test('deflate compressed length > 0', compressed.length > 0, true); 73 + 74 + const ds = new DecompressionStream('deflate'); 75 + const writer2 = ds.writable.getWriter(); 76 + writer2.write(compressed); 77 + writer2.close(); 78 + const decompressed = await collectStream(ds.readable); 79 + test('deflate roundtrip', new TextDecoder().decode(decompressed), 'Deflate test data'); 80 + } 81 + 82 + async function testDeflateRawRoundTrip() { 83 + const input = new TextEncoder().encode('Raw deflate test'); 84 + const cs = new CompressionStream('deflate-raw'); 85 + const writer = cs.writable.getWriter(); 86 + writer.write(input); 87 + writer.close(); 88 + const compressed = await collectStream(cs.readable); 89 + test('deflate-raw compressed length > 0', compressed.length > 0, true); 90 + 91 + const ds = new DecompressionStream('deflate-raw'); 92 + const writer2 = ds.writable.getWriter(); 93 + writer2.write(compressed); 94 + writer2.close(); 95 + const decompressed = await collectStream(ds.readable); 96 + test('deflate-raw roundtrip', new TextDecoder().decode(decompressed), 'Raw deflate test'); 97 + } 98 + 99 + async function testPipeThrough() { 100 + const input = 'pipe through compression test'; 101 + const encoded = new TextEncoder().encode(input); 102 + const rs = new ReadableStream({ 103 + start(c) { 104 + c.enqueue(encoded); 105 + c.close(); 106 + } 107 + }); 108 + const compressed = await collectStream(rs.pipeThrough(new CompressionStream('gzip'))); 109 + 110 + const rs2 = new ReadableStream({ 111 + start(c) { 112 + c.enqueue(compressed); 113 + c.close(); 114 + } 115 + }); 116 + const decompressed = await collectStream(rs2.pipeThrough(new DecompressionStream('gzip'))); 117 + test('pipeThrough roundtrip', new TextDecoder().decode(decompressed), input); 118 + } 119 + 120 + async function testMultipleChunks() { 121 + const cs = new CompressionStream('gzip'); 122 + const writer = cs.writable.getWriter(); 123 + writer.write(new TextEncoder().encode('chunk1 ')); 124 + writer.write(new TextEncoder().encode('chunk2 ')); 125 + writer.write(new TextEncoder().encode('chunk3')); 126 + writer.close(); 127 + const compressed = await collectStream(cs.readable); 128 + 129 + const ds = new DecompressionStream('gzip'); 130 + const writer2 = ds.writable.getWriter(); 131 + writer2.write(compressed); 132 + writer2.close(); 133 + const decompressed = await collectStream(ds.readable); 134 + test('multi-chunk roundtrip', new TextDecoder().decode(decompressed), 'chunk1 chunk2 chunk3'); 135 + } 136 + 137 + async function testLargeData() { 138 + const str = 'x'.repeat(100000); 139 + const input = new TextEncoder().encode(str); 140 + const cs = new CompressionStream('gzip'); 141 + const writer = cs.writable.getWriter(); 142 + writer.write(input); 143 + writer.close(); 144 + const compressed = await collectStream(cs.readable); 145 + test('large data compresses', compressed.length < input.length, true); 146 + 147 + const ds = new DecompressionStream('gzip'); 148 + const writer2 = ds.writable.getWriter(); 149 + writer2.write(compressed); 150 + writer2.close(); 151 + const decompressed = await collectStream(ds.readable); 152 + test('large data roundtrip length', decompressed.length, input.length); 153 + } 154 + 155 + await testGzipRoundTrip(); 156 + await testDeflateRoundTrip(); 157 + await testDeflateRawRoundTrip(); 158 + await testPipeThrough(); 159 + await testMultipleChunks(); 160 + await testLargeData(); 161 + 162 + summary();
+175
examples/spec/streams-encoding.js
··· 1 + import { test, testDeep, testThrows, summary } from './helpers.js'; 2 + 3 + console.log('TextEncoderStream / TextDecoderStream Tests\n'); 4 + 5 + test('TES typeof', typeof TextEncoderStream, 'function'); 6 + test('TES toStringTag', Object.prototype.toString.call(new TextEncoderStream()), '[object TextEncoderStream]'); 7 + 8 + testThrows('TES requires new', () => TextEncoderStream()); 9 + 10 + const tes0 = new TextEncoderStream(); 11 + test('TES encoding', tes0.encoding, 'utf-8'); 12 + test('TES readable is ReadableStream', tes0.readable instanceof ReadableStream, true); 13 + test('TES writable is WritableStream', tes0.writable instanceof WritableStream, true); 14 + 15 + async function testTESBasic() { 16 + const tes = new TextEncoderStream(); 17 + const writer = tes.writable.getWriter(); 18 + const reader = tes.readable.getReader(); 19 + writer.write('hello'); 20 + const { value } = await reader.read(); 21 + test('TES basic encode type', value instanceof Uint8Array, true); 22 + testDeep('TES basic encode value', Array.from(value), [104, 101, 108, 108, 111]); 23 + writer.close(); 24 + } 25 + 26 + async function testTESEmpty() { 27 + const tes = new TextEncoderStream(); 28 + const writer = tes.writable.getWriter(); 29 + const reader = tes.readable.getReader(); 30 + writer.write('a'); 31 + writer.close(); 32 + const { value } = await reader.read(); 33 + testDeep('TES non-empty chunk', Array.from(value), [97]); 34 + const { done } = await reader.read(); 35 + test('TES stream closes', done, true); 36 + } 37 + 38 + async function testTESMultiChunks() { 39 + const tes = new TextEncoderStream(); 40 + const writer = tes.writable.getWriter(); 41 + const reader = tes.readable.getReader(); 42 + writer.write('abc'); 43 + writer.write('def'); 44 + const r1 = await reader.read(); 45 + const r2 = await reader.read(); 46 + testDeep('TES chunk1', Array.from(r1.value), [97, 98, 99]); 47 + testDeep('TES chunk2', Array.from(r2.value), [100, 101, 102]); 48 + } 49 + 50 + async function testTESUnicode() { 51 + const tes = new TextEncoderStream(); 52 + const writer = tes.writable.getWriter(); 53 + const reader = tes.readable.getReader(); 54 + writer.write('cafรฉ'); 55 + const { value } = await reader.read(); 56 + testDeep('TES unicode', Array.from(value), [99, 97, 102, 195, 169]); 57 + } 58 + 59 + async function testTESPipeThrough() { 60 + const rs = new ReadableStream({ 61 + start(c) { 62 + c.enqueue('hello'); 63 + c.close(); 64 + } 65 + }); 66 + const tes = new TextEncoderStream(); 67 + const reader = rs.pipeThrough(tes).getReader(); 68 + const { value } = await reader.read(); 69 + test('TES pipeThrough type', value instanceof Uint8Array, true); 70 + testDeep('TES pipeThrough value', Array.from(value), [104, 101, 108, 108, 111]); 71 + } 72 + 73 + test('TDS typeof', typeof TextDecoderStream, 'function'); 74 + test('TDS toStringTag', Object.prototype.toString.call(new TextDecoderStream()), '[object TextDecoderStream]'); 75 + testThrows('TDS requires new', () => TextDecoderStream()); 76 + 77 + const tds0 = new TextDecoderStream(); 78 + test('TDS encoding default', tds0.encoding, 'utf-8'); 79 + test('TDS fatal default', tds0.fatal, false); 80 + test('TDS ignoreBOM default', tds0.ignoreBOM, false); 81 + test('TDS readable is ReadableStream', tds0.readable instanceof ReadableStream, true); 82 + test('TDS writable is WritableStream', tds0.writable instanceof WritableStream, true); 83 + 84 + const tds1 = new TextDecoderStream('utf-8', { fatal: true, ignoreBOM: true }); 85 + test('TDS fatal option', tds1.fatal, true); 86 + test('TDS ignoreBOM option', tds1.ignoreBOM, true); 87 + 88 + testThrows('TDS invalid encoding', () => new TextDecoderStream('invalid-encoding')); 89 + 90 + async function testTDSBasic() { 91 + const tds = new TextDecoderStream(); 92 + const writer = tds.writable.getWriter(); 93 + const reader = tds.readable.getReader(); 94 + writer.write(new Uint8Array([104, 101, 108, 108, 111])); 95 + const { value } = await reader.read(); 96 + test('TDS basic decode', value, 'hello'); 97 + writer.close(); 98 + } 99 + 100 + async function testTDSMultiChunks() { 101 + const tds = new TextDecoderStream(); 102 + const writer = tds.writable.getWriter(); 103 + const reader = tds.readable.getReader(); 104 + writer.write(new Uint8Array([97, 98, 99])); 105 + writer.write(new Uint8Array([100, 101, 102])); 106 + const r1 = await reader.read(); 107 + const r2 = await reader.read(); 108 + test('TDS chunk1', r1.value, 'abc'); 109 + test('TDS chunk2', r2.value, 'def'); 110 + } 111 + 112 + async function testTDSSplitMultibyte() { 113 + const tds = new TextDecoderStream(); 114 + const writer = tds.writable.getWriter(); 115 + const reader = tds.readable.getReader(); 116 + writer.write(new Uint8Array([99, 97, 102, 0xc3])); 117 + writer.write(new Uint8Array([0xa9])); 118 + writer.close(); 119 + const chunks = []; 120 + while (true) { 121 + const { value, done } = await reader.read(); 122 + if (done) break; 123 + if (value) chunks.push(value); 124 + } 125 + test('TDS split multibyte', chunks.join(''), 'cafรฉ'); 126 + } 127 + 128 + async function testTDSPipeThrough() { 129 + const rs = new ReadableStream({ 130 + start(c) { 131 + c.enqueue(new Uint8Array([104, 101, 108, 108, 111])); 132 + c.close(); 133 + } 134 + }); 135 + const tds = new TextDecoderStream(); 136 + const reader = rs.pipeThrough(tds).getReader(); 137 + const { value } = await reader.read(); 138 + test('TDS pipeThrough', value, 'hello'); 139 + } 140 + 141 + async function testRoundTrip() { 142 + const rs = new ReadableStream({ 143 + start(c) { 144 + c.enqueue('hello world'); 145 + c.close(); 146 + } 147 + }); 148 + const reader = rs.pipeThrough(new TextEncoderStream()).pipeThrough(new TextDecoderStream()).getReader(); 149 + const { value } = await reader.read(); 150 + test('encodeโ†’decode roundtrip', value, 'hello world'); 151 + } 152 + 153 + async function testTDSUTF16LE() { 154 + const tds = new TextDecoderStream('utf-16le'); 155 + test('TDS utf-16le encoding', tds.encoding, 'utf-16le'); 156 + const writer = tds.writable.getWriter(); 157 + const reader = tds.readable.getReader(); 158 + writer.write(new Uint8Array([0x68, 0x00, 0x69, 0x00])); 159 + const { value } = await reader.read(); 160 + test('TDS utf-16le decode', value, 'hi'); 161 + } 162 + 163 + await testTESBasic(); 164 + await testTESEmpty(); 165 + await testTESMultiChunks(); 166 + await testTESUnicode(); 167 + await testTESPipeThrough(); 168 + await testTDSBasic(); 169 + await testTDSMultiChunks(); 170 + await testTDSSplitMultibyte(); 171 + await testTDSPipeThrough(); 172 + await testRoundTrip(); 173 + await testTDSUTF16LE(); 174 + 175 + summary();
+2
include/gc/modules.h
··· 25 25 void gc_mark_readable_streams(ant_t *js, gc_mark_fn mark); 26 26 void gc_mark_writable_streams(ant_t *js, gc_mark_fn mark); 27 27 void gc_mark_transform_streams(ant_t *js, gc_mark_fn mark); 28 + void gc_mark_codec_streams(ant_t *js, gc_mark_fn mark); 29 + void gc_mark_compression_streams(ant_t *js, gc_mark_fn mark); 28 30 29 31 #endif
+21
include/streams/codec.h
··· 1 + #ifndef STREAMS_CODEC_H 2 + #define STREAMS_CODEC_H 3 + 4 + #include "types.h" 5 + #include <stdbool.h> 6 + 7 + extern ant_value_t g_tes_proto; 8 + extern ant_value_t g_tds_proto; 9 + 10 + void init_codec_stream_module(void); 11 + void gc_mark_codec_streams(ant_t *js, void (*mark)(ant_t *, ant_value_t)); 12 + 13 + bool tes_is_stream(ant_value_t obj); 14 + bool tds_is_stream(ant_value_t obj); 15 + 16 + ant_value_t tes_stream_readable(ant_value_t obj); 17 + ant_value_t tes_stream_writable(ant_value_t obj); 18 + ant_value_t tds_stream_readable(ant_value_t obj); 19 + ant_value_t tds_stream_writable(ant_value_t obj); 20 + 21 + #endif
+27
include/streams/compression.h
··· 1 + #ifndef STREAMS_COMPRESSION_H 2 + #define STREAMS_COMPRESSION_H 3 + 4 + #include "types.h" 5 + #include <stdbool.h> 6 + 7 + typedef enum { 8 + ZFMT_GZIP = 0, 9 + ZFMT_DEFLATE, 10 + ZFMT_DEFLATE_RAW, 11 + } zformat_t; 12 + 13 + extern ant_value_t g_cs_proto; 14 + extern ant_value_t g_ds_proto; 15 + 16 + void init_compression_stream_module(void); 17 + void gc_mark_compression_streams(ant_t *js, void (*mark)(ant_t *, ant_value_t)); 18 + 19 + bool cs_is_stream(ant_value_t obj); 20 + bool ds_is_stream(ant_value_t obj); 21 + 22 + ant_value_t cs_stream_readable(ant_value_t obj); 23 + ant_value_t cs_stream_writable(ant_value_t obj); 24 + ant_value_t ds_stream_readable(ant_value_t obj); 25 + ant_value_t ds_stream_writable(ant_value_t obj); 26 + 27 + #endif
+10
include/streams/transform.h
··· 4 4 #include "types.h" 5 5 #include <stdbool.h> 6 6 7 + extern ant_value_t g_ts_proto; 8 + extern ant_value_t g_ts_ctrl_proto; 9 + 7 10 void init_transform_stream_module(void); 8 11 void gc_mark_transform_streams(ant_t *js, void (*mark)(ant_t *, ant_value_t)); 9 12 10 13 bool ts_is_stream(ant_value_t obj); 11 14 bool ts_is_controller(ant_value_t obj); 12 15 16 + void ts_ctrl_error(ant_t *js, ant_value_t ctrl_obj, ant_value_t e); 17 + void ts_ctrl_terminate(ant_t *js, ant_value_t ctrl_obj); 18 + 13 19 ant_value_t ts_stream_readable(ant_value_t ts_obj); 14 20 ant_value_t ts_stream_writable(ant_value_t ts_obj); 21 + 22 + ant_value_t ts_stream_controller(ant_value_t ts_obj); 23 + ant_value_t ts_ctrl_enqueue(ant_t *js, ant_value_t ctrl_obj, ant_value_t chunk); 24 + ant_value_t js_ts_ctor(ant_t *js, ant_value_t *args, int nargs); 15 25 16 26 #endif
+2
src/gc/objects.c
··· 458 458 gc_mark_readable_streams(js, gc_mark_value); 459 459 gc_mark_writable_streams(js, gc_mark_value); 460 460 gc_mark_transform_streams(js, gc_mark_value); 461 + gc_mark_codec_streams(js, gc_mark_value); 462 + gc_mark_compression_streams(js, gc_mark_value); 461 463 462 464 for ( 463 465 ant_object_t *obj = g_pending_promises;
+4
src/main.c
··· 83 83 #include "streams/readable.h" 84 84 #include "streams/writable.h" 85 85 #include "streams/transform.h" 86 + #include "streams/codec.h" 87 + #include "streams/compression.h" 86 88 87 89 int js_result = EXIT_SUCCESS; 88 90 typedef int (*cmd_fn)(int argc, char **argv); ··· 605 607 init_readable_stream_module(); 606 608 init_writable_stream_module(); 607 609 init_transform_stream_module(); 610 + init_codec_stream_module(); 611 + init_compression_stream_module(); 608 612 init_fs_module(); 609 613 init_atomics_module(); 610 614 init_crypto_module();
+482
src/streams/codec.c
··· 1 + #include <stdlib.h> 2 + #include <string.h> 3 + 4 + #include "ant.h" 5 + #include "errors.h" 6 + #include "runtime.h" 7 + #include "internal.h" 8 + #include "descriptors.h" 9 + 10 + #include "modules/symbol.h" 11 + #include "modules/buffer.h" 12 + #include "modules/textcodec.h" 13 + #include "streams/codec.h" 14 + #include "streams/transform.h" 15 + #include "streams/readable.h" 16 + 17 + ant_value_t g_tes_proto; 18 + ant_value_t g_tds_proto; 19 + 20 + typedef struct { 21 + uint8_t pending[3]; 22 + uint8_t pending_len; 23 + } tes_state_t; 24 + 25 + static ant_value_t tes_get_ts(ant_value_t obj) { 26 + return js_get_slot(obj, SLOT_ENTRIES); 27 + } 28 + 29 + static ant_value_t tds_get_ts(ant_value_t obj) { 30 + return js_get_slot(obj, SLOT_ENTRIES); 31 + } 32 + 33 + bool tes_is_stream(ant_value_t obj) { 34 + return is_object_type(obj) 35 + && vtype(js_get_slot(obj, SLOT_DATA)) == T_NUM 36 + && ts_is_stream(tes_get_ts(obj)); 37 + } 38 + 39 + bool tds_is_stream(ant_value_t obj) { 40 + return is_object_type(obj) 41 + && vtype(js_get_slot(obj, SLOT_DATA)) == T_NUM 42 + && ts_is_stream(tds_get_ts(obj)); 43 + } 44 + 45 + ant_value_t tes_stream_readable(ant_value_t obj) { 46 + return ts_stream_readable(tes_get_ts(obj)); 47 + } 48 + 49 + ant_value_t tes_stream_writable(ant_value_t obj) { 50 + return ts_stream_writable(tes_get_ts(obj)); 51 + } 52 + 53 + ant_value_t tds_stream_readable(ant_value_t obj) { 54 + return ts_stream_readable(tds_get_ts(obj)); 55 + } 56 + 57 + ant_value_t tds_stream_writable(ant_value_t obj) { 58 + return ts_stream_writable(tds_get_ts(obj)); 59 + } 60 + 61 + static void tes_state_finalize(ant_t *js, ant_object_t *obj) { 62 + if (!obj->extra_slots) return; 63 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 64 + for (uint8_t i = 0; i < obj->extra_count; i++) { 65 + if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 66 + free((tes_state_t *)(uintptr_t)(size_t)js_getnum(entries[i].value)); 67 + return; 68 + }} 69 + } 70 + 71 + static void tds_state_finalize(ant_t *js, ant_object_t *obj) { 72 + if (!obj->extra_slots) return; 73 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 74 + for (uint8_t i = 0; i < obj->extra_count; i++) { 75 + if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 76 + free((td_state_t *)(uintptr_t)(size_t)js_getnum(entries[i].value)); 77 + return; 78 + }} 79 + } 80 + 81 + static bool get_chunk_bytes(ant_t *js, ant_value_t chunk, const uint8_t **out, size_t *len) { 82 + ant_value_t slot = js_get_slot(chunk, SLOT_BUFFER); 83 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); 84 + if (ta) { 85 + if (!ta->buffer || ta->buffer->is_detached) { *out = NULL; *len = 0; return true; } 86 + *out = ta->buffer->data + ta->byte_offset; 87 + *len = ta->byte_length; 88 + return true; 89 + } 90 + 91 + if (vtype(slot) == T_NUM) { 92 + ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(slot); 93 + if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 94 + *out = ab->data; 95 + *len = ab->length; 96 + return true; 97 + } 98 + 99 + ant_value_t buf_prop = js_get(js, chunk, "buffer"); 100 + if (is_object_type(buf_prop)) { 101 + ant_value_t buf_slot = js_get_slot(buf_prop, SLOT_BUFFER); 102 + if (vtype(buf_slot) == T_NUM) { 103 + ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(buf_slot); 104 + if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 105 + ant_value_t off_v = js_get(js, chunk, "byteOffset"); 106 + ant_value_t len_v = js_get(js, chunk, "byteLength"); 107 + size_t off = (vtype(off_v) == T_NUM) ? (size_t)js_getnum(off_v) : 0; 108 + size_t blen = (vtype(len_v) == T_NUM) ? (size_t)js_getnum(len_v) : ab->length - off; 109 + *out = ab->data + off; 110 + *len = blen; 111 + return true; 112 + }} 113 + 114 + return false; 115 + } 116 + 117 + static ant_value_t tes_transform(ant_t *js, ant_value_t *args, int nargs) { 118 + ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 119 + ant_value_t state_val = js_get_slot(wrapper, SLOT_DATA); 120 + tes_state_t *st = (tes_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 121 + 122 + ant_value_t chunk = (nargs > 0) ? args[0] : js_mkundef(); 123 + size_t str_len = 0; 124 + const char *str = NULL; 125 + 126 + if (vtype(chunk) == T_STR) { 127 + str = js_getstr(js, chunk, &str_len); 128 + } else { 129 + ant_value_t sv = js_tostring_val(js, chunk); 130 + if (is_err(sv)) return sv; 131 + str = js_getstr(js, sv, &str_len); 132 + } 133 + if (!str) { str = ""; str_len = 0; } 134 + 135 + const uint8_t *s = (const uint8_t *)str; 136 + size_t total = st->pending_len + str_len; 137 + 138 + if (total == 0) return js_mkundef(); 139 + 140 + uint8_t *buf = malloc(total); 141 + if (!buf) return js_mkerr(js, "out of memory"); 142 + 143 + size_t off = 0; 144 + if (st->pending_len > 0) { 145 + memcpy(buf, st->pending, st->pending_len); 146 + off = st->pending_len; 147 + st->pending_len = 0; 148 + } 149 + memcpy(buf + off, s, str_len); 150 + 151 + size_t out_len = total; 152 + if (out_len >= 3) { 153 + uint8_t b0 = buf[out_len - 3], b1 = buf[out_len - 2], b2 = buf[out_len - 1]; 154 + if (b0 == 0xED && b1 >= 0xA0 && b1 <= 0xAF) { 155 + st->pending[0] = b0; 156 + st->pending[1] = b1; 157 + st->pending[2] = b2; 158 + st->pending_len = 3; 159 + out_len -= 3; 160 + }} 161 + 162 + if (out_len == 0) { 163 + free(buf); 164 + return js_mkundef(); 165 + } 166 + 167 + uint8_t *out = malloc(out_len * 4 / 3 + 4); 168 + if (!out) { free(buf); return js_mkerr(js, "out of memory"); } 169 + 170 + size_t i = 0, o = 0; 171 + while (i < out_len) { 172 + if (i + 5 < out_len && 173 + buf[i] == 0xED && buf[i+1] >= 0xA0 && buf[i+1] <= 0xAF && 174 + buf[i+3] == 0xED && buf[i+4] >= 0xB0 && buf[i+4] <= 0xBF) { 175 + uint32_t hi_cp = ((uint32_t)0x0D << 12) | ((uint32_t)(buf[i+1] & 0x3F) << 6) | (buf[i+2] & 0x3F); 176 + uint32_t lo_cp = ((uint32_t)0x0D << 12) | ((uint32_t)(buf[i+4] & 0x3F) << 6) | (buf[i+5] & 0x3F); 177 + uint32_t cp = 0x10000 + ((hi_cp - 0xD800) << 10) + (lo_cp - 0xDC00); 178 + out[o++] = (uint8_t)(0xF0 | (cp >> 18)); 179 + out[o++] = (uint8_t)(0x80 | ((cp >> 12) & 0x3F)); 180 + out[o++] = (uint8_t)(0x80 | ((cp >> 6) & 0x3F)); 181 + out[o++] = (uint8_t)(0x80 | (cp & 0x3F)); 182 + i += 6; 183 + } else if (buf[i] == 0xED && i + 2 < out_len && buf[i+1] >= 0xA0 && buf[i+1] <= 0xBF) { 184 + out[o++] = 0xEF; out[o++] = 0xBF; out[o++] = 0xBD; 185 + i += 3; 186 + } else out[o++] = buf[i++]; } 187 + 188 + free(buf); 189 + 190 + ArrayBufferData *ab = create_array_buffer_data(o); 191 + if (!ab) { free(out); return js_mkerr(js, "out of memory"); } 192 + memcpy(ab->data, out, o); 193 + free(out); 194 + 195 + ant_value_t result = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, o, "Uint8Array"); 196 + ant_value_t ts_obj = js_get_slot(wrapper, SLOT_ENTRIES); 197 + ant_value_t readable = ts_stream_readable(ts_obj); 198 + ant_value_t rs_ctrl = rs_stream_controller(js, readable); 199 + rs_controller_enqueue(js, rs_ctrl, result); 200 + 201 + return js_mkundef(); 202 + } 203 + 204 + static ant_value_t tes_flush(ant_t *js, ant_value_t *args, int nargs) { 205 + ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 206 + ant_value_t state_val = js_get_slot(wrapper, SLOT_DATA); 207 + tes_state_t *st = (tes_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 208 + 209 + if (st->pending_len > 0) { 210 + uint8_t fffd[3] = { 0xEF, 0xBF, 0xBD }; 211 + ArrayBufferData *ab = create_array_buffer_data(3); 212 + if (!ab) return js_mkerr(js, "out of memory"); 213 + memcpy(ab->data, fffd, 3); 214 + st->pending_len = 0; 215 + 216 + ant_value_t result = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, 3, "Uint8Array"); 217 + ant_value_t ts_obj = js_get_slot(wrapper, SLOT_ENTRIES); 218 + ant_value_t readable = ts_stream_readable(ts_obj); 219 + ant_value_t rs_ctrl = rs_stream_controller(js, readable); 220 + rs_controller_enqueue(js, rs_ctrl, result); 221 + } 222 + 223 + return js_mkundef(); 224 + } 225 + 226 + static ant_value_t js_tes_get_encoding(ant_t *js, ant_value_t *args, int nargs) { 227 + return js_mkstr(js, "utf-8", 5); 228 + } 229 + 230 + static ant_value_t js_tes_get_readable(ant_t *js, ant_value_t *args, int nargs) { 231 + ant_value_t ts_obj = tes_get_ts(js->this_val); 232 + if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TextEncoderStream"); 233 + return ts_stream_readable(ts_obj); 234 + } 235 + 236 + static ant_value_t js_tes_get_writable(ant_t *js, ant_value_t *args, int nargs) { 237 + ant_value_t ts_obj = tes_get_ts(js->this_val); 238 + if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TextEncoderStream"); 239 + return ts_stream_writable(ts_obj); 240 + } 241 + 242 + static ant_value_t js_tes_ctor(ant_t *js, ant_value_t *args, int nargs) { 243 + if (vtype(js->new_target) == T_UNDEF) 244 + return js_mkerr_typed(js, JS_ERR_TYPE, "TextEncoderStream constructor requires 'new'"); 245 + 246 + tes_state_t *st = calloc(1, sizeof(tes_state_t)); 247 + if (!st) return js_mkerr(js, "out of memory"); 248 + 249 + ant_value_t obj = js_mkobj(js); 250 + ant_value_t proto = js_instance_proto_from_new_target(js, g_tes_proto); 251 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 252 + js_set_slot(obj, SLOT_DATA, ANT_PTR(st)); 253 + js_set_finalizer(obj, tes_state_finalize); 254 + 255 + ant_value_t wrapper = js_mkobj(js); 256 + js_set_slot(wrapper, SLOT_DATA, ANT_PTR(st)); 257 + 258 + ant_value_t transformer = js_mkobj(js); 259 + ant_value_t transform_fn = js_heavy_mkfun(js, tes_transform, wrapper); 260 + ant_value_t flush_fn = js_heavy_mkfun(js, tes_flush, wrapper); 261 + js_set(js, transformer, "transform", transform_fn); 262 + js_set(js, transformer, "flush", flush_fn); 263 + 264 + ant_value_t ctor_args[1] = { transformer }; 265 + ant_value_t saved_new_target = js->new_target; 266 + ant_value_t saved_this = js->this_val; 267 + js->new_target = js_mknum(1); 268 + 269 + ant_value_t ts_obj = js_ts_ctor(js, ctor_args, 1); 270 + js->new_target = saved_new_target; 271 + js->this_val = saved_this; 272 + 273 + if (is_err(ts_obj)) { free(st); return ts_obj; } 274 + js_set_slot(obj, SLOT_ENTRIES, ts_obj); 275 + js_set_slot(wrapper, SLOT_ENTRIES, ts_obj); 276 + 277 + return obj; 278 + } 279 + 280 + static ant_value_t tds_transform(ant_t *js, ant_value_t *args, int nargs) { 281 + ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 282 + ant_value_t state_val = js_get_slot(wrapper, SLOT_DATA); 283 + td_state_t *st = (td_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 284 + 285 + ant_value_t chunk = (nargs > 0) ? args[0] : js_mkundef(); 286 + const uint8_t *input = NULL; 287 + size_t input_len = 0; 288 + 289 + if (is_object_type(chunk)) 290 + get_chunk_bytes(js, chunk, &input, &input_len); 291 + 292 + ant_value_t result = td_decode(js, st, input, input_len, true); 293 + if (is_err(result)) return result; 294 + 295 + size_t slen = 0; 296 + const char *sval = js_getstr(js, result, &slen); 297 + if (sval && slen > 0) { 298 + ant_value_t ts_obj = js_get_slot(wrapper, SLOT_ENTRIES); 299 + ant_value_t readable = ts_stream_readable(ts_obj); 300 + ant_value_t rs_ctrl = rs_stream_controller(js, readable); 301 + rs_controller_enqueue(js, rs_ctrl, result); 302 + } 303 + 304 + return js_mkundef(); 305 + } 306 + 307 + static ant_value_t tds_flush(ant_t *js, ant_value_t *args, int nargs) { 308 + ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 309 + ant_value_t state_val = js_get_slot(wrapper, SLOT_DATA); 310 + td_state_t *st = (td_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 311 + 312 + ant_value_t result = td_decode(js, st, NULL, 0, false); 313 + if (is_err(result)) return result; 314 + 315 + size_t slen = 0; 316 + const char *sval = js_getstr(js, result, &slen); 317 + if (sval && slen > 0) { 318 + ant_value_t ts_obj = js_get_slot(wrapper, SLOT_ENTRIES); 319 + ant_value_t readable = ts_stream_readable(ts_obj); 320 + ant_value_t rs_ctrl = rs_stream_controller(js, readable); 321 + rs_controller_enqueue(js, rs_ctrl, result); 322 + } 323 + 324 + return js_mkundef(); 325 + } 326 + 327 + static ant_value_t js_tds_get_encoding(ant_t *js, ant_value_t *args, int nargs) { 328 + ant_value_t state_val = js_get_slot(js->this_val, SLOT_DATA); 329 + td_state_t *st = (td_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 330 + if (!st) return js_mkstr(js, "utf-8", 5); 331 + switch (st->encoding) { 332 + case TD_ENC_UTF16LE: return js_mkstr(js, "utf-16le", 8); 333 + case TD_ENC_UTF16BE: return js_mkstr(js, "utf-16be", 8); 334 + default: return js_mkstr(js, "utf-8", 5); 335 + } 336 + } 337 + 338 + static ant_value_t js_tds_get_fatal(ant_t *js, ant_value_t *args, int nargs) { 339 + ant_value_t state_val = js_get_slot(js->this_val, SLOT_DATA); 340 + td_state_t *st = (td_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 341 + return (st && st->fatal) ? js_true : js_false; 342 + } 343 + 344 + static ant_value_t js_tds_get_ignore_bom(ant_t *js, ant_value_t *args, int nargs) { 345 + ant_value_t state_val = js_get_slot(js->this_val, SLOT_DATA); 346 + td_state_t *st = (td_state_t *)(uintptr_t)(size_t)js_getnum(state_val); 347 + return (st && st->ignore_bom) ? js_true : js_false; 348 + } 349 + 350 + static ant_value_t js_tds_get_readable(ant_t *js, ant_value_t *args, int nargs) { 351 + ant_value_t ts_obj = tds_get_ts(js->this_val); 352 + if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TextDecoderStream"); 353 + return ts_stream_readable(ts_obj); 354 + } 355 + 356 + static ant_value_t js_tds_get_writable(ant_t *js, ant_value_t *args, int nargs) { 357 + ant_value_t ts_obj = tds_get_ts(js->this_val); 358 + if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid TextDecoderStream"); 359 + return ts_stream_writable(ts_obj); 360 + } 361 + 362 + static const char *tds_trim_label(const char *s, size_t len, size_t *out_len) { 363 + while (len > 0 && (unsigned char)*s <= 0x20) { s++; len--; } 364 + while (len > 0 && (unsigned char)s[len - 1] <= 0x20) { len--; } 365 + *out_len = len; 366 + return s; 367 + } 368 + 369 + static int tds_resolve_encoding(const char *s, size_t len) { 370 + static const struct { const char *label; uint8_t label_len; td_encoding_t enc; } map[] = { 371 + {"unicode-1-1-utf-8", 18, TD_ENC_UTF8}, {"unicode11utf8", 13, TD_ENC_UTF8}, 372 + {"unicode20utf8", 13, TD_ENC_UTF8}, {"utf-8", 5, TD_ENC_UTF8}, 373 + {"utf8", 4, TD_ENC_UTF8}, {"x-unicode20utf8",17, TD_ENC_UTF8}, 374 + {"unicodefffe", 11, TD_ENC_UTF16BE}, {"utf-16be", 8, TD_ENC_UTF16BE}, 375 + {"csunicode", 9, TD_ENC_UTF16LE}, {"iso-10646-ucs-2",16, TD_ENC_UTF16LE}, 376 + {"ucs-2", 5, TD_ENC_UTF16LE}, {"unicode", 7, TD_ENC_UTF16LE}, 377 + {"unicodefeff", 11, TD_ENC_UTF16LE}, {"utf-16", 6, TD_ENC_UTF16LE}, 378 + {"utf-16le", 8, TD_ENC_UTF16LE}, 379 + {NULL, 0, 0} 380 + }; 381 + for (int i = 0; map[i].label; i++) { 382 + if (len == map[i].label_len && strncasecmp(s, map[i].label, len) == 0) return (int)map[i].enc; 383 + } 384 + return -1; 385 + } 386 + 387 + static ant_value_t js_tds_ctor(ant_t *js, ant_value_t *args, int nargs) { 388 + if (vtype(js->new_target) == T_UNDEF) 389 + return js_mkerr_typed(js, JS_ERR_TYPE, "TextDecoderStream constructor requires 'new'"); 390 + 391 + td_encoding_t enc = TD_ENC_UTF8; 392 + if (nargs > 0 && vtype(args[0]) == T_STR) { 393 + size_t llen; 394 + const char *raw = js_getstr(js, args[0], &llen); 395 + if (raw) { 396 + size_t tlen; 397 + const char *trimmed = tds_trim_label(raw, llen, &tlen); 398 + int resolved = tds_resolve_encoding(trimmed, tlen); 399 + if (resolved < 0) return js_mkerr_typed( 400 + js, JS_ERR_RANGE, "Failed to construct 'TextDecoderStream': The encoding label provided ('%.*s') is invalid.", 401 + (int)tlen, trimmed 402 + ); 403 + enc = (td_encoding_t)resolved; 404 + }} 405 + 406 + bool fatal = false; 407 + bool ignore_bom = false; 408 + if (nargs > 1 && is_object_type(args[1])) { 409 + ant_value_t fv = js_get(js, args[1], "fatal"); 410 + if (vtype(fv) != T_UNDEF) fatal = js_truthy(js, fv); 411 + ant_value_t bv = js_get(js, args[1], "ignoreBOM"); 412 + if (vtype(bv) != T_UNDEF) ignore_bom = js_truthy(js, bv); 413 + } 414 + 415 + td_state_t *st = td_state_new(enc, fatal, ignore_bom); 416 + if (!st) return js_mkerr(js, "out of memory"); 417 + 418 + ant_value_t obj = js_mkobj(js); 419 + ant_value_t proto = js_instance_proto_from_new_target(js, g_tds_proto); 420 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 421 + js_set_slot(obj, SLOT_DATA, ANT_PTR(st)); 422 + js_set_finalizer(obj, tds_state_finalize); 423 + 424 + ant_value_t wrapper = js_mkobj(js); 425 + js_set_slot(wrapper, SLOT_DATA, ANT_PTR(st)); 426 + 427 + ant_value_t transformer = js_mkobj(js); 428 + ant_value_t transform_fn = js_heavy_mkfun(js, tds_transform, wrapper); 429 + ant_value_t flush_fn = js_heavy_mkfun(js, tds_flush, wrapper); 430 + js_set(js, transformer, "transform", transform_fn); 431 + js_set(js, transformer, "flush", flush_fn); 432 + 433 + ant_value_t ctor_args[1] = { transformer }; 434 + 435 + ant_value_t saved_new_target = js->new_target; 436 + ant_value_t saved_this = js->this_val; 437 + js->new_target = js_mknum(1); 438 + 439 + ant_value_t ts_obj = js_ts_ctor(js, ctor_args, 1); 440 + 441 + js->new_target = saved_new_target; 442 + js->this_val = saved_this; 443 + 444 + if (is_err(ts_obj)) { free(st); return ts_obj; } 445 + 446 + js_set_slot(obj, SLOT_ENTRIES, ts_obj); 447 + js_set_slot(wrapper, SLOT_ENTRIES, ts_obj); 448 + 449 + return obj; 450 + } 451 + 452 + void init_codec_stream_module(void) { 453 + ant_t *js = rt->js; 454 + ant_value_t g = js_glob(js); 455 + 456 + g_tes_proto = js_mkobj(js); 457 + js_set_getter_desc(js, g_tes_proto, "encoding", 8, js_mkfun(js_tes_get_encoding), JS_DESC_C); 458 + js_set_getter_desc(js, g_tes_proto, "readable", 8, js_mkfun(js_tes_get_readable), JS_DESC_C); 459 + js_set_getter_desc(js, g_tes_proto, "writable", 8, js_mkfun(js_tes_get_writable), JS_DESC_C); 460 + js_set_sym(js, g_tes_proto, get_toStringTag_sym(), js_mkstr(js, "TextEncoderStream", 17)); 461 + 462 + ant_value_t tes_ctor = js_make_ctor(js, js_tes_ctor, g_tes_proto, "TextEncoderStream", 17); 463 + js_set(js, g, "TextEncoderStream", tes_ctor); 464 + js_set_descriptor(js, g, "TextEncoderStream", 17, JS_DESC_W | JS_DESC_C); 465 + 466 + g_tds_proto = js_mkobj(js); 467 + js_set_getter_desc(js, g_tds_proto, "encoding", 8, js_mkfun(js_tds_get_encoding), JS_DESC_C); 468 + js_set_getter_desc(js, g_tds_proto, "fatal", 5, js_mkfun(js_tds_get_fatal), JS_DESC_C); 469 + js_set_getter_desc(js, g_tds_proto, "ignoreBOM", 9, js_mkfun(js_tds_get_ignore_bom), JS_DESC_C); 470 + js_set_getter_desc(js, g_tds_proto, "readable", 8, js_mkfun(js_tds_get_readable), JS_DESC_C); 471 + js_set_getter_desc(js, g_tds_proto, "writable", 8, js_mkfun(js_tds_get_writable), JS_DESC_C); 472 + js_set_sym(js, g_tds_proto, get_toStringTag_sym(), js_mkstr(js, "TextDecoderStream", 17)); 473 + 474 + ant_value_t tds_ctor = js_make_ctor(js, js_tds_ctor, g_tds_proto, "TextDecoderStream", 17); 475 + js_set(js, g, "TextDecoderStream", tds_ctor); 476 + js_set_descriptor(js, g, "TextDecoderStream", 17, JS_DESC_W | JS_DESC_C); 477 + } 478 + 479 + void gc_mark_codec_streams(ant_t *js, void (*mark)(ant_t *, ant_value_t)) { 480 + mark(js, g_tes_proto); 481 + mark(js, g_tds_proto); 482 + }
+403
src/streams/compression.c
··· 1 + #include <stdlib.h> 2 + #include <string.h> 3 + #include <zlib.h> 4 + 5 + #include "ant.h" 6 + #include "errors.h" 7 + #include "runtime.h" 8 + #include "internal.h" 9 + #include "descriptors.h" 10 + 11 + #include "modules/symbol.h" 12 + #include "modules/buffer.h" 13 + #include "streams/compression.h" 14 + #include "streams/transform.h" 15 + #include "streams/readable.h" 16 + 17 + ant_value_t g_cs_proto; 18 + ant_value_t g_ds_proto; 19 + 20 + typedef struct { 21 + z_stream strm; 22 + zformat_t format; 23 + bool initialized; 24 + } zstate_t; 25 + 26 + static int zfmt_window_bits(zformat_t fmt, bool decompress) { 27 + switch (fmt) { 28 + case ZFMT_GZIP: return decompress ? (15 + 32) : (15 + 16); 29 + case ZFMT_DEFLATE: return 15; 30 + case ZFMT_DEFLATE_RAW: return -15; 31 + } 32 + return 15; 33 + } 34 + 35 + static int parse_format(ant_t *js, ant_value_t arg, zformat_t *out) { 36 + if (vtype(arg) != T_STR) return -1; 37 + size_t len; 38 + const char *s = js_getstr(js, arg, &len); 39 + if (!s) return -1; 40 + if (len == 4 && !memcmp(s, "gzip", 4)) { *out = ZFMT_GZIP; return 0; } 41 + if (len == 7 && !memcmp(s, "deflate", 7)) { *out = ZFMT_DEFLATE; return 0; } 42 + if (len == 11 && !memcmp(s, "deflate-raw", 11)) { *out = ZFMT_DEFLATE_RAW; return 0; } 43 + return -1; 44 + } 45 + 46 + static ant_value_t get_ts(ant_value_t obj) { 47 + return js_get_slot(obj, SLOT_ENTRIES); 48 + } 49 + 50 + bool cs_is_stream(ant_value_t obj) { 51 + return is_object_type(obj) 52 + && vtype(js_get_slot(obj, SLOT_DATA)) == T_NUM 53 + && ts_is_stream(get_ts(obj)); 54 + } 55 + 56 + bool ds_is_stream(ant_value_t obj) { 57 + return is_object_type(obj) 58 + && vtype(js_get_slot(obj, SLOT_DATA)) == T_NUM 59 + && ts_is_stream(get_ts(obj)); 60 + } 61 + 62 + ant_value_t cs_stream_readable(ant_value_t obj) { 63 + return ts_stream_readable(get_ts(obj)); 64 + } 65 + 66 + ant_value_t cs_stream_writable(ant_value_t obj) { 67 + return ts_stream_writable(get_ts(obj)); 68 + } 69 + 70 + ant_value_t ds_stream_readable(ant_value_t obj) { 71 + return ts_stream_readable(get_ts(obj)); 72 + } 73 + 74 + ant_value_t ds_stream_writable(ant_value_t obj) { 75 + return ts_stream_writable(get_ts(obj)); 76 + } 77 + 78 + static void zstate_finalize(ant_t *js, ant_object_t *obj) { 79 + if (!obj->extra_slots) return; 80 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 81 + for (uint8_t i = 0; i < obj->extra_count; i++) { 82 + if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 83 + zstate_t *st = (zstate_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 84 + if (st->initialized) deflateEnd(&st->strm); 85 + free(st); 86 + return; 87 + }} 88 + } 89 + 90 + static void zstate_inflate_finalize(ant_t *js, ant_object_t *obj) { 91 + if (!obj->extra_slots) return; 92 + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; 93 + for (uint8_t i = 0; i < obj->extra_count; i++) { 94 + if (entries[i].slot == SLOT_DATA && vtype(entries[i].value) == T_NUM) { 95 + zstate_t *st = (zstate_t *)(uintptr_t)(size_t)js_getnum(entries[i].value); 96 + if (st->initialized) inflateEnd(&st->strm); 97 + free(st); 98 + return; 99 + }} 100 + } 101 + 102 + static bool get_chunk_bytes(ant_t *js, ant_value_t chunk, const uint8_t **out, size_t *len) { 103 + ant_value_t slot = js_get_slot(chunk, SLOT_BUFFER); 104 + TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); 105 + if (ta) { 106 + if (!ta->buffer || ta->buffer->is_detached) { *out = NULL; *len = 0; return true; } 107 + *out = ta->buffer->data + ta->byte_offset; 108 + *len = ta->byte_length; 109 + return true; 110 + } 111 + 112 + if (vtype(slot) == T_NUM) { 113 + ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(slot); 114 + if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 115 + *out = ab->data; 116 + *len = ab->length; 117 + return true; 118 + } 119 + 120 + ant_value_t buf_prop = js_get(js, chunk, "buffer"); 121 + if (is_object_type(buf_prop)) { 122 + ant_value_t buf_slot = js_get_slot(buf_prop, SLOT_BUFFER); 123 + if (vtype(buf_slot) == T_NUM) { 124 + ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)(size_t)js_getnum(buf_slot); 125 + if (!ab || ab->is_detached) { *out = NULL; *len = 0; return true; } 126 + ant_value_t off_v = js_get(js, chunk, "byteOffset"); 127 + ant_value_t len_v = js_get(js, chunk, "byteLength"); 128 + size_t off = (vtype(off_v) == T_NUM) ? (size_t)js_getnum(off_v) : 0; 129 + size_t blen = (vtype(len_v) == T_NUM) ? (size_t)js_getnum(len_v) : ab->length - off; 130 + *out = ab->data + off; 131 + *len = blen; 132 + return true; 133 + }} 134 + 135 + return false; 136 + } 137 + 138 + static ant_value_t enqueue_buffer(ant_t *js, ant_value_t ts_obj, const uint8_t *data, size_t len) { 139 + ArrayBufferData *ab = create_array_buffer_data(len); 140 + if (!ab) return js_mkerr(js, "out of memory"); 141 + memcpy(ab->data, data, len); 142 + ant_value_t arr = create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Uint8Array"); 143 + ant_value_t readable = ts_stream_readable(ts_obj); 144 + ant_value_t rs_ctrl = rs_stream_controller(js, readable); 145 + return rs_controller_enqueue(js, rs_ctrl, arr); 146 + } 147 + 148 + #define ZCHUNK_SIZE 16384 149 + 150 + static ant_value_t cs_transform(ant_t *js, ant_value_t *args, int nargs) { 151 + ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 152 + ant_value_t state_val = js_get_slot(wrapper, SLOT_DATA); 153 + zstate_t *st = (zstate_t *)(uintptr_t)(size_t)js_getnum(state_val); 154 + ant_value_t ts_obj = js_get_slot(wrapper, SLOT_ENTRIES); 155 + 156 + ant_value_t chunk = (nargs > 0) ? args[0] : js_mkundef(); 157 + const uint8_t *input = NULL; 158 + size_t input_len = 0; 159 + 160 + if (!is_object_type(chunk) || !get_chunk_bytes(js, chunk, &input, &input_len)) 161 + return js_mkerr_typed(js, JS_ERR_TYPE, "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"); 162 + 163 + if (!input || input_len == 0) return js_mkundef(); 164 + 165 + st->strm.next_in = (Bytef *)input; 166 + st->strm.avail_in = (uInt)input_len; 167 + 168 + uint8_t out_buf[ZCHUNK_SIZE]; 169 + do { 170 + st->strm.next_out = out_buf; 171 + st->strm.avail_out = ZCHUNK_SIZE; 172 + int ret = deflate(&st->strm, Z_NO_FLUSH); 173 + if (ret == Z_STREAM_ERROR) 174 + return js_mkerr_typed(js, JS_ERR_TYPE, "Compression failed"); 175 + size_t have = ZCHUNK_SIZE - st->strm.avail_out; 176 + if (have > 0) { 177 + ant_value_t r = enqueue_buffer(js, ts_obj, out_buf, have); 178 + if (is_err(r)) return r; 179 + } 180 + } while (st->strm.avail_out == 0); 181 + 182 + return js_mkundef(); 183 + } 184 + 185 + static ant_value_t cs_flush(ant_t *js, ant_value_t *args, int nargs) { 186 + ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 187 + 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 + ant_value_t ts_obj = js_get_slot(wrapper, SLOT_ENTRIES); 190 + 191 + st->strm.next_in = NULL; 192 + st->strm.avail_in = 0; 193 + 194 + uint8_t out_buf[ZCHUNK_SIZE]; 195 + int ret; 196 + do { 197 + st->strm.next_out = out_buf; 198 + st->strm.avail_out = ZCHUNK_SIZE; 199 + ret = deflate(&st->strm, Z_FINISH); 200 + size_t have = ZCHUNK_SIZE - st->strm.avail_out; 201 + if (have > 0) { 202 + ant_value_t r = enqueue_buffer(js, ts_obj, out_buf, have); 203 + if (is_err(r)) return r; 204 + } 205 + } while (ret != Z_STREAM_END); 206 + 207 + return js_mkundef(); 208 + } 209 + 210 + static ant_value_t js_cs_get_readable(ant_t *js, ant_value_t *args, int nargs) { 211 + ant_value_t ts_obj = get_ts(js->this_val); 212 + if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid CompressionStream"); 213 + return ts_stream_readable(ts_obj); 214 + } 215 + 216 + static ant_value_t js_cs_get_writable(ant_t *js, ant_value_t *args, int nargs) { 217 + ant_value_t ts_obj = get_ts(js->this_val); 218 + if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid CompressionStream"); 219 + return ts_stream_writable(ts_obj); 220 + } 221 + 222 + static ant_value_t js_cs_ctor(ant_t *js, ant_value_t *args, int nargs) { 223 + if (vtype(js->new_target) == T_UNDEF) 224 + return js_mkerr_typed(js, JS_ERR_TYPE, "CompressionStream constructor requires 'new'"); 225 + 226 + if (nargs < 1) 227 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'CompressionStream': 1 argument required"); 228 + 229 + zformat_t fmt; 230 + if (parse_format(js, args[0], &fmt) < 0) 231 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'CompressionStream': Unsupported compression format"); 232 + 233 + 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; 241 + 242 + ant_value_t obj = js_mkobj(js); 243 + ant_value_t proto = js_instance_proto_from_new_target(js, g_cs_proto); 244 + 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); 247 + 248 + ant_value_t wrapper = js_mkobj(js); 249 + js_set_slot(wrapper, SLOT_DATA, ANT_PTR(st)); 250 + 251 + ant_value_t transformer = js_mkobj(js); 252 + ant_value_t transform_fn = js_heavy_mkfun(js, cs_transform, wrapper); 253 + ant_value_t flush_fn = js_heavy_mkfun(js, cs_flush, wrapper); 254 + js_set(js, transformer, "transform", transform_fn); 255 + js_set(js, transformer, "flush", flush_fn); 256 + 257 + ant_value_t ctor_args[1] = { transformer }; 258 + ant_value_t saved_new_target = js->new_target; 259 + ant_value_t saved_this = js->this_val; 260 + js->new_target = js_mknum(1); 261 + 262 + ant_value_t ts_obj = js_ts_ctor(js, ctor_args, 1); 263 + js->new_target = saved_new_target; 264 + js->this_val = saved_this; 265 + 266 + if (is_err(ts_obj)) { deflateEnd(&st->strm); free(st); return ts_obj; } 267 + js_set_slot(obj, SLOT_ENTRIES, ts_obj); 268 + js_set_slot(wrapper, SLOT_ENTRIES, ts_obj); 269 + 270 + return obj; 271 + } 272 + 273 + static ant_value_t ds_transform(ant_t *js, ant_value_t *args, int nargs) { 274 + ant_value_t wrapper = js_get_slot(js->current_func, SLOT_DATA); 275 + ant_value_t state_val = js_get_slot(wrapper, SLOT_DATA); 276 + zstate_t *st = (zstate_t *)(uintptr_t)(size_t)js_getnum(state_val); 277 + ant_value_t ts_obj = js_get_slot(wrapper, SLOT_ENTRIES); 278 + 279 + ant_value_t chunk = (nargs > 0) ? args[0] : js_mkundef(); 280 + const uint8_t *input = NULL; 281 + size_t input_len = 0; 282 + 283 + if (!is_object_type(chunk) || !get_chunk_bytes(js, chunk, &input, &input_len)) 284 + return js_mkerr_typed(js, JS_ERR_TYPE, "The provided value is not of type '(ArrayBuffer or ArrayBufferView)'"); 285 + 286 + if (!input || input_len == 0) return js_mkundef(); 287 + 288 + st->strm.next_in = (Bytef *)input; 289 + st->strm.avail_in = (uInt)input_len; 290 + 291 + uint8_t out_buf[ZCHUNK_SIZE]; 292 + do { 293 + st->strm.next_out = out_buf; 294 + st->strm.avail_out = ZCHUNK_SIZE; 295 + int ret = inflate(&st->strm, Z_NO_FLUSH); 296 + if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) 297 + return js_mkerr_typed(js, JS_ERR_TYPE, "Decompression failed"); 298 + size_t have = ZCHUNK_SIZE - st->strm.avail_out; 299 + if (have > 0) { 300 + ant_value_t r = enqueue_buffer(js, ts_obj, out_buf, have); 301 + if (is_err(r)) return r; 302 + } 303 + if (ret == Z_STREAM_END) break; 304 + } while (st->strm.avail_out == 0); 305 + 306 + return js_mkundef(); 307 + } 308 + 309 + static ant_value_t ds_flush(ant_t *js, ant_value_t *args, int nargs) { 310 + return js_mkundef(); 311 + } 312 + 313 + static ant_value_t js_ds_get_readable(ant_t *js, ant_value_t *args, int nargs) { 314 + ant_value_t ts_obj = get_ts(js->this_val); 315 + if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid DecompressionStream"); 316 + return ts_stream_readable(ts_obj); 317 + } 318 + 319 + static ant_value_t js_ds_get_writable(ant_t *js, ant_value_t *args, int nargs) { 320 + ant_value_t ts_obj = get_ts(js->this_val); 321 + if (!ts_is_stream(ts_obj)) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid DecompressionStream"); 322 + return ts_stream_writable(ts_obj); 323 + } 324 + 325 + static ant_value_t js_ds_ctor(ant_t *js, ant_value_t *args, int nargs) { 326 + if (vtype(js->new_target) == T_UNDEF) 327 + return js_mkerr_typed(js, JS_ERR_TYPE, "DecompressionStream constructor requires 'new'"); 328 + 329 + if (nargs < 1) 330 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'DecompressionStream': 1 argument required"); 331 + 332 + zformat_t fmt; 333 + if (parse_format(js, args[0], &fmt) < 0) 334 + return js_mkerr_typed(js, JS_ERR_TYPE, "Failed to construct 'DecompressionStream': Unsupported compression format"); 335 + 336 + zstate_t *st = calloc(1, sizeof(zstate_t)); 337 + if (!st) return js_mkerr(js, "out of memory"); 338 + st->format = fmt; 339 + 340 + int ret = inflateInit2(&st->strm, zfmt_window_bits(fmt, true)); 341 + if (ret != Z_OK) { free(st); return js_mkerr(js, "Failed to initialize decompression"); } 342 + st->initialized = true; 343 + 344 + ant_value_t obj = js_mkobj(js); 345 + ant_value_t proto = js_instance_proto_from_new_target(js, g_ds_proto); 346 + if (is_object_type(proto)) js_set_proto_init(obj, proto); 347 + js_set_slot(obj, SLOT_DATA, ANT_PTR(st)); 348 + js_set_finalizer(obj, zstate_inflate_finalize); 349 + 350 + ant_value_t wrapper = js_mkobj(js); 351 + js_set_slot(wrapper, SLOT_DATA, ANT_PTR(st)); 352 + 353 + ant_value_t transformer = js_mkobj(js); 354 + ant_value_t transform_fn = js_heavy_mkfun(js, ds_transform, wrapper); 355 + ant_value_t flush_fn = js_heavy_mkfun(js, ds_flush, wrapper); 356 + 357 + js_set(js, transformer, "transform", transform_fn); 358 + js_set(js, transformer, "flush", flush_fn); 359 + 360 + ant_value_t ctor_args[1] = { transformer }; 361 + ant_value_t saved_new_target = js->new_target; 362 + ant_value_t saved_this = js->this_val; 363 + js->new_target = js_mknum(1); 364 + 365 + ant_value_t ts_obj = js_ts_ctor(js, ctor_args, 1); 366 + 367 + js->new_target = saved_new_target; 368 + js->this_val = saved_this; 369 + if (is_err(ts_obj)) { inflateEnd(&st->strm); free(st); return ts_obj; } 370 + 371 + js_set_slot(obj, SLOT_ENTRIES, ts_obj); 372 + js_set_slot(wrapper, SLOT_ENTRIES, ts_obj); 373 + 374 + return obj; 375 + } 376 + 377 + void init_compression_stream_module(void) { 378 + ant_t *js = rt->js; 379 + ant_value_t g = js_glob(js); 380 + 381 + g_cs_proto = js_mkobj(js); 382 + js_set_getter_desc(js, g_cs_proto, "readable", 8, js_mkfun(js_cs_get_readable), JS_DESC_C); 383 + js_set_getter_desc(js, g_cs_proto, "writable", 8, js_mkfun(js_cs_get_writable), JS_DESC_C); 384 + js_set_sym(js, g_cs_proto, get_toStringTag_sym(), js_mkstr(js, "CompressionStream", 17)); 385 + 386 + ant_value_t cs_ctor = js_make_ctor(js, js_cs_ctor, g_cs_proto, "CompressionStream", 17); 387 + js_set(js, g, "CompressionStream", cs_ctor); 388 + js_set_descriptor(js, g, "CompressionStream", 17, JS_DESC_W | JS_DESC_C); 389 + 390 + g_ds_proto = js_mkobj(js); 391 + js_set_getter_desc(js, g_ds_proto, "readable", 8, js_mkfun(js_ds_get_readable), JS_DESC_C); 392 + js_set_getter_desc(js, g_ds_proto, "writable", 8, js_mkfun(js_ds_get_writable), JS_DESC_C); 393 + js_set_sym(js, g_ds_proto, get_toStringTag_sym(), js_mkstr(js, "DecompressionStream", 19)); 394 + 395 + ant_value_t ds_ctor = js_make_ctor(js, js_ds_ctor, g_ds_proto, "DecompressionStream", 19); 396 + js_set(js, g, "DecompressionStream", ds_ctor); 397 + js_set_descriptor(js, g, "DecompressionStream", 19, JS_DESC_W | JS_DESC_C); 398 + } 399 + 400 + void gc_mark_compression_streams(ant_t *js, void (*mark)(ant_t *, ant_value_t)) { 401 + mark(js, g_cs_proto); 402 + mark(js, g_ds_proto); 403 + }
+16 -10
src/streams/transform.c
··· 14 14 #include "streams/readable.h" 15 15 #include "streams/writable.h" 16 16 17 - static ant_value_t g_ts_proto; 18 - static ant_value_t g_ts_ctrl_proto; 17 + ant_value_t g_ts_proto; 18 + ant_value_t g_ts_ctrl_proto; 19 19 20 20 bool ts_is_controller(ant_value_t obj) { 21 21 if (!is_object_type(obj)) return false; ··· 105 105 return js_get_slot(ts_obj, SLOT_BUFFER); 106 106 } 107 107 108 - static inline ant_value_t ts_controller(ant_value_t ts_obj) { 108 + ant_value_t ts_stream_controller(ant_value_t ts_obj) { 109 109 return js_get_slot(ts_obj, SLOT_DEFAULT); 110 + } 111 + 112 + static inline ant_value_t ts_controller(ant_value_t ts_obj) { 113 + return ts_stream_controller(ts_obj); 110 114 } 111 115 112 116 static inline ant_value_t ts_ctrl_transform_fn(ant_value_t ctrl_obj) { ··· 254 258 ts_set_bp_flag(ts_obj, backpressure); 255 259 } 256 260 257 - static ant_value_t ts_ctrl_enqueue(ant_t *js, ant_value_t ctrl_obj, ant_value_t chunk) { 261 + ant_value_t ts_ctrl_enqueue(ant_t *js, ant_value_t ctrl_obj, ant_value_t chunk) { 258 262 ant_value_t ts_obj = ts_ctrl_stream(ctrl_obj); 259 263 ant_value_t readable = ts_readable(ts_obj); 260 264 ant_value_t rs_ctrl = rs_stream_controller(js, readable); 261 265 rs_stream_t *rs = rs_get_stream(readable); 266 + 262 267 if (!rs || rs->state != RS_STATE_READABLE) 263 268 return js_mkerr_typed(js, JS_ERR_TYPE, "Readable side is not in a readable state"); 264 269 ··· 271 276 272 277 rs_controller_t *rc = rs_get_controller(rs_ctrl); 273 278 bool bp = rc && ((rc->strategy_hwm - rc->queue_total_size) <= 0); 274 - if (bp != ts_get_backpressure(ts_obj)) 275 - ts_set_backpressure(js, ts_obj, bp); 279 + if (bp != ts_get_backpressure(ts_obj)) ts_set_backpressure(js, ts_obj, bp); 280 + 276 281 return js_mkundef(); 277 282 } 278 283 279 - static void ts_ctrl_error(ant_t *js, ant_value_t ctrl_obj, ant_value_t e) { 284 + void ts_ctrl_error(ant_t *js, ant_value_t ctrl_obj, ant_value_t e) { 280 285 ant_value_t ts_obj = ts_ctrl_stream(ctrl_obj); 281 286 if (vtype(ts_cancel_promise(ts_obj)) == T_PROMISE && ts_cancel_has_user_handler(ts_obj)) 282 287 js_set_slot(ts_obj, SLOT_RS_SIZE, e); ··· 284 289 ts_error_writable_and_unblock_write(js, ts_obj, e); 285 290 } 286 291 287 - static void ts_ctrl_terminate(ant_t *js, ant_value_t ctrl_obj) { 292 + void ts_ctrl_terminate(ant_t *js, ant_value_t ctrl_obj) { 288 293 ant_value_t ts_obj = ts_ctrl_stream(ctrl_obj); 289 294 ant_value_t readable = ts_readable(ts_obj); 290 295 ant_value_t rs_ctrl = rs_stream_controller(js, readable); 296 + 291 297 rs_controller_close(js, rs_ctrl); 292 - 293 298 ant_value_t writable = ts_writable(ts_obj); 294 299 ws_stream_t *ws = ws_get_stream(writable); 300 + 295 301 if (ws && ws->state == WS_STATE_WRITABLE) { 296 302 ant_value_t err = js_make_error_silent(js, JS_ERR_TYPE, "TransformStream readable side terminated"); 297 303 ts_error_writable_and_unblock_write(js, ts_obj, err); ··· 817 823 return js_mkundef(); 818 824 } 819 825 820 - static ant_value_t js_ts_ctor(ant_t *js, ant_value_t *args, int nargs) { 826 + ant_value_t js_ts_ctor(ant_t *js, ant_value_t *args, int nargs) { 821 827 if (vtype(js->new_target) == T_UNDEF) 822 828 return js_mkerr_typed(js, JS_ERR_TYPE, "TransformStream constructor requires 'new'"); 823 829