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.

feat(buffer): add encoding support and shared TypedArray prototype

- Add Buffer.from/toString encoding support for base64, hex, utf8,
utf-8, ascii, latin1, binary, ucs2, ucs-2, utf16le, utf-16le
(case-insensitive)
- Add Buffer static methods: isBuffer, isEncoding, byteLength,
concat, compare
- Create shared TypedArray.prototype with slice, subarray, fill
methods inherited by all typed array prototypes
- Fix Buffer to use proper constructor pattern with prototype chain
- Fix Object.prototype.toString to follow prototype chain for
Symbol.toStringTag lookup
- Simplify Buffer.isBuffer to compare prototypes directly
- Remove redundant per-instance method assignments

+461 -74
+38
examples/spec/base64.js
··· 26 26 test('btoa binary', btoa('\x00\x01\x02'), 'AAEC'); 27 27 test('atob binary', atob('AAEC'), '\x00\x01\x02'); 28 28 29 + // Buffer base64 tests 30 + console.log('\nBuffer Base64 Tests\n'); 31 + 32 + const buf1 = Buffer.from('Hello, World!'); 33 + test('Buffer.toString base64', buf1.toString('base64'), 'SGVsbG8sIFdvcmxkIQ=='); 34 + 35 + const buf2 = Buffer.from('ABC'); 36 + test('Buffer.toString base64 ABC', buf2.toString('base64'), 'QUJD'); 37 + 38 + const buf3 = Buffer.from(''); 39 + test('Buffer.toString base64 empty', buf3.toString('base64'), ''); 40 + 41 + const buf4 = Buffer.from([0x00, 0x01, 0x02]); 42 + test('Buffer.toString base64 binary', buf4.toString('base64'), 'AAEC'); 43 + 44 + const buf5 = Buffer.from('A'); 45 + test('Buffer.toString base64 single', buf5.toString('base64'), 'QQ=='); 46 + 47 + const buf6 = Buffer.from('AB'); 48 + test('Buffer.toString base64 two chars', buf6.toString('base64'), 'QUI='); 49 + 50 + // Buffer.toBase64() method 51 + test('Buffer.toBase64', buf1.toBase64(), 'SGVsbG8sIFdvcmxkIQ=='); 52 + test('Buffer.toBase64 ABC', buf2.toBase64(), 'QUJD'); 53 + test('Buffer.toBase64 empty', buf3.toBase64(), ''); 54 + 55 + // Buffer hex encoding 56 + console.log('\nBuffer Hex Tests\n'); 57 + 58 + const hexBuf1 = Buffer.from('Hello'); 59 + test('Buffer.toString hex Hello', hexBuf1.toString('hex'), '48656c6c6f'); 60 + 61 + const hexBuf2 = Buffer.from([0xde, 0xad, 0xbe, 0xef]); 62 + test('Buffer.toString hex deadbeef', hexBuf2.toString('hex'), 'deadbeef'); 63 + 64 + const hexBuf3 = Buffer.from([0x00, 0xff, 0x10]); 65 + test('Buffer.toString hex padding', hexBuf3.toString('hex'), '00ff10'); 66 + 29 67 summary();
+127
examples/spec/buffer.js
··· 120 120 test('TypedArray subarray length', subarrayed.length, 6); 121 121 test('TypedArray subarray byteOffset', subarrayed.byteOffset, 2); 122 122 123 + test('Buffer.isBuffer with Buffer', Buffer.isBuffer(Buffer.from('test')), true); 124 + test('Buffer.isBuffer with string', Buffer.isBuffer('test'), false); 125 + test('Buffer.isBuffer with number', Buffer.isBuffer(123), false); 126 + test('Buffer.isBuffer with object', Buffer.isBuffer({}), false); 127 + test('Buffer.isBuffer with Uint8Array', Buffer.isBuffer(new Uint8Array(4)), false); 128 + 129 + test('Buffer.isEncoding utf8', Buffer.isEncoding('utf8'), true); 130 + test('Buffer.isEncoding utf-8', Buffer.isEncoding('utf-8'), true); 131 + test('Buffer.isEncoding hex', Buffer.isEncoding('hex'), true); 132 + test('Buffer.isEncoding base64', Buffer.isEncoding('base64'), true); 133 + test('Buffer.isEncoding ascii', Buffer.isEncoding('ascii'), true); 134 + test('Buffer.isEncoding latin1', Buffer.isEncoding('latin1'), true); 135 + test('Buffer.isEncoding binary', Buffer.isEncoding('binary'), true); 136 + test('Buffer.isEncoding invalid', Buffer.isEncoding('invalid'), false); 137 + test('Buffer.isEncoding empty', Buffer.isEncoding(''), false); 138 + 139 + test('Buffer.byteLength string', Buffer.byteLength('Hello'), 5); 140 + test('Buffer.byteLength buffer', Buffer.byteLength(Buffer.from('Hello')), 5); 141 + test('Buffer.byteLength empty', Buffer.byteLength(''), 0); 142 + 143 + const concatBuf1 = Buffer.from('Hello'); 144 + const concatBuf2 = Buffer.from(' '); 145 + const concatBuf3 = Buffer.from('World'); 146 + const concatenated = Buffer.concat([concatBuf1, concatBuf2, concatBuf3]); 147 + test('Buffer.concat length', concatenated.length, 11); 148 + test('Buffer.concat toString', concatenated.toString(), 'Hello World'); 149 + 150 + const emptyConcat = Buffer.concat([]); 151 + test('Buffer.concat empty array', emptyConcat.length, 0); 152 + 153 + const singleConcat = Buffer.concat([Buffer.from('Only')]); 154 + test('Buffer.concat single buffer', singleConcat.toString(), 'Only'); 155 + 156 + const limitedConcat = Buffer.concat([concatBuf1, concatBuf2, concatBuf3], 5); 157 + test('Buffer.concat with totalLength', limitedConcat.length, 5); 158 + test('Buffer.concat with totalLength content', limitedConcat.toString(), 'Hello'); 159 + 160 + const cmpA = Buffer.from('abc'); 161 + const cmpB = Buffer.from('abc'); 162 + const cmpC = Buffer.from('abd'); 163 + const cmpD = Buffer.from('ab'); 164 + test('Buffer.compare equal', Buffer.compare(cmpA, cmpB), 0); 165 + test('Buffer.compare less', Buffer.compare(cmpA, cmpC), -1); 166 + test('Buffer.compare greater', Buffer.compare(cmpC, cmpA), 1); 167 + test('Buffer.compare shorter', Buffer.compare(cmpD, cmpA), -1); 168 + test('Buffer.compare longer', Buffer.compare(cmpA, cmpD), 1); 169 + 170 + console.log('\nBuffer Encoding Tests\n'); 171 + 172 + const b64Buf = Buffer.from('Hello, World!'); 173 + test('Buffer.toString base64', b64Buf.toString('base64'), 'SGVsbG8sIFdvcmxkIQ=='); 174 + test('Buffer.toString BASE64 (case insensitive)', b64Buf.toString('BASE64'), 'SGVsbG8sIFdvcmxkIQ=='); 175 + 176 + const b64Decoded = Buffer.from('SGVsbG8sIFdvcmxkIQ==', 'base64'); 177 + test('Buffer.from base64', b64Decoded.toString(), 'Hello, World!'); 178 + test('Buffer.from BASE64 (case insensitive)', Buffer.from('SGVsbG8=', 'BASE64').toString(), 'Hello'); 179 + 180 + const b64Roundtrip = Buffer.from(Buffer.from('hello').toString('base64'), 'base64').toString(); 181 + test('Buffer base64 roundtrip', b64Roundtrip, 'hello'); 182 + 183 + const hexBuf = Buffer.from('Hello'); 184 + test('Buffer.toString hex', hexBuf.toString('hex'), '48656c6c6f'); 185 + test('Buffer.toString HEX (case insensitive)', hexBuf.toString('HEX'), '48656c6c6f'); 186 + 187 + const hexDecoded = Buffer.from('48656c6c6f', 'hex'); 188 + test('Buffer.from hex', hexDecoded.toString(), 'Hello'); 189 + test('Buffer.from HEX (case insensitive)', Buffer.from('48656C6C6F', 'HEX').toString(), 'Hello'); 190 + 191 + const hexRoundtrip = Buffer.from(Buffer.from('world').toString('hex'), 'hex').toString(); 192 + test('Buffer hex roundtrip', hexRoundtrip, 'world'); 193 + 194 + const utf8Buf = Buffer.from('Hello', 'utf8'); 195 + test('Buffer.from utf8', utf8Buf.toString(), 'Hello'); 196 + test('Buffer.from utf-8', Buffer.from('Hello', 'utf-8').toString(), 'Hello'); 197 + test('Buffer.from UTF8 (case insensitive)', Buffer.from('Hello', 'UTF8').toString(), 'Hello'); 198 + test('Buffer.from UTF-8 (case insensitive)', Buffer.from('Hello', 'UTF-8').toString(), 'Hello'); 199 + 200 + test('Buffer.toString utf8', utf8Buf.toString('utf8'), 'Hello'); 201 + test('Buffer.toString utf-8', utf8Buf.toString('utf-8'), 'Hello'); 202 + 203 + const asciiBuf = Buffer.from('Hello', 'ascii'); 204 + test('Buffer.from ascii', asciiBuf.toString(), 'Hello'); 205 + test('Buffer.from ASCII (case insensitive)', Buffer.from('Hello', 'ASCII').toString(), 'Hello'); 206 + test('Buffer.toString ascii', asciiBuf.toString('ascii'), 'Hello'); 207 + 208 + const latin1Buf = Buffer.from('Hello', 'latin1'); 209 + test('Buffer.from latin1', latin1Buf.toString(), 'Hello'); 210 + test('Buffer.from LATIN1 (case insensitive)', Buffer.from('Hello', 'LATIN1').toString(), 'Hello'); 211 + test('Buffer.from binary', Buffer.from('Hello', 'binary').toString(), 'Hello'); 212 + test('Buffer.from BINARY (case insensitive)', Buffer.from('Hello', 'BINARY').toString(), 'Hello'); 213 + 214 + test('Buffer.toString latin1', latin1Buf.toString('latin1'), 'Hello'); 215 + test('Buffer.toString binary', latin1Buf.toString('binary'), 'Hello'); 216 + 217 + const ucs2Buf = Buffer.from('Hi', 'ucs2'); 218 + test('Buffer.from ucs2 length', ucs2Buf.length, 4); // 2 chars * 2 bytes 219 + test('Buffer.from ucs-2 length', Buffer.from('Hi', 'ucs-2').length, 4); 220 + test('Buffer.from utf16le length', Buffer.from('Hi', 'utf16le').length, 4); 221 + test('Buffer.from utf-16le length', Buffer.from('Hi', 'utf-16le').length, 4); 222 + test('Buffer.from UCS2 (case insensitive)', Buffer.from('Hi', 'UCS2').length, 4); 223 + 224 + test('Buffer.from ucs2 byte 0', ucs2Buf[0], 0x48); 225 + test('Buffer.from ucs2 byte 1', ucs2Buf[1], 0x00); 226 + test('Buffer.from ucs2 byte 2', ucs2Buf[2], 0x69); 227 + test('Buffer.from ucs2 byte 3', ucs2Buf[3], 0x00); 228 + 229 + test('Buffer.toString ucs2', ucs2Buf.toString('ucs2'), 'Hi'); 230 + test('Buffer.toString ucs-2', ucs2Buf.toString('ucs-2'), 'Hi'); 231 + test('Buffer.toString utf16le', ucs2Buf.toString('utf16le'), 'Hi'); 232 + test('Buffer.toString utf-16le', ucs2Buf.toString('utf-16le'), 'Hi'); 233 + 234 + const binaryBuf = Buffer.from([0x00, 0x01, 0x02, 0xff]); 235 + test('Buffer binary toString base64', binaryBuf.toString('base64'), 'AAEC/w=='); 236 + test('Buffer binary toString hex', binaryBuf.toString('hex'), '000102ff'); 237 + 238 + const binaryFromB64 = Buffer.from('AAEC/w==', 'base64'); 239 + test('Buffer binary from base64 byte 0', binaryFromB64[0], 0x00); 240 + test('Buffer binary from base64 byte 1', binaryFromB64[1], 0x01); 241 + test('Buffer binary from base64 byte 2', binaryFromB64[2], 0x02); 242 + test('Buffer binary from base64 byte 3', binaryFromB64[3], 0xff); 243 + 244 + const binaryFromHex = Buffer.from('000102ff', 'hex'); 245 + test('Buffer binary from hex byte 0', binaryFromHex[0], 0x00); 246 + test('Buffer binary from hex byte 1', binaryFromHex[1], 0x01); 247 + test('Buffer binary from hex byte 2', binaryFromHex[2], 0x02); 248 + test('Buffer binary from hex byte 3', binaryFromHex[3], 0xff); 249 + 123 250 summary();
+11 -26
examples/spec/dataview.js
··· 2 2 3 3 console.log('DataView Tests\n'); 4 4 5 - // Basic DataView creation 6 5 const buffer = new ArrayBuffer(16); 7 6 const dv = new DataView(buffer); 8 7 9 8 test('DataView byteLength', dv.byteLength, 16); 10 9 test('DataView byteOffset', dv.byteOffset, 0); 11 10 12 - // DataView with offset 13 11 const dv2 = new DataView(buffer, 4); 14 12 test('DataView with offset byteLength', dv2.byteLength, 12); 15 13 test('DataView with offset byteOffset', dv2.byteOffset, 4); 16 14 17 - // DataView with offset and length 18 15 const dv3 = new DataView(buffer, 4, 8); 19 16 test('DataView with offset+length byteLength', dv3.byteLength, 8); 20 17 test('DataView with offset+length byteOffset', dv3.byteOffset, 4); 21 18 22 - // Uint8 get/set 23 19 dv.setUint8(0, 255); 24 20 test('setUint8/getUint8', dv.getUint8(0), 255); 25 21 26 22 dv.setUint8(1, 0); 27 23 test('setUint8/getUint8 zero', dv.getUint8(1), 0); 28 24 29 - // Int16 get/set - Big Endian (default) 30 25 dv.setInt16(0, 0x1234); 31 26 test('setInt16 BE / getInt16 BE', dv.getInt16(0), 0x1234); 32 27 test('setInt16 BE byte 0', dv.getUint8(0), 0x12); 33 28 test('setInt16 BE byte 1', dv.getUint8(1), 0x34); 34 29 35 - // Int16 get/set - Little Endian 36 30 dv.setInt16(2, 0x5678, true); 37 31 test('setInt16 LE / getInt16 LE', dv.getInt16(2, true), 0x5678); 38 32 test('setInt16 LE byte 0', dv.getUint8(2), 0x78); 39 33 test('setInt16 LE byte 1', dv.getUint8(3), 0x56); 40 34 41 - // Read Int16 LE as BE and vice versa 42 35 test('setInt16 LE / getInt16 BE', dv.getInt16(2, false), 0x7856); 43 36 44 - // Int32 get/set - Big Endian (default) 45 37 dv.setInt32(4, 0x12345678); 46 38 test('setInt32 BE / getInt32 BE', dv.getInt32(4), 0x12345678); 47 39 test('setInt32 BE byte 0', dv.getUint8(4), 0x12); ··· 49 41 test('setInt32 BE byte 2', dv.getUint8(6), 0x56); 50 42 test('setInt32 BE byte 3', dv.getUint8(7), 0x78); 51 43 52 - // Int32 get/set - Little Endian 53 - dv.setInt32(8, 0xAABBCCDD, true); 54 - test('setInt32 LE / getInt32 LE', dv.getInt32(8, true), 0xAABBCCDD | 0); 55 - test('setInt32 LE byte 0', dv.getUint8(8), 0xDD); 56 - test('setInt32 LE byte 1', dv.getUint8(9), 0xCC); 57 - test('setInt32 LE byte 2', dv.getUint8(10), 0xBB); 58 - test('setInt32 LE byte 3', dv.getUint8(11), 0xAA); 44 + dv.setInt32(8, 0xaabbccdd, true); 45 + test('setInt32 LE / getInt32 LE', dv.getInt32(8, true), 0xaabbccdd | 0); 46 + test('setInt32 LE byte 0', dv.getUint8(8), 0xdd); 47 + test('setInt32 LE byte 1', dv.getUint8(9), 0xcc); 48 + test('setInt32 LE byte 2', dv.getUint8(10), 0xbb); 49 + test('setInt32 LE byte 3', dv.getUint8(11), 0xaa); 59 50 60 - // Float32 get/set 61 51 const testFloat = 3.14; 62 52 dv.setFloat32(0, testFloat); 63 53 const readFloat = dv.getFloat32(0); ··· 67 57 const readFloatLE = dv.getFloat32(4, true); 68 58 test('setFloat32/getFloat32 LE approx', Math.abs(readFloatLE - testFloat) < 0.001, true); 69 59 70 - // Float64 get/set 71 60 const testDouble = 3.141592653589793; 72 61 dv.setFloat64(0, testDouble); 73 62 test('setFloat64/getFloat64 BE', dv.getFloat64(0), testDouble); ··· 75 64 dv.setFloat64(8, testDouble, true); 76 65 test('setFloat64/getFloat64 LE', dv.getFloat64(8, true), testDouble); 77 66 78 - // Negative numbers 79 67 dv.setInt16(0, -1234); 80 68 test('setInt16/getInt16 negative BE', dv.getInt16(0), -1234); 81 69 ··· 88 76 dv.setInt32(8, -123456789, true); 89 77 test('setInt32/getInt32 negative LE', dv.getInt32(8, true), -123456789); 90 78 91 - // Test that DataView shares buffer with TypedArrays 92 79 const sharedBuffer = new ArrayBuffer(8); 93 80 const sharedDV = new DataView(sharedBuffer); 94 81 const sharedU8 = new Uint8Array(sharedBuffer); ··· 99 86 test('DataView/TypedArray share buffer byte 2', sharedU8[2], 0x03); 100 87 test('DataView/TypedArray share buffer byte 3', sharedU8[3], 0x04); 101 88 102 - // Modify via TypedArray, read via DataView 103 - sharedU8[4] = 0xAA; 104 - sharedU8[5] = 0xBB; 105 - sharedU8[6] = 0xCC; 106 - sharedU8[7] = 0xDD; 107 - test('TypedArray write / DataView read', sharedDV.getInt32(4), 0xAABBCCDD | 0); 89 + sharedU8[4] = 0xaa; 90 + sharedU8[5] = 0xbb; 91 + sharedU8[6] = 0xcc; 92 + sharedU8[7] = 0xdd; 93 + test('TypedArray write / DataView read', sharedDV.getInt32(4), 0xaabbccdd | 0); 108 94 109 - // Node.js style example from the prompt 110 95 const exampleBuffer = new ArrayBuffer(16); 111 96 const exampleView = new DataView(exampleBuffer); 112 97 exampleView.setInt32(0, 123456);
+1
src/ant.c
··· 14647 14647 jsval_t check_obj = (t == T_FUNC) ? mkval(T_OBJ, vdata(obj)) : obj; 14648 14648 const char *tostr_tag_key = get_toStringTag_sym_key(); 14649 14649 jsoff_t tag_off = lkp(js, check_obj, tostr_tag_key, strlen(tostr_tag_key)); 14650 + if (tag_off == 0) tag_off = lkp_proto(js, check_obj, tostr_tag_key, strlen(tostr_tag_key)); 14650 14651 if (tag_off != 0) { 14651 14652 jsval_t tag_val = resolveprop(js, mkval(T_PROP, tag_off)); 14652 14653 if (vtype(tag_val) == T_STR) {
+284 -48
src/modules/buffer.c
··· 328 328 js_set(js, obj, "byteLength", js_mknum((double)(length * element_size))); 329 329 js_set(js, obj, "byteOffset", js_mknum((double)byte_offset)); 330 330 js_set(js, obj, "BYTES_PER_ELEMENT", js_mknum((double)element_size)); 331 - js_set(js, obj, "slice", js_mkfun(js_typedarray_slice)); 332 - js_set(js, obj, "subarray", js_mkfun(js_typedarray_subarray)); 333 - js_set(js, obj, "fill", js_mkfun(js_typedarray_fill)); 334 331 335 332 js_set_getter(js, obj, typedarray_index_getter); 336 333 js_set_setter(js, obj, typedarray_index_setter); ··· 929 926 return js_mkundef(); 930 927 } 931 928 932 - // Buffer.from(array/string/buffer) 929 + static uint8_t *hex_decode(const char *data, size_t len, size_t *out_len) { 930 + if (len % 2 != 0) return NULL; 931 + 932 + size_t decoded_len = len / 2; 933 + uint8_t *decoded = ANT_GC_MALLOC(decoded_len); 934 + if (!decoded) return NULL; 935 + 936 + for (size_t i = 0; i < decoded_len; i++) { 937 + unsigned int byte; 938 + if (sscanf(data + i * 2, "%2x", &byte) != 1) { 939 + ANT_GC_FREE(decoded); return NULL; 940 + } 941 + decoded[i] = (uint8_t)byte; 942 + } 943 + 944 + *out_len = decoded_len; 945 + return decoded; 946 + } 947 + 948 + typedef enum { 949 + ENC_UTF8, 950 + ENC_HEX, 951 + ENC_BASE64, 952 + ENC_ASCII, 953 + ENC_LATIN1, 954 + ENC_UCS2, 955 + ENC_UNKNOWN 956 + } BufferEncoding; 957 + 958 + static BufferEncoding parse_encoding(const char *enc, size_t len) { 959 + if (len == 3 && strncasecmp(enc, "hex", 3) == 0) return ENC_HEX; 960 + if (len == 5 && strncasecmp(enc, "ascii", 5) == 0) return ENC_ASCII; 961 + if (len == 6 && strncasecmp(enc, "base64", 6) == 0) return ENC_BASE64; 962 + if ((len == 4 && strncasecmp(enc, "utf8", 4) == 0) || (len == 5 && strncasecmp(enc, "utf-8", 5) == 0)) return ENC_UTF8; 963 + if ((len == 6 && strncasecmp(enc, "latin1", 6) == 0) || (len == 6 && strncasecmp(enc, "binary", 6) == 0)) return ENC_LATIN1; 964 + 965 + if ( 966 + (len == 4 && strncasecmp(enc, "ucs2", 4) == 0) || 967 + (len == 5 && strncasecmp(enc, "ucs-2", 5) == 0) || 968 + (len == 7 && strncasecmp(enc, "utf16le", 7) == 0) || 969 + (len == 8 && strncasecmp(enc, "utf-16le", 8) == 0) 970 + ) return ENC_UCS2; 971 + 972 + return ENC_UNKNOWN; 973 + } 974 + 975 + // Buffer.from(array/string/buffer, encoding) 933 976 static jsval_t js_buffer_from(struct js *js, jsval_t *args, int nargs) { 934 - if (nargs < 1) { 935 - return js_mkerr(js, "Buffer.from requires at least one argument"); 936 - } 977 + if (nargs < 1) return js_mkerr(js, "Buffer.from requires at least one argument"); 937 978 938 979 if (js_type(args[0]) == JS_STR) { 939 980 size_t len; 940 981 char *str = js_getstr(js, args[0], &len); 941 982 942 - ArrayBufferData *buffer = create_array_buffer_data(len); 943 - if (!buffer) return js_mkerr(js, "Failed to allocate buffer"); 983 + BufferEncoding encoding = ENC_UTF8; 984 + if (nargs >= 2 && js_type(args[1]) == JS_STR) { 985 + size_t enc_len; 986 + char *enc_str = js_getstr(js, args[1], &enc_len); 987 + encoding = parse_encoding(enc_str, enc_len); 988 + if (encoding == ENC_UNKNOWN) encoding = ENC_UTF8; 989 + } 944 990 945 - memcpy(buffer->data, str, len); 946 - jsval_t obj = create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, len, "Buffer"); 947 - js_set(js, obj, "toString", js_mkfun(js_buffer_toString)); 948 - js_set(js, obj, "toBase64", js_mkfun(js_buffer_toBase64)); 949 - js_set(js, obj, "write", js_mkfun(js_buffer_write)); 950 - return obj; 991 + if (encoding == ENC_BASE64) { 992 + size_t decoded_len; 993 + uint8_t *decoded = base64_decode(str, len, &decoded_len); 994 + if (!decoded) return js_mkerr(js, "Failed to decode base64"); 995 + 996 + ArrayBufferData *buffer = create_array_buffer_data(decoded_len); 997 + if (!buffer) { free(decoded); return js_mkerr(js, "Failed to allocate buffer"); } 998 + 999 + memcpy(buffer->data, decoded, decoded_len); 1000 + free(decoded); 1001 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, decoded_len, "Buffer"); 1002 + } else if (encoding == ENC_HEX) { 1003 + size_t decoded_len; 1004 + uint8_t *decoded = hex_decode(str, len, &decoded_len); 1005 + if (!decoded) return js_mkerr(js, "Failed to decode hex"); 1006 + 1007 + ArrayBufferData *buffer = create_array_buffer_data(decoded_len); 1008 + if (!buffer) { free(decoded); return js_mkerr(js, "Failed to allocate buffer"); } 1009 + 1010 + memcpy(buffer->data, decoded, decoded_len); 1011 + free(decoded); 1012 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, decoded_len, "Buffer"); 1013 + } else if (encoding == ENC_UCS2) { 1014 + size_t decoded_len = len * 2; 1015 + ArrayBufferData *buffer = create_array_buffer_data(decoded_len); 1016 + if (!buffer) return js_mkerr(js, "Failed to allocate buffer"); 1017 + 1018 + for (size_t i = 0; i < len; i++) { 1019 + buffer->data[i * 2] = (uint8_t)str[i]; 1020 + buffer->data[i * 2 + 1] = 0; 1021 + } 1022 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, decoded_len, "Buffer"); 1023 + } else { 1024 + ArrayBufferData *buffer = create_array_buffer_data(len); 1025 + if (!buffer) return js_mkerr(js, "Failed to allocate buffer"); 1026 + 1027 + memcpy(buffer->data, str, len); 1028 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, len, "Buffer"); 1029 + } 951 1030 } 952 1031 953 1032 jsval_t length_val = js_get(js, args[0], "length"); ··· 965 1044 } 966 1045 } 967 1046 968 - jsval_t obj = create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, len, "Buffer"); 969 - js_set(js, obj, "toString", js_mkfun(js_buffer_toString)); 970 - js_set(js, obj, "toBase64", js_mkfun(js_buffer_toBase64)); 971 - js_set(js, obj, "write", js_mkfun(js_buffer_write)); 972 - return obj; 1047 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, len, "Buffer"); 973 1048 } 974 1049 975 1050 return js_mkerr(js, "Invalid argument to Buffer.from"); ··· 986 1061 if (!buffer) return js_mkerr(js, "Failed to allocate buffer"); 987 1062 988 1063 memset(buffer->data, 0, size); 989 - 990 - jsval_t obj = create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, size, "Buffer"); 991 - js_set(js, obj, "toString", js_mkfun(js_buffer_toString)); 992 - js_set(js, obj, "toBase64", js_mkfun(js_buffer_toBase64)); 993 - js_set(js, obj, "write", js_mkfun(js_buffer_write)); 994 - 995 - return obj; 1064 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, size, "Buffer"); 996 1065 } 997 1066 998 1067 // Buffer.allocUnsafe(size) ··· 1004 1073 size_t size = (size_t)js_getnum(args[0]); 1005 1074 ArrayBufferData *buffer = create_array_buffer_data(size); 1006 1075 if (!buffer) return js_mkerr(js, "Failed to allocate buffer"); 1007 - 1008 - jsval_t obj = create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, size, "Buffer"); 1009 - js_set(js, obj, "toString", js_mkfun(js_buffer_toString)); 1010 - js_set(js, obj, "toBase64", js_mkfun(js_buffer_toBase64)); 1011 - js_set(js, obj, "write", js_mkfun(js_buffer_write)); 1012 - 1013 - return obj; 1076 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, size, "Buffer"); 1014 1077 } 1015 1078 1016 1079 // Buffer.prototype.toString(encoding) ··· 1021 1084 TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); 1022 1085 if (!ta_data) return js_mkerr(js, "Invalid Buffer"); 1023 1086 1024 - char *encoding = "utf8"; 1087 + BufferEncoding encoding = ENC_UTF8; 1025 1088 if (nargs > 0 && js_type(args[0]) == JS_STR) { 1026 - encoding = js_getstr(js, args[0], NULL); 1089 + size_t enc_len; 1090 + char *enc_str = js_getstr(js, args[0], &enc_len); 1091 + encoding = parse_encoding(enc_str, enc_len); 1092 + if (encoding == ENC_UNKNOWN) encoding = ENC_UTF8; 1027 1093 } 1028 1094 1029 1095 uint8_t *data = ta_data->buffer->data + ta_data->byte_offset; 1030 1096 size_t len = ta_data->byte_length; 1031 1097 1032 - if (strcmp(encoding, "base64") == 0) { 1098 + if (encoding == ENC_BASE64) { 1033 1099 size_t out_len; 1034 1100 char *encoded = base64_encode(data, len, &out_len); 1035 1101 if (!encoded) return js_mkerr(js, "Failed to encode base64"); ··· 1037 1103 jsval_t result = js_mkstr(js, encoded, out_len); 1038 1104 free(encoded); 1039 1105 return result; 1040 - } else if (strcmp(encoding, "hex") == 0) { 1106 + } else if (encoding == ENC_HEX) { 1041 1107 char *hex = malloc(len * 2 + 1); 1042 1108 if (!hex) return js_mkerr(js, "Failed to allocate hex string"); 1043 1109 ··· 1048 1114 jsval_t result = js_mkstr(js, hex, len * 2); 1049 1115 free(hex); 1050 1116 return result; 1051 - } else return js_mkstr(js, data, len); 1117 + } else if (encoding == ENC_UCS2) { 1118 + size_t char_count = len / 2; 1119 + char *str = malloc(char_count + 1); 1120 + if (!str) return js_mkerr(js, "Failed to allocate string"); 1121 + 1122 + for (size_t i = 0; i < char_count; i++) str[i] = (char)data[i * 2]; 1123 + str[char_count] = '\0'; 1124 + 1125 + jsval_t result = js_mkstr(js, str, char_count); 1126 + free(str); 1127 + return result; 1128 + } else return js_mkstr(js, (char *)data, len); 1052 1129 } 1053 1130 1054 1131 // Buffer.prototype.toBase64() ··· 1094 1171 return js_mknum((double)to_write); 1095 1172 } 1096 1173 1174 + // Buffer.isBuffer(obj) 1175 + static jsval_t js_buffer_isBuffer(struct js *js, jsval_t *args, int nargs) { 1176 + if (nargs < 1) return js_mkfalse(); 1177 + if (js_type(args[0]) != JS_OBJ) return js_mkfalse(); 1178 + 1179 + jsval_t proto = js_get_proto(js, args[0]); 1180 + jsval_t buffer_proto = js_get_ctor_proto(js, "Buffer", 6); 1181 + 1182 + return (proto == buffer_proto) ? js_mktrue() : js_mkfalse(); 1183 + } 1184 + 1185 + // Buffer.isEncoding(encoding) 1186 + static jsval_t js_buffer_isEncoding(struct js *js, jsval_t *args, int nargs) { 1187 + if (nargs < 1 || js_type(args[0]) != JS_STR) return js_mkfalse(); 1188 + 1189 + size_t len; 1190 + char *enc = js_getstr(js, args[0], &len); 1191 + 1192 + if ((len == 4 && strncasecmp(enc, "utf8", 4) == 0) || 1193 + (len == 5 && strncasecmp(enc, "utf-8", 5) == 0) || 1194 + (len == 3 && strncasecmp(enc, "hex", 3) == 0) || 1195 + (len == 6 && strncasecmp(enc, "base64", 6) == 0) || 1196 + (len == 5 && strncasecmp(enc, "ascii", 5) == 0) || 1197 + (len == 6 && strncasecmp(enc, "latin1", 6) == 0) || 1198 + (len == 6 && strncasecmp(enc, "binary", 6) == 0) || 1199 + (len == 4 && strncasecmp(enc, "ucs2", 4) == 0) || 1200 + (len == 5 && strncasecmp(enc, "ucs-2", 5) == 0) || 1201 + (len == 7 && strncasecmp(enc, "utf16le", 7) == 0) || 1202 + (len == 8 && strncasecmp(enc, "utf-16le", 8) == 0)) { 1203 + return js_mktrue(); 1204 + } 1205 + 1206 + return js_mkfalse(); 1207 + } 1208 + 1209 + // Buffer.byteLength(string, encoding) 1210 + static jsval_t js_buffer_byteLength(struct js *js, jsval_t *args, int nargs) { 1211 + if (nargs < 1) return js_mknum(0); 1212 + 1213 + jsval_t arg = args[0]; 1214 + 1215 + if (js_type(arg) == JS_OBJ) { 1216 + jsval_t bytelen = js_get(js, arg, "byteLength"); 1217 + if (js_type(bytelen) == JS_NUM) return bytelen; 1218 + 1219 + jsval_t len = js_get(js, arg, "length"); 1220 + if (js_type(len) == JS_NUM) return len; 1221 + } 1222 + 1223 + if (js_type(arg) == JS_STR) { 1224 + size_t len; 1225 + js_getstr(js, arg, &len); 1226 + return js_mknum((double)len); 1227 + } 1228 + 1229 + return js_mknum(0); 1230 + } 1231 + 1232 + // Buffer.concat(list, totalLength) 1233 + static jsval_t js_buffer_concat(struct js *js, jsval_t *args, int nargs) { 1234 + if (nargs < 1 || js_type(args[0]) != JS_OBJ) { 1235 + return js_mkerr(js, "First argument must be an array"); 1236 + } 1237 + 1238 + jsval_t list = args[0]; 1239 + jsval_t len_val = js_get(js, list, "length"); 1240 + if (js_type(len_val) != JS_NUM) { 1241 + return js_mkerr(js, "First argument must be an array"); 1242 + } 1243 + 1244 + size_t list_len = (size_t)js_getnum(len_val); 1245 + size_t total_length = 0; 1246 + 1247 + if (nargs > 1 && js_type(args[1]) == JS_NUM) { 1248 + total_length = (size_t)js_getnum(args[1]); 1249 + } else { 1250 + for (size_t i = 0; i < list_len; i++) { 1251 + char idx[16]; 1252 + snprintf(idx, sizeof(idx), "%zu", i); 1253 + jsval_t buf = js_get(js, list, idx); 1254 + jsval_t buf_len = js_get(js, buf, "length"); 1255 + if (js_type(buf_len) == JS_NUM) total_length += (size_t)js_getnum(buf_len); 1256 + } 1257 + } 1258 + 1259 + ArrayBufferData *buffer = create_array_buffer_data(total_length); 1260 + if (!buffer) return js_mkerr(js, "Failed to allocate buffer"); 1261 + 1262 + size_t offset = 0; 1263 + for (size_t i = 0; i < list_len && offset < total_length; i++) { 1264 + char idx[16]; 1265 + snprintf(idx, sizeof(idx), "%zu", i); 1266 + jsval_t buf = js_get(js, list, idx); 1267 + 1268 + jsval_t ta_data_val = js_get_slot(js, buf, SLOT_BUFFER); 1269 + TypedArrayData *ta = js_gettypedarray(ta_data_val); 1270 + if (!ta || !ta->buffer) continue; 1271 + 1272 + size_t copy_len = ta->byte_length; 1273 + if (offset + copy_len > total_length) { 1274 + copy_len = total_length - offset; 1275 + } 1276 + 1277 + memcpy(buffer->data + offset, ta->buffer->data + ta->byte_offset, copy_len); 1278 + offset += copy_len; 1279 + } 1280 + 1281 + return create_typed_array(js, TYPED_ARRAY_UINT8, buffer, 0, total_length, "Buffer"); 1282 + } 1283 + 1284 + // Buffer.compare(buf1, buf2) 1285 + static jsval_t js_buffer_compare(struct js *js, jsval_t *args, int nargs) { 1286 + if (nargs < 2) return js_mkerr(js, "Buffer.compare requires two arguments"); 1287 + 1288 + jsval_t ta1_val = js_get_slot(js, args[0], SLOT_BUFFER); 1289 + jsval_t ta2_val = js_get_slot(js, args[1], SLOT_BUFFER); 1290 + 1291 + TypedArrayData *ta1 = js_gettypedarray(ta1_val); 1292 + TypedArrayData *ta2 = js_gettypedarray(ta2_val); 1293 + 1294 + if (!ta1 || !ta2) { 1295 + return js_mkerr(js, "Arguments must be Buffers"); 1296 + } 1297 + 1298 + if (!ta1 || !ta1->buffer || !ta2 || !ta2->buffer) { 1299 + return js_mkerr(js, "Invalid buffer"); 1300 + } 1301 + 1302 + size_t len = ta1->byte_length < ta2->byte_length ? ta1->byte_length : ta2->byte_length; 1303 + int cmp = memcmp(ta1->buffer->data + ta1->byte_offset, ta2->buffer->data + ta2->byte_offset, len); 1304 + 1305 + if (cmp == 0) { 1306 + if (ta1->byte_length < ta2->byte_length) cmp = -1; 1307 + else if (ta1->byte_length > ta2->byte_length) cmp = 1; 1308 + } else cmp = cmp < 0 ? -1 : 1; 1309 + 1310 + return js_mknum((double)cmp); 1311 + } 1312 + 1097 1313 static jsval_t js_sharedarraybuffer_constructor(struct js *js, jsval_t *args, int nargs) { 1098 1314 size_t length = 0; 1099 1315 if (nargs > 0 && js_type(args[0]) == JS_NUM) { ··· 1131 1347 js_set_descriptor(js, arraybuffer_ctor_obj, "name", 4, 0); 1132 1348 js_set(js, glob, "ArrayBuffer", js_obj_to_func(arraybuffer_ctor_obj)); 1133 1349 1350 + jsval_t typedarray_proto = js_mkobj(js); 1351 + js_set(js, typedarray_proto, "slice", js_mkfun(js_typedarray_slice)); 1352 + js_set(js, typedarray_proto, "subarray", js_mkfun(js_typedarray_subarray)); 1353 + js_set(js, typedarray_proto, "fill", js_mkfun(js_typedarray_fill)); 1354 + js_set(js, typedarray_proto, get_toStringTag_sym_key(), js_mkstr(js, "TypedArray", 10)); 1355 + 1134 1356 #define SETUP_TYPEDARRAY(name) \ 1135 1357 do { \ 1136 1358 jsval_t name##_ctor_obj = js_mkobj(js); \ 1137 1359 jsval_t name##_proto = js_mkobj(js); \ 1138 - js_set(js, name##_proto, "slice", js_mkfun(js_typedarray_slice)); \ 1139 - js_set(js, name##_proto, "subarray", js_mkfun(js_typedarray_subarray)); \ 1140 - js_set(js, name##_proto, "fill", js_mkfun(js_typedarray_fill)); \ 1360 + js_set_proto(js, name##_proto, typedarray_proto); \ 1141 1361 js_set_slot(js, name##_ctor_obj, SLOT_CFUNC, js_mkfun(js_##name##_constructor)); \ 1142 1362 js_setprop(js, name##_ctor_obj, js_mkstr(js, "prototype", 9), name##_proto); \ 1143 1363 js_set(js, glob, #name, js_obj_to_func(name##_ctor_obj)); \ ··· 1188 1408 js_set_descriptor(js, sharedarraybuffer_ctor_obj, "name", 4, 0); 1189 1409 js_set(js, glob, "SharedArrayBuffer", js_obj_to_func(sharedarraybuffer_ctor_obj)); 1190 1410 1191 - jsval_t buffer_obj = js_mkobj(js); 1192 - js_set(js, buffer_obj, "from", js_mkfun(js_buffer_from)); 1193 - js_set(js, buffer_obj, "alloc", js_mkfun(js_buffer_alloc)); 1194 - js_set(js, buffer_obj, "allocUnsafe", js_mkfun(js_buffer_allocUnsafe)); 1195 - js_set(js, buffer_obj, get_toStringTag_sym_key(), js_mkstr(js, "Buffer", 6)); 1196 - js_set(js, glob, "Buffer", buffer_obj); 1411 + jsval_t buffer_ctor_obj = js_mkobj(js); 1412 + jsval_t buffer_proto = js_mkobj(js); 1413 + 1414 + js_set(js, buffer_proto, "toString", js_mkfun(js_buffer_toString)); 1415 + js_set(js, buffer_proto, "toBase64", js_mkfun(js_buffer_toBase64)); 1416 + js_set(js, buffer_proto, "write", js_mkfun(js_buffer_write)); 1417 + js_set(js, buffer_proto, get_toStringTag_sym_key(), js_mkstr(js, "Buffer", 6)); 1418 + 1419 + js_set(js, buffer_ctor_obj, "from", js_mkfun(js_buffer_from)); 1420 + js_set(js, buffer_ctor_obj, "alloc", js_mkfun(js_buffer_alloc)); 1421 + js_set(js, buffer_ctor_obj, "allocUnsafe", js_mkfun(js_buffer_allocUnsafe)); 1422 + js_set(js, buffer_ctor_obj, "isBuffer", js_mkfun(js_buffer_isBuffer)); 1423 + js_set(js, buffer_ctor_obj, "isEncoding", js_mkfun(js_buffer_isEncoding)); 1424 + js_set(js, buffer_ctor_obj, "byteLength", js_mkfun(js_buffer_byteLength)); 1425 + js_set(js, buffer_ctor_obj, "concat", js_mkfun(js_buffer_concat)); 1426 + js_set(js, buffer_ctor_obj, "compare", js_mkfun(js_buffer_compare)); 1427 + 1428 + js_set_slot(js, buffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_buffer_from)); 1429 + js_mkprop_fast(js, buffer_ctor_obj, "prototype", 9, buffer_proto); 1430 + js_mkprop_fast(js, buffer_ctor_obj, "name", 4, ANT_STRING("Buffer")); 1431 + js_set_descriptor(js, buffer_ctor_obj, "name", 4, 0); 1432 + js_set(js, glob, "Buffer", js_obj_to_func(buffer_ctor_obj)); 1197 1433 }