MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#include <stdlib.h>
2#include <string.h>
3#include <stdint.h>
4#include <stdbool.h>
5
6#include "ant.h"
7#include "ptr.h"
8#include "base64.h"
9#include "errors.h"
10#include "internal.h"
11#include "descriptors.h"
12
13#include "modules/buffer.h"
14#include "modules/symbol.h"
15#include "modules/textcodec.h"
16#include "modules/string_decoder.h"
17
18#define SD_ENC_UTF8 0
19#define SD_ENC_UTF16LE 1
20#define SD_ENC_UTF16BE 2
21#define SD_ENC_LATIN1 3
22#define SD_ENC_HEX 4
23#define SD_ENC_BASE64 5
24
25typedef struct {
26 int encoding;
27 td_state_t *td;
28 uint8_t pending[2];
29 int pending_len;
30} sd_state_t;
31
32static ant_value_t g_string_decoder_proto = 0;
33
34enum { STRING_DECODER_NATIVE_TAG = 0x53444543u }; // SDEC
35
36static sd_state_t *sd_get_state(ant_value_t obj) {
37 return (sd_state_t *)js_get_native(obj, STRING_DECODER_NATIVE_TAG);
38}
39
40static void sd_finalize(ant_t *js, ant_object_t *obj) {
41 ant_value_t value = js_obj_from_ptr(obj);
42 sd_state_t *st = (sd_state_t *)js_get_native(value, STRING_DECODER_NATIVE_TAG);
43 if (st) { free(st->td); free(st); }
44 js_clear_native(value, STRING_DECODER_NATIVE_TAG);
45}
46
47static int sd_parse_encoding(const char *s, size_t len) {
48 static const struct { const char *label; uint8_t llen; int enc; } map[] = {
49 {"utf8", 4, SD_ENC_UTF8},
50 {"utf-8", 5, SD_ENC_UTF8},
51 {"utf16le", 7, SD_ENC_UTF16LE},
52 {"utf-16le", 8, SD_ENC_UTF16LE},
53 {"ucs2", 4, SD_ENC_UTF16LE},
54 {"ucs-2", 5, SD_ENC_UTF16LE},
55 {"latin1", 6, SD_ENC_LATIN1},
56 {"binary", 6, SD_ENC_LATIN1},
57 {"ascii", 5, SD_ENC_LATIN1},
58 {"hex", 3, SD_ENC_HEX},
59 {"base64", 6, SD_ENC_BASE64},
60 {"base64url", 9, SD_ENC_BASE64},
61 {NULL, 0, 0}
62 };
63 for (int i = 0; map[i].label; i++) {
64 if (len == map[i].llen && strncasecmp(s, map[i].label, len) == 0)
65 return map[i].enc;
66 }
67 return SD_ENC_UTF8;
68}
69
70static const char *sd_encoding_name(int enc) {
71switch (enc) {
72 case SD_ENC_UTF16LE: return "utf-16le";
73 case SD_ENC_LATIN1: return "latin1";
74 case SD_ENC_HEX: return "hex";
75 case SD_ENC_BASE64: return "base64";
76 default: return "utf-8";
77}}
78
79static ant_value_t sd_latin1_to_str(ant_t *js, const uint8_t *src, size_t len) {
80 char *out = malloc(len * 2 + 1);
81 if (!out) return js_mkerr(js, "out of memory");
82 size_t o = 0;
83
84 for (size_t i = 0; i < len; i++) {
85 uint8_t b = src[i];
86
87 if (b < 0x80) out[o++] = (char)b;
88 else {
89 out[o++] = (char)(0xC0 | (b >> 6));
90 out[o++] = (char)(0x80 | (b & 0x3F));
91 }}
92
93 ant_value_t result = js_mkstr(js, out, o);
94 free(out);
95
96 return result;
97}
98
99static ant_value_t sd_hex_decode(ant_t *js, const uint8_t *src, size_t len) {
100 static const char hex[] = "0123456789abcdef";
101 if (len == 0) return js_mkstr(js, "", 0);
102
103 char *out = malloc(len * 2 + 1);
104 if (!out) return js_mkerr(js, "out of memory");
105 for (size_t i = 0; i < len; i++) {
106 out[i*2] = hex[(src[i] >> 4) & 0xF];
107 out[i*2+1] = hex[src[i] & 0xF];
108 }
109
110 ant_value_t result = js_mkstr(js, out, len * 2);
111 free(out);
112
113 return result;
114}
115
116static ant_value_t sd_base64_write(ant_t *js, sd_state_t *st, const uint8_t *src, size_t len, bool flush) {
117 size_t total = (size_t)st->pending_len + len;
118 if (total == 0) return js_mkstr(js, "", 0);
119
120 uint8_t *work = malloc(total);
121 if (!work) return js_mkerr(js, "out of memory");
122
123 if (st->pending_len > 0)
124 memcpy(work, st->pending, (size_t)st->pending_len);
125 if (src && len > 0)
126 memcpy(work + st->pending_len, src, len);
127
128 size_t encode_len = flush ? total : (total / 3) * 3;
129 int new_pending = (int)(total - encode_len);
130
131 ant_value_t result;
132 if (encode_len == 0) result = js_mkstr(js, "", 0); else {
133 size_t out_len;
134 char *out = ant_base64_encode(work, encode_len, &out_len);
135 if (!out) { free(work); return js_mkerr(js, "out of memory"); }
136 result = js_mkstr(js, out, out_len);
137 free(out);
138 }
139
140 st->pending_len = new_pending;
141 if (new_pending > 0)
142 memcpy(st->pending, work + encode_len, (size_t)new_pending);
143
144 free(work);
145 return result;
146}
147
148static ant_value_t sd_do_write(ant_t *js, sd_state_t *st, const uint8_t *src, size_t len, bool flush) {
149switch (st->encoding) {
150 case SD_ENC_UTF8:
151 case SD_ENC_UTF16LE:
152 case SD_ENC_UTF16BE:
153 return td_decode(js, st->td, src, len, !flush);
154 case SD_ENC_LATIN1:
155 if (!src || len == 0) return js_mkstr(js, "", 0);
156 return sd_latin1_to_str(js, src, len);
157 case SD_ENC_HEX:
158 if (!src || len == 0) return js_mkstr(js, "", 0);
159 return sd_hex_decode(js, src, len);
160 case SD_ENC_BASE64:
161 return sd_base64_write(js, st, src, len, flush);
162 default:
163 return js_mkstr(js, "", 0);
164}}
165
166static ant_value_t js_sd_get_encoding(ant_t *js, ant_value_t *args, int nargs) {
167 sd_state_t *st = sd_get_state(js->this_val);
168 if (!st) return js_mkstr(js, "utf-8", 5);
169 const char *name = sd_encoding_name(st->encoding);
170 return js_mkstr(js, name, strlen(name));
171}
172
173static ant_value_t js_sd_write(ant_t *js, ant_value_t *args, int nargs) {
174 sd_state_t *st = sd_get_state(js->this_val);
175
176 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid StringDecoder");
177 if (nargs < 1) return js_mkstr(js, "", 0);
178
179 if (vtype(args[0]) == T_STR) {
180 size_t slen;
181 const char *s = js_getstr(js, args[0], &slen);
182 return s ? js_mkstr(js, s, slen) : js_mkstr(js, "", 0);
183 }
184
185 const uint8_t *src = NULL;
186 size_t len = 0;
187 if (is_object_type(args[0]))
188 buffer_source_get_bytes(js, args[0], &src, &len);
189
190 return sd_do_write(js, st, src, len, false);
191}
192
193static ant_value_t js_sd_end(ant_t *js, ant_value_t *args, int nargs) {
194 sd_state_t *st = sd_get_state(js->this_val);
195 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid StringDecoder");
196
197 const uint8_t *src = NULL;
198 size_t len = 0;
199 if (nargs > 0 && is_object_type(args[0]))
200 buffer_source_get_bytes(js, args[0], &src, &len);
201
202 return sd_do_write(js, st, src, len, true);
203}
204
205ant_value_t string_decoder_create(ant_t *js, ant_value_t encoding) {
206 int enc = SD_ENC_UTF8;
207 if (!is_undefined(encoding)) {
208 ant_value_t label_val = (vtype(encoding) == T_STR) ? encoding : coerce_to_str(js, encoding);
209 if (!is_err(label_val) && vtype(label_val) == T_STR) {
210 size_t llen;
211 const char *label = js_getstr(js, label_val, &llen);
212 if (label) enc = sd_parse_encoding(label, llen);
213 }}
214
215 sd_state_t *st = calloc(1, sizeof(sd_state_t));
216 if (!st) return js_mkerr(js, "out of memory");
217 st->encoding = enc;
218
219 if (enc == SD_ENC_UTF8 || enc == SD_ENC_UTF16LE || enc == SD_ENC_UTF16BE) {
220 td_encoding_t td_enc = (enc == SD_ENC_UTF16LE) ? TD_ENC_UTF16LE : (enc == SD_ENC_UTF16BE) ? TD_ENC_UTF16BE : TD_ENC_UTF8;
221 st->td = td_state_new(td_enc, false, false);
222 if (!st->td) { free(st); return js_mkerr(js, "out of memory"); }
223 }
224
225 ant_value_t obj = js_mkobj(js);
226 ant_value_t proto = js_instance_proto_from_new_target(js, g_string_decoder_proto);
227
228 if (is_object_type(proto)) js_set_proto_init(obj, proto);
229 js_set_native(obj, st, STRING_DECODER_NATIVE_TAG);
230 js_set_finalizer(obj, sd_finalize);
231
232 return obj;
233}
234
235ant_value_t string_decoder_decode_bytes(
236 ant_t *js, ant_value_t decoder,
237 const uint8_t *src, size_t len, bool flush
238) {
239 sd_state_t *st = sd_get_state(decoder);
240 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid StringDecoder");
241 return sd_do_write(js, st, src, len, flush);
242}
243
244ant_value_t string_decoder_decode_value(
245 ant_t *js, ant_value_t decoder,
246 ant_value_t chunk, bool flush
247) {
248 sd_state_t *st = sd_get_state(decoder);
249 if (!st) return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid StringDecoder");
250
251 if (vtype(chunk) == T_STR) {
252 size_t slen = 0;
253 const char *s = js_getstr(js, chunk, &slen);
254 return s ? js_mkstr(js, s, slen) : js_mkstr(js, "", 0);
255 }
256
257 const uint8_t *src = NULL;
258 size_t len = 0;
259 if (is_object_type(chunk))
260 buffer_source_get_bytes(js, chunk, &src, &len);
261
262 return sd_do_write(js, st, src, len, flush);
263}
264
265static ant_value_t js_sd_ctor(ant_t *js, ant_value_t *args, int nargs) {
266 if (vtype(js->new_target) == T_UNDEF)
267 return js_mkerr_typed(js, JS_ERR_TYPE, "StringDecoder constructor requires 'new'");
268
269 ant_value_t encoding = nargs > 0 ? args[0] : js_mkundef();
270 return string_decoder_create(js, encoding);
271}
272
273ant_value_t string_decoder_library(ant_t *js) {
274 g_string_decoder_proto = js_mkobj(js);
275
276 js_set_getter_desc(js, g_string_decoder_proto, "encoding", 8, js_mkfun(js_sd_get_encoding), JS_DESC_C);
277 js_set(js, g_string_decoder_proto, "write", js_mkfun(js_sd_write));
278 js_set(js, g_string_decoder_proto, "end", js_mkfun(js_sd_end));
279 js_set_sym(js, g_string_decoder_proto, get_toStringTag_sym(), js_mkstr(js, "StringDecoder", 13));
280
281 ant_value_t ctor = js_make_ctor(js, js_sd_ctor, g_string_decoder_proto, "StringDecoder", 13);
282 ant_value_t lib = js_mkobj(js);
283 js_set(js, lib, "StringDecoder", ctor);
284
285 return lib;
286}