crous 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +413 -0
- package/binding.gyp +50 -0
- package/index.d.ts +358 -0
- package/index.js +290 -0
- package/package.json +57 -0
- package/src/crous_node.c +819 -0
package/src/crous_node.c
ADDED
|
@@ -0,0 +1,819 @@
|
|
|
1
|
+
#include <node_api.h>
|
|
2
|
+
#include <string.h>
|
|
3
|
+
#include <stdlib.h>
|
|
4
|
+
#include <stdio.h>
|
|
5
|
+
#include "crous.h"
|
|
6
|
+
|
|
7
|
+
/* ============================================================================
|
|
8
|
+
ERROR HANDLING
|
|
9
|
+
============================================================================ */
|
|
10
|
+
|
|
11
|
+
static napi_value throw_crous_error(napi_env env, const char* msg) {
|
|
12
|
+
napi_throw_error(env, "CrousError", msg);
|
|
13
|
+
return NULL;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static napi_value throw_encode_error(napi_env env, const char* msg) {
|
|
17
|
+
napi_throw_error(env, "CrousEncodeError", msg);
|
|
18
|
+
return NULL;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static napi_value throw_decode_error(napi_env env, const char* msg) {
|
|
22
|
+
napi_throw_error(env, "CrousDecodeError", msg);
|
|
23
|
+
return NULL;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ============================================================================
|
|
27
|
+
CUSTOM SERIALIZER/DECODER REGISTRIES
|
|
28
|
+
============================================================================ */
|
|
29
|
+
|
|
30
|
+
/* Global references for custom serializers and decoders */
|
|
31
|
+
static napi_ref custom_serializers_ref = NULL;
|
|
32
|
+
static napi_ref custom_decoders_ref = NULL;
|
|
33
|
+
static napi_ref type_to_tag_ref = NULL;
|
|
34
|
+
static uint32_t next_custom_tag = 100;
|
|
35
|
+
|
|
36
|
+
/* ============================================================================
|
|
37
|
+
CONVERSION: NODE.JS VALUE -> CROUS VALUE
|
|
38
|
+
============================================================================ */
|
|
39
|
+
|
|
40
|
+
static crous_value* napi_to_crous(napi_env env, napi_value value, napi_value default_func, crous_err_t *err);
|
|
41
|
+
|
|
42
|
+
static crous_value* try_custom_serializer(napi_env env, napi_value obj, napi_value default_func,
|
|
43
|
+
crous_err_t *err, int *handled) {
|
|
44
|
+
*handled = 0;
|
|
45
|
+
*err = CROUS_OK;
|
|
46
|
+
|
|
47
|
+
// Get custom serializers map
|
|
48
|
+
napi_value serializers;
|
|
49
|
+
if (custom_serializers_ref == NULL) {
|
|
50
|
+
return NULL;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
napi_status status = napi_get_reference_value(env, custom_serializers_ref, &serializers);
|
|
54
|
+
if (status != napi_ok || serializers == NULL) {
|
|
55
|
+
return NULL;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Get constructor of object
|
|
59
|
+
napi_value constructor;
|
|
60
|
+
status = napi_get_named_property(env, obj, "constructor", &constructor);
|
|
61
|
+
if (status != napi_ok) {
|
|
62
|
+
return NULL;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Look up serializer
|
|
66
|
+
napi_value serializer;
|
|
67
|
+
bool has_property;
|
|
68
|
+
status = napi_has_property(env, serializers, constructor, &has_property);
|
|
69
|
+
if (status != napi_ok || !has_property) {
|
|
70
|
+
// Try default_func if provided
|
|
71
|
+
if (default_func != NULL) {
|
|
72
|
+
napi_valuetype type;
|
|
73
|
+
napi_typeof(env, default_func, &type);
|
|
74
|
+
if (type == napi_function) {
|
|
75
|
+
serializer = default_func;
|
|
76
|
+
*handled = 1;
|
|
77
|
+
} else {
|
|
78
|
+
return NULL;
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
return NULL;
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
status = napi_get_property(env, serializers, constructor, &serializer);
|
|
85
|
+
if (status != napi_ok) {
|
|
86
|
+
return NULL;
|
|
87
|
+
}
|
|
88
|
+
*handled = 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Call serializer
|
|
92
|
+
napi_value global;
|
|
93
|
+
napi_get_global(env, &global);
|
|
94
|
+
napi_value result;
|
|
95
|
+
status = napi_call_function(env, global, serializer, 1, &obj, &result);
|
|
96
|
+
if (status != napi_ok) {
|
|
97
|
+
*err = CROUS_ERR_ENCODE;
|
|
98
|
+
return NULL;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Get tag for this type (default 100)
|
|
102
|
+
uint32_t tag = 100;
|
|
103
|
+
if (type_to_tag_ref != NULL) {
|
|
104
|
+
napi_value tag_map;
|
|
105
|
+
status = napi_get_reference_value(env, type_to_tag_ref, &tag_map);
|
|
106
|
+
if (status == napi_ok) {
|
|
107
|
+
napi_value tag_value;
|
|
108
|
+
bool has_tag;
|
|
109
|
+
status = napi_has_property(env, tag_map, constructor, &has_tag);
|
|
110
|
+
if (status == napi_ok && has_tag) {
|
|
111
|
+
status = napi_get_property(env, tag_map, constructor, &tag_value);
|
|
112
|
+
if (status == napi_ok) {
|
|
113
|
+
napi_get_value_uint32(env, tag_value, &tag);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Convert result to crous_value
|
|
120
|
+
crous_value *inner = napi_to_crous(env, result, NULL, err);
|
|
121
|
+
if (*err != CROUS_OK || inner == NULL) {
|
|
122
|
+
return NULL;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Wrap in tagged value
|
|
126
|
+
crous_value *tagged = crous_value_new_tagged(tag, inner);
|
|
127
|
+
if (!tagged) {
|
|
128
|
+
crous_value_free_tree(inner);
|
|
129
|
+
*err = CROUS_ERR_OOM;
|
|
130
|
+
return NULL;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return tagged;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static crous_value* napi_to_crous(napi_env env, napi_value value, napi_value default_func, crous_err_t *err) {
|
|
137
|
+
*err = CROUS_OK;
|
|
138
|
+
napi_status status;
|
|
139
|
+
napi_valuetype type;
|
|
140
|
+
|
|
141
|
+
status = napi_typeof(env, value, &type);
|
|
142
|
+
if (status != napi_ok) {
|
|
143
|
+
*err = CROUS_ERR_ENCODE;
|
|
144
|
+
return NULL;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
switch (type) {
|
|
148
|
+
case napi_undefined:
|
|
149
|
+
case napi_null:
|
|
150
|
+
return crous_value_new_null();
|
|
151
|
+
|
|
152
|
+
case napi_boolean: {
|
|
153
|
+
bool val;
|
|
154
|
+
status = napi_get_value_bool(env, value, &val);
|
|
155
|
+
if (status != napi_ok) {
|
|
156
|
+
*err = CROUS_ERR_ENCODE;
|
|
157
|
+
return NULL;
|
|
158
|
+
}
|
|
159
|
+
return crous_value_new_bool(val ? 1 : 0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
case napi_number: {
|
|
163
|
+
double num;
|
|
164
|
+
status = napi_get_value_double(env, value, &num);
|
|
165
|
+
if (status != napi_ok) {
|
|
166
|
+
*err = CROUS_ERR_ENCODE;
|
|
167
|
+
return NULL;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check if it's an integer
|
|
171
|
+
if (num == (int64_t)num && num >= -9223372036854775807LL && num <= 9223372036854775807LL) {
|
|
172
|
+
return crous_value_new_int((int64_t)num);
|
|
173
|
+
} else {
|
|
174
|
+
return crous_value_new_float(num);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case napi_string: {
|
|
179
|
+
size_t str_len;
|
|
180
|
+
status = napi_get_value_string_utf8(env, value, NULL, 0, &str_len);
|
|
181
|
+
if (status != napi_ok) {
|
|
182
|
+
*err = CROUS_ERR_ENCODE;
|
|
183
|
+
return NULL;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
char *buf = (char*)malloc(str_len + 1);
|
|
187
|
+
if (!buf) {
|
|
188
|
+
*err = CROUS_ERR_OOM;
|
|
189
|
+
return NULL;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
size_t copied;
|
|
193
|
+
status = napi_get_value_string_utf8(env, value, buf, str_len + 1, &copied);
|
|
194
|
+
if (status != napi_ok) {
|
|
195
|
+
free(buf);
|
|
196
|
+
*err = CROUS_ERR_ENCODE;
|
|
197
|
+
return NULL;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
crous_value *result = crous_value_new_string(buf, copied);
|
|
201
|
+
free(buf);
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
case napi_object: {
|
|
206
|
+
// Check if it's a Buffer
|
|
207
|
+
bool is_buffer;
|
|
208
|
+
status = napi_is_buffer(env, value, &is_buffer);
|
|
209
|
+
if (status == napi_ok && is_buffer) {
|
|
210
|
+
void *data;
|
|
211
|
+
size_t length;
|
|
212
|
+
status = napi_get_buffer_info(env, value, &data, &length);
|
|
213
|
+
if (status != napi_ok) {
|
|
214
|
+
*err = CROUS_ERR_ENCODE;
|
|
215
|
+
return NULL;
|
|
216
|
+
}
|
|
217
|
+
return crous_value_new_bytes((const uint8_t*)data, length);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Check if it's an Array
|
|
221
|
+
bool is_array;
|
|
222
|
+
status = napi_is_array(env, value, &is_array);
|
|
223
|
+
if (status == napi_ok && is_array) {
|
|
224
|
+
uint32_t length;
|
|
225
|
+
status = napi_get_array_length(env, value, &length);
|
|
226
|
+
if (status != napi_ok) {
|
|
227
|
+
*err = CROUS_ERR_ENCODE;
|
|
228
|
+
return NULL;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
crous_value *list = crous_value_new_list((size_t)length);
|
|
232
|
+
if (!list) {
|
|
233
|
+
*err = CROUS_ERR_OOM;
|
|
234
|
+
return NULL;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for (uint32_t i = 0; i < length; i++) {
|
|
238
|
+
napi_value element;
|
|
239
|
+
status = napi_get_element(env, value, i, &element);
|
|
240
|
+
if (status != napi_ok) {
|
|
241
|
+
crous_value_free_tree(list);
|
|
242
|
+
*err = CROUS_ERR_ENCODE;
|
|
243
|
+
return NULL;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
crous_value *item = napi_to_crous(env, element, default_func, err);
|
|
247
|
+
if (*err != CROUS_OK) {
|
|
248
|
+
crous_value_free_tree(list);
|
|
249
|
+
return NULL;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (crous_value_list_append(list, item) != CROUS_OK) {
|
|
253
|
+
crous_value_free_tree(list);
|
|
254
|
+
crous_value_free_tree(item);
|
|
255
|
+
*err = CROUS_ERR_OOM;
|
|
256
|
+
return NULL;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return list;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Check if it's a Set
|
|
264
|
+
napi_value set_constructor;
|
|
265
|
+
napi_value global;
|
|
266
|
+
napi_get_global(env, &global);
|
|
267
|
+
napi_get_named_property(env, global, "Set", &set_constructor);
|
|
268
|
+
|
|
269
|
+
bool is_set = false;
|
|
270
|
+
napi_instanceof(env, value, set_constructor, &is_set);
|
|
271
|
+
|
|
272
|
+
if (is_set) {
|
|
273
|
+
int handled = 0;
|
|
274
|
+
crous_value *result = try_custom_serializer(env, value, default_func, err, &handled);
|
|
275
|
+
if (handled) return result;
|
|
276
|
+
|
|
277
|
+
// Convert Set to Array
|
|
278
|
+
napi_value array_from;
|
|
279
|
+
napi_value array_constructor;
|
|
280
|
+
napi_get_named_property(env, global, "Array", &array_constructor);
|
|
281
|
+
napi_get_named_property(env, array_constructor, "from", &array_from);
|
|
282
|
+
|
|
283
|
+
napi_value as_array;
|
|
284
|
+
napi_call_function(env, global, array_from, 1, &value, &as_array);
|
|
285
|
+
|
|
286
|
+
crous_value *list_val = napi_to_crous(env, as_array, default_func, err);
|
|
287
|
+
if (*err != CROUS_OK) return NULL;
|
|
288
|
+
|
|
289
|
+
crous_value *tagged = crous_value_new_tagged(90, list_val);
|
|
290
|
+
if (!tagged) {
|
|
291
|
+
crous_value_free_tree(list_val);
|
|
292
|
+
*err = CROUS_ERR_OOM;
|
|
293
|
+
return NULL;
|
|
294
|
+
}
|
|
295
|
+
return tagged;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Regular object (dictionary)
|
|
299
|
+
napi_value property_names;
|
|
300
|
+
status = napi_get_property_names(env, value, &property_names);
|
|
301
|
+
if (status != napi_ok) {
|
|
302
|
+
*err = CROUS_ERR_ENCODE;
|
|
303
|
+
return NULL;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
uint32_t prop_count;
|
|
307
|
+
status = napi_get_array_length(env, property_names, &prop_count);
|
|
308
|
+
if (status != napi_ok) {
|
|
309
|
+
*err = CROUS_ERR_ENCODE;
|
|
310
|
+
return NULL;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
crous_value *dict = crous_value_new_dict((size_t)prop_count);
|
|
314
|
+
if (!dict) {
|
|
315
|
+
*err = CROUS_ERR_OOM;
|
|
316
|
+
return NULL;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
for (uint32_t i = 0; i < prop_count; i++) {
|
|
320
|
+
napi_value prop_name;
|
|
321
|
+
status = napi_get_element(env, property_names, i, &prop_name);
|
|
322
|
+
if (status != napi_ok) {
|
|
323
|
+
crous_value_free_tree(dict);
|
|
324
|
+
*err = CROUS_ERR_ENCODE;
|
|
325
|
+
return NULL;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
size_t key_len;
|
|
329
|
+
status = napi_get_value_string_utf8(env, prop_name, NULL, 0, &key_len);
|
|
330
|
+
if (status != napi_ok) {
|
|
331
|
+
crous_value_free_tree(dict);
|
|
332
|
+
*err = CROUS_ERR_ENCODE;
|
|
333
|
+
return NULL;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
char *key_buf = (char*)malloc(key_len + 1);
|
|
337
|
+
if (!key_buf) {
|
|
338
|
+
crous_value_free_tree(dict);
|
|
339
|
+
*err = CROUS_ERR_OOM;
|
|
340
|
+
return NULL;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
size_t copied;
|
|
344
|
+
status = napi_get_value_string_utf8(env, prop_name, key_buf, key_len + 1, &copied);
|
|
345
|
+
if (status != napi_ok) {
|
|
346
|
+
free(key_buf);
|
|
347
|
+
crous_value_free_tree(dict);
|
|
348
|
+
*err = CROUS_ERR_ENCODE;
|
|
349
|
+
return NULL;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
napi_value prop_value;
|
|
353
|
+
status = napi_get_property(env, value, prop_name, &prop_value);
|
|
354
|
+
if (status != napi_ok) {
|
|
355
|
+
free(key_buf);
|
|
356
|
+
crous_value_free_tree(dict);
|
|
357
|
+
*err = CROUS_ERR_ENCODE;
|
|
358
|
+
return NULL;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
crous_value *cval = napi_to_crous(env, prop_value, default_func, err);
|
|
362
|
+
if (*err != CROUS_OK) {
|
|
363
|
+
free(key_buf);
|
|
364
|
+
crous_value_free_tree(dict);
|
|
365
|
+
return NULL;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (crous_value_dict_set_binary(dict, key_buf, copied, cval) != CROUS_OK) {
|
|
369
|
+
free(key_buf);
|
|
370
|
+
crous_value_free_tree(dict);
|
|
371
|
+
crous_value_free_tree(cval);
|
|
372
|
+
*err = CROUS_ERR_OOM;
|
|
373
|
+
return NULL;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
free(key_buf);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return dict;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
default: {
|
|
383
|
+
// Try custom serializer
|
|
384
|
+
int handled = 0;
|
|
385
|
+
crous_value *result = try_custom_serializer(env, value, default_func, err, &handled);
|
|
386
|
+
if (handled) return result;
|
|
387
|
+
|
|
388
|
+
*err = CROUS_ERR_INVALID_TYPE;
|
|
389
|
+
return NULL;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* ============================================================================
|
|
395
|
+
CONVERSION: CROUS VALUE -> NODE.JS VALUE
|
|
396
|
+
============================================================================ */
|
|
397
|
+
|
|
398
|
+
static napi_value crous_to_napi(napi_env env, const crous_value *v, napi_value object_hook) {
|
|
399
|
+
if (!v) {
|
|
400
|
+
napi_value result;
|
|
401
|
+
napi_get_null(env, &result);
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
napi_status status;
|
|
406
|
+
napi_value result;
|
|
407
|
+
|
|
408
|
+
switch (crous_value_get_type(v)) {
|
|
409
|
+
case CROUS_TYPE_NULL:
|
|
410
|
+
napi_get_null(env, &result);
|
|
411
|
+
return result;
|
|
412
|
+
|
|
413
|
+
case CROUS_TYPE_BOOL:
|
|
414
|
+
napi_get_boolean(env, crous_value_get_bool(v), &result);
|
|
415
|
+
return result;
|
|
416
|
+
|
|
417
|
+
case CROUS_TYPE_INT:
|
|
418
|
+
napi_create_int64(env, crous_value_get_int(v), &result);
|
|
419
|
+
return result;
|
|
420
|
+
|
|
421
|
+
case CROUS_TYPE_FLOAT:
|
|
422
|
+
napi_create_double(env, crous_value_get_float(v), &result);
|
|
423
|
+
return result;
|
|
424
|
+
|
|
425
|
+
case CROUS_TYPE_STRING: {
|
|
426
|
+
size_t len;
|
|
427
|
+
const char *data = crous_value_get_string(v, &len);
|
|
428
|
+
napi_create_string_utf8(env, data, len, &result);
|
|
429
|
+
return result;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
case CROUS_TYPE_BYTES: {
|
|
433
|
+
size_t len;
|
|
434
|
+
const uint8_t *data = crous_value_get_bytes(v, &len);
|
|
435
|
+
void *buffer_data;
|
|
436
|
+
status = napi_create_buffer_copy(env, len, data, &buffer_data, &result);
|
|
437
|
+
if (status != napi_ok) {
|
|
438
|
+
napi_get_null(env, &result);
|
|
439
|
+
}
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
case CROUS_TYPE_LIST: {
|
|
444
|
+
size_t size = crous_value_list_size(v);
|
|
445
|
+
napi_create_array_with_length(env, size, &result);
|
|
446
|
+
|
|
447
|
+
for (size_t i = 0; i < size; i++) {
|
|
448
|
+
napi_value element = crous_to_napi(env, crous_value_list_get(v, i), object_hook);
|
|
449
|
+
napi_set_element(env, result, (uint32_t)i, element);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return result;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
case CROUS_TYPE_TUPLE: {
|
|
456
|
+
// Tuples are represented as arrays in JavaScript
|
|
457
|
+
size_t size = crous_value_list_size(v);
|
|
458
|
+
napi_create_array_with_length(env, size, &result);
|
|
459
|
+
|
|
460
|
+
for (size_t i = 0; i < size; i++) {
|
|
461
|
+
napi_value element = crous_to_napi(env, crous_value_list_get(v, i), object_hook);
|
|
462
|
+
napi_set_element(env, result, (uint32_t)i, element);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
return result;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
case CROUS_TYPE_DICT: {
|
|
469
|
+
napi_create_object(env, &result);
|
|
470
|
+
|
|
471
|
+
size_t size = crous_value_dict_size(v);
|
|
472
|
+
for (size_t i = 0; i < size; i++) {
|
|
473
|
+
const crous_dict_entry *entry = crous_value_dict_get_entry(v, i);
|
|
474
|
+
if (!entry) continue;
|
|
475
|
+
|
|
476
|
+
napi_value key;
|
|
477
|
+
napi_create_string_utf8(env, entry->key, entry->key_len, &key);
|
|
478
|
+
|
|
479
|
+
napi_value val = crous_to_napi(env, entry->value, object_hook);
|
|
480
|
+
napi_set_property(env, result, key, val);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Apply object_hook if provided
|
|
484
|
+
if (object_hook != NULL) {
|
|
485
|
+
napi_valuetype hook_type;
|
|
486
|
+
napi_typeof(env, object_hook, &hook_type);
|
|
487
|
+
if (hook_type == napi_function) {
|
|
488
|
+
napi_value global;
|
|
489
|
+
napi_get_global(env, &global);
|
|
490
|
+
napi_value hooked_result;
|
|
491
|
+
status = napi_call_function(env, global, object_hook, 1, &result, &hooked_result);
|
|
492
|
+
if (status == napi_ok) {
|
|
493
|
+
return hooked_result;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return result;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
case CROUS_TYPE_TAGGED: {
|
|
502
|
+
uint32_t tag = crous_value_get_tag(v);
|
|
503
|
+
const crous_value *inner = crous_value_get_tagged_inner(v);
|
|
504
|
+
|
|
505
|
+
// Check for built-in tags
|
|
506
|
+
if (tag == 90) {
|
|
507
|
+
// Set
|
|
508
|
+
napi_value array = crous_to_napi(env, inner, object_hook);
|
|
509
|
+
napi_value global;
|
|
510
|
+
napi_get_global(env, &global);
|
|
511
|
+
napi_value set_constructor;
|
|
512
|
+
napi_get_named_property(env, global, "Set", &set_constructor);
|
|
513
|
+
napi_new_instance(env, set_constructor, 1, &array, &result);
|
|
514
|
+
return result;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Check for custom decoder
|
|
518
|
+
if (custom_decoders_ref != NULL) {
|
|
519
|
+
napi_value decoders;
|
|
520
|
+
status = napi_get_reference_value(env, custom_decoders_ref, &decoders);
|
|
521
|
+
if (status == napi_ok) {
|
|
522
|
+
napi_value tag_key;
|
|
523
|
+
napi_create_uint32(env, tag, &tag_key);
|
|
524
|
+
|
|
525
|
+
bool has_decoder;
|
|
526
|
+
status = napi_has_property(env, decoders, tag_key, &has_decoder);
|
|
527
|
+
if (status == napi_ok && has_decoder) {
|
|
528
|
+
napi_value decoder;
|
|
529
|
+
status = napi_get_property(env, decoders, tag_key, &decoder);
|
|
530
|
+
if (status == napi_ok) {
|
|
531
|
+
napi_value inner_js = crous_to_napi(env, inner, object_hook);
|
|
532
|
+
napi_value global;
|
|
533
|
+
napi_get_global(env, &global);
|
|
534
|
+
status = napi_call_function(env, global, decoder, 1, &inner_js, &result);
|
|
535
|
+
if (status == napi_ok) {
|
|
536
|
+
return result;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// No decoder found, return inner value
|
|
544
|
+
return crous_to_napi(env, inner, object_hook);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
default:
|
|
548
|
+
napi_get_null(env, &result);
|
|
549
|
+
return result;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* ============================================================================
|
|
554
|
+
EXPORTED FUNCTIONS
|
|
555
|
+
============================================================================ */
|
|
556
|
+
|
|
557
|
+
static napi_value dumps(napi_env env, napi_callback_info info) {
|
|
558
|
+
size_t argc = 2;
|
|
559
|
+
napi_value args[2];
|
|
560
|
+
napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
561
|
+
|
|
562
|
+
if (status != napi_ok || argc < 1) {
|
|
563
|
+
napi_throw_type_error(env, NULL, "Wrong number of arguments");
|
|
564
|
+
return NULL;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
napi_value default_func = (argc > 1) ? args[1] : NULL;
|
|
568
|
+
|
|
569
|
+
crous_err_t err;
|
|
570
|
+
crous_value *value = napi_to_crous(env, args[0], default_func, &err);
|
|
571
|
+
if (!value || err != CROUS_OK) {
|
|
572
|
+
return throw_encode_error(env, crous_err_str(err));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
uint8_t *buf = NULL;
|
|
576
|
+
size_t size = 0;
|
|
577
|
+
err = crous_encode(value, &buf, &size);
|
|
578
|
+
crous_value_free_tree(value);
|
|
579
|
+
|
|
580
|
+
if (err != CROUS_OK) {
|
|
581
|
+
free(buf);
|
|
582
|
+
return throw_encode_error(env, crous_err_str(err));
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
napi_value result;
|
|
586
|
+
void *result_data;
|
|
587
|
+
status = napi_create_buffer_copy(env, size, buf, &result_data, &result);
|
|
588
|
+
free(buf);
|
|
589
|
+
|
|
590
|
+
if (status != napi_ok) {
|
|
591
|
+
return throw_encode_error(env, "Failed to create buffer");
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
static napi_value loads(napi_env env, napi_callback_info info) {
|
|
598
|
+
size_t argc = 2;
|
|
599
|
+
napi_value args[2];
|
|
600
|
+
napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
601
|
+
|
|
602
|
+
if (status != napi_ok || argc < 1) {
|
|
603
|
+
napi_throw_type_error(env, NULL, "Wrong number of arguments");
|
|
604
|
+
return NULL;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
bool is_buffer;
|
|
608
|
+
status = napi_is_buffer(env, args[0], &is_buffer);
|
|
609
|
+
if (status != napi_ok || !is_buffer) {
|
|
610
|
+
napi_throw_type_error(env, NULL, "First argument must be a Buffer");
|
|
611
|
+
return NULL;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
void *data;
|
|
615
|
+
size_t length;
|
|
616
|
+
status = napi_get_buffer_info(env, args[0], &data, &length);
|
|
617
|
+
if (status != napi_ok) {
|
|
618
|
+
return throw_decode_error(env, "Failed to get buffer data");
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
napi_value object_hook = (argc > 1) ? args[1] : NULL;
|
|
622
|
+
|
|
623
|
+
crous_value *value = NULL;
|
|
624
|
+
crous_err_t err = crous_decode((const uint8_t*)data, length, &value);
|
|
625
|
+
|
|
626
|
+
if (err != CROUS_OK) {
|
|
627
|
+
if (value) crous_value_free_tree(value);
|
|
628
|
+
return throw_decode_error(env, crous_err_str(err));
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
napi_value result = crous_to_napi(env, value, object_hook);
|
|
632
|
+
crous_value_free_tree(value);
|
|
633
|
+
|
|
634
|
+
return result;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
static napi_value register_serializer(napi_env env, napi_callback_info info) {
|
|
638
|
+
size_t argc = 2;
|
|
639
|
+
napi_value args[2];
|
|
640
|
+
napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
641
|
+
|
|
642
|
+
if (status != napi_ok || argc < 2) {
|
|
643
|
+
napi_throw_type_error(env, NULL, "Expected 2 arguments: type and function");
|
|
644
|
+
return NULL;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
napi_valuetype arg1_type;
|
|
648
|
+
napi_typeof(env, args[1], &arg1_type);
|
|
649
|
+
if (arg1_type != napi_function) {
|
|
650
|
+
napi_throw_type_error(env, NULL, "Second argument must be a function");
|
|
651
|
+
return NULL;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Initialize serializers map if needed
|
|
655
|
+
if (custom_serializers_ref == NULL) {
|
|
656
|
+
napi_value serializers;
|
|
657
|
+
napi_create_object(env, &serializers);
|
|
658
|
+
napi_create_reference(env, serializers, 1, &custom_serializers_ref);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
napi_value serializers;
|
|
662
|
+
napi_get_reference_value(env, custom_serializers_ref, &serializers);
|
|
663
|
+
napi_set_property(env, serializers, args[0], args[1]);
|
|
664
|
+
|
|
665
|
+
// Initialize type_to_tag if needed
|
|
666
|
+
if (type_to_tag_ref == NULL) {
|
|
667
|
+
napi_value tag_map;
|
|
668
|
+
napi_create_object(env, &tag_map);
|
|
669
|
+
napi_create_reference(env, tag_map, 1, &type_to_tag_ref);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Assign tag
|
|
673
|
+
napi_value tag_map;
|
|
674
|
+
napi_get_reference_value(env, type_to_tag_ref, &tag_map);
|
|
675
|
+
napi_value tag_value;
|
|
676
|
+
napi_create_uint32(env, next_custom_tag++, &tag_value);
|
|
677
|
+
napi_set_property(env, tag_map, args[0], tag_value);
|
|
678
|
+
|
|
679
|
+
napi_value result;
|
|
680
|
+
napi_get_undefined(env, &result);
|
|
681
|
+
return result;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
static napi_value unregister_serializer(napi_env env, napi_callback_info info) {
|
|
685
|
+
size_t argc = 1;
|
|
686
|
+
napi_value args[1];
|
|
687
|
+
napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
688
|
+
|
|
689
|
+
if (status != napi_ok || argc < 1) {
|
|
690
|
+
napi_throw_type_error(env, NULL, "Expected 1 argument");
|
|
691
|
+
return NULL;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
if (custom_serializers_ref != NULL) {
|
|
695
|
+
napi_value serializers;
|
|
696
|
+
napi_get_reference_value(env, custom_serializers_ref, &serializers);
|
|
697
|
+
bool deleted;
|
|
698
|
+
napi_delete_property(env, serializers, args[0], &deleted);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
if (type_to_tag_ref != NULL) {
|
|
702
|
+
napi_value tag_map;
|
|
703
|
+
napi_get_reference_value(env, type_to_tag_ref, &tag_map);
|
|
704
|
+
bool deleted;
|
|
705
|
+
napi_delete_property(env, tag_map, args[0], &deleted);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
napi_value result;
|
|
709
|
+
napi_get_undefined(env, &result);
|
|
710
|
+
return result;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
static napi_value register_decoder(napi_env env, napi_callback_info info) {
|
|
714
|
+
size_t argc = 2;
|
|
715
|
+
napi_value args[2];
|
|
716
|
+
napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
717
|
+
|
|
718
|
+
if (status != napi_ok || argc < 2) {
|
|
719
|
+
napi_throw_type_error(env, NULL, "Expected 2 arguments: tag and function");
|
|
720
|
+
return NULL;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
napi_valuetype arg1_type;
|
|
724
|
+
napi_typeof(env, args[1], &arg1_type);
|
|
725
|
+
if (arg1_type != napi_function) {
|
|
726
|
+
napi_throw_type_error(env, NULL, "Second argument must be a function");
|
|
727
|
+
return NULL;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Initialize decoders map if needed
|
|
731
|
+
if (custom_decoders_ref == NULL) {
|
|
732
|
+
napi_value decoders;
|
|
733
|
+
napi_create_object(env, &decoders);
|
|
734
|
+
napi_create_reference(env, decoders, 1, &custom_decoders_ref);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
napi_value decoders;
|
|
738
|
+
napi_get_reference_value(env, custom_decoders_ref, &decoders);
|
|
739
|
+
napi_set_property(env, decoders, args[0], args[1]);
|
|
740
|
+
|
|
741
|
+
napi_value result;
|
|
742
|
+
napi_get_undefined(env, &result);
|
|
743
|
+
return result;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
static napi_value unregister_decoder(napi_env env, napi_callback_info info) {
|
|
747
|
+
size_t argc = 1;
|
|
748
|
+
napi_value args[1];
|
|
749
|
+
napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
|
|
750
|
+
|
|
751
|
+
if (status != napi_ok || argc < 1) {
|
|
752
|
+
napi_throw_type_error(env, NULL, "Expected 1 argument");
|
|
753
|
+
return NULL;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (custom_decoders_ref != NULL) {
|
|
757
|
+
napi_value decoders;
|
|
758
|
+
napi_get_reference_value(env, custom_decoders_ref, &decoders);
|
|
759
|
+
bool deleted;
|
|
760
|
+
napi_delete_property(env, decoders, args[0], &deleted);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
napi_value result;
|
|
764
|
+
napi_get_undefined(env, &result);
|
|
765
|
+
return result;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/* ============================================================================
|
|
769
|
+
MODULE INITIALIZATION
|
|
770
|
+
============================================================================ */
|
|
771
|
+
|
|
772
|
+
static napi_value Init(napi_env env, napi_value exports) {
|
|
773
|
+
napi_status status;
|
|
774
|
+
napi_value fn;
|
|
775
|
+
|
|
776
|
+
// Create dumps function
|
|
777
|
+
status = napi_create_function(env, "dumps", NAPI_AUTO_LENGTH, dumps, NULL, &fn);
|
|
778
|
+
if (status != napi_ok) return NULL;
|
|
779
|
+
status = napi_set_named_property(env, exports, "dumps", fn);
|
|
780
|
+
if (status != napi_ok) return NULL;
|
|
781
|
+
|
|
782
|
+
// Create loads function
|
|
783
|
+
status = napi_create_function(env, "loads", NAPI_AUTO_LENGTH, loads, NULL, &fn);
|
|
784
|
+
if (status != napi_ok) return NULL;
|
|
785
|
+
status = napi_set_named_property(env, exports, "loads", fn);
|
|
786
|
+
if (status != napi_ok) return NULL;
|
|
787
|
+
|
|
788
|
+
// Create register_serializer function
|
|
789
|
+
status = napi_create_function(env, "register_serializer", NAPI_AUTO_LENGTH,
|
|
790
|
+
register_serializer, NULL, &fn);
|
|
791
|
+
if (status != napi_ok) return NULL;
|
|
792
|
+
status = napi_set_named_property(env, exports, "registerSerializer", fn);
|
|
793
|
+
if (status != napi_ok) return NULL;
|
|
794
|
+
|
|
795
|
+
// Create unregister_serializer function
|
|
796
|
+
status = napi_create_function(env, "unregister_serializer", NAPI_AUTO_LENGTH,
|
|
797
|
+
unregister_serializer, NULL, &fn);
|
|
798
|
+
if (status != napi_ok) return NULL;
|
|
799
|
+
status = napi_set_named_property(env, exports, "unregisterSerializer", fn);
|
|
800
|
+
if (status != napi_ok) return NULL;
|
|
801
|
+
|
|
802
|
+
// Create register_decoder function
|
|
803
|
+
status = napi_create_function(env, "register_decoder", NAPI_AUTO_LENGTH,
|
|
804
|
+
register_decoder, NULL, &fn);
|
|
805
|
+
if (status != napi_ok) return NULL;
|
|
806
|
+
status = napi_set_named_property(env, exports, "registerDecoder", fn);
|
|
807
|
+
if (status != napi_ok) return NULL;
|
|
808
|
+
|
|
809
|
+
// Create unregister_decoder function
|
|
810
|
+
status = napi_create_function(env, "unregister_decoder", NAPI_AUTO_LENGTH,
|
|
811
|
+
unregister_decoder, NULL, &fn);
|
|
812
|
+
if (status != napi_ok) return NULL;
|
|
813
|
+
status = napi_set_named_property(env, exports, "unregisterDecoder", fn);
|
|
814
|
+
if (status != napi_ok) return NULL;
|
|
815
|
+
|
|
816
|
+
return exports;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
|