tranfi 0.1.2 → 0.2.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.
Files changed (132) hide show
  1. package/LICENSE +177 -0
  2. package/NOTICE +8 -0
  3. package/README.md +272 -40
  4. package/app/assets/{index-pDFMluyz.js → index-BIAIKnrp.js} +1 -1
  5. package/app/index.html +1 -1
  6. package/binding.gyp +55 -3
  7. package/csrc/arena.c +7 -5
  8. package/csrc/batch.c +818 -71
  9. package/csrc/buffer.c +84 -8
  10. package/csrc/cJSON.c +262 -19
  11. package/csrc/cJSON.h +17 -1
  12. package/csrc/codec_csv.c +1074 -181
  13. package/csrc/codec_jsonl.c +830 -118
  14. package/csrc/codec_table.c +108 -78
  15. package/csrc/codec_text.c +286 -68
  16. package/csrc/compiler.c +31 -3
  17. package/csrc/config.h +21 -0
  18. package/csrc/dsl.c +4722 -485
  19. package/csrc/expr.c +363 -55
  20. package/csrc/expr.h +2 -0
  21. package/csrc/internal.h +316 -27
  22. package/csrc/ir.c +65 -18
  23. package/csrc/ir.h +41 -0
  24. package/csrc/ir_schema.c +20 -5
  25. package/csrc/ir_serialize.c +68 -6
  26. package/csrc/ir_sql.c +796 -185
  27. package/csrc/ir_validate.c +462 -6
  28. package/csrc/json_path.c +210 -0
  29. package/csrc/main.c +879 -30
  30. package/csrc/memory_estimate.c +477 -0
  31. package/csrc/op_acf.c +171 -21
  32. package/csrc/op_across.c +477 -0
  33. package/csrc/op_anomaly.c +167 -32
  34. package/csrc/op_assert.c +761 -0
  35. package/csrc/op_bin.c +168 -29
  36. package/csrc/op_cast.c +383 -55
  37. package/csrc/op_clip.c +30 -19
  38. package/csrc/op_date_trunc.c +208 -34
  39. package/csrc/op_datetime.c +259 -77
  40. package/csrc/op_derive.c +65 -97
  41. package/csrc/op_diff.c +146 -30
  42. package/csrc/op_ewma.c +149 -30
  43. package/csrc/op_explode.c +124 -26
  44. package/csrc/op_fill_down.c +125 -53
  45. package/csrc/op_fill_null.c +176 -31
  46. package/csrc/op_filter.c +89 -40
  47. package/csrc/op_frequency.c +571 -43
  48. package/csrc/op_grep.c +36 -18
  49. package/csrc/op_group_agg.c +1790 -119
  50. package/csrc/op_hash.c +48 -15
  51. package/csrc/op_head.c +21 -86
  52. package/csrc/op_interpolate.c +268 -62
  53. package/csrc/op_join.c +2700 -182
  54. package/csrc/op_json_extract.c +227 -0
  55. package/csrc/op_json_filter.c +384 -0
  56. package/csrc/op_json_flatten.c +293 -0
  57. package/csrc/op_json_schema.c +503 -0
  58. package/csrc/op_label_encode.c +328 -53
  59. package/csrc/op_lag.c +181 -0
  60. package/csrc/op_lead.c +141 -89
  61. package/csrc/op_normalize.c +363 -79
  62. package/csrc/op_onehot.c +345 -73
  63. package/csrc/op_pivot.c +1546 -162
  64. package/csrc/op_quarantine.c +189 -0
  65. package/csrc/op_registry.c +2062 -166
  66. package/csrc/op_rename.c +41 -50
  67. package/csrc/op_replace.c +270 -118
  68. package/csrc/op_rleid.c +297 -0
  69. package/csrc/op_rowid.c +559 -0
  70. package/csrc/op_sample.c +80 -23
  71. package/csrc/op_schema.c +1341 -0
  72. package/csrc/op_schema_infer.c +252 -0
  73. package/csrc/op_select.c +265 -65
  74. package/csrc/op_set.c +3449 -0
  75. package/csrc/op_skip.c +30 -87
  76. package/csrc/op_sort.c +670 -124
  77. package/csrc/op_source_name.c +120 -0
  78. package/csrc/op_split.c +65 -28
  79. package/csrc/op_split_data.c +41 -9
  80. package/csrc/op_stack.c +178 -222
  81. package/csrc/op_stats.c +206 -110
  82. package/csrc/op_step.c +217 -55
  83. package/csrc/op_tail.c +21 -12
  84. package/csrc/op_tee.c +338 -0
  85. package/csrc/op_top.c +260 -53
  86. package/csrc/op_trim.c +48 -19
  87. package/csrc/op_unique.c +1193 -150
  88. package/csrc/op_unpivot.c +100 -66
  89. package/csrc/op_validate.c +601 -24
  90. package/csrc/op_window.c +492 -51
  91. package/csrc/path_policy.c +85 -0
  92. package/csrc/pipeline.c +872 -99
  93. package/csrc/recipes.c +3 -1
  94. package/csrc/report.c +73 -30
  95. package/csrc/selector.c +1097 -0
  96. package/csrc/size_utils.c +348 -0
  97. package/csrc/spill.c +317 -0
  98. package/csrc/spill.h +21 -0
  99. package/csrc/tranfi.h +169 -1
  100. package/csrc/transform.h +209 -0
  101. package/csrc/transform_api.c +2237 -0
  102. package/csrc/transform_categorical.c +923 -0
  103. package/csrc/transform_internal.h +472 -0
  104. package/csrc/transform_json.c +3812 -0
  105. package/csrc/transform_numeric.c +1966 -0
  106. package/csrc/transform_sha256.c +154 -0
  107. package/csrc/transform_wasm.h +162 -0
  108. package/csrc/transform_wasm_api.c +1373 -0
  109. package/csrc/wasm_api.c +70 -9
  110. package/napi_api.c +219 -11
  111. package/napi_transform.c +1648 -0
  112. package/napi_transform.h +8 -0
  113. package/package.json +27 -11
  114. package/scripts/install-native.js +76 -0
  115. package/scripts/prepack.js +64 -0
  116. package/scripts/sync-csrc.js +23 -0
  117. package/src/cli.js +8 -11
  118. package/src/engines/duckdb.js +45 -12
  119. package/src/index.js +661 -42
  120. package/src/memory_policy.js +411 -0
  121. package/src/native.js +1 -5
  122. package/src/pipeline.js +454 -31
  123. package/src/recipe_json.js +80 -0
  124. package/src/server.js +10 -8
  125. package/src/transform.js +403 -0
  126. package/src/transform_error.js +10 -0
  127. package/src/wasm.js +6 -4
  128. package/wasm/index.js +498 -10
  129. package/wasm/tranfi_core.js +0 -0
  130. package/wasm/transform.js +1156 -0
  131. package/wasm/worker.js +786 -0
  132. package/csrc/plan.c +0 -206
@@ -0,0 +1,1648 @@
1
+ #include "napi_transform.h"
2
+
3
+ #include "transform.h"
4
+
5
+ #include <math.h>
6
+ #include <stddef.h>
7
+ #include <stdint.h>
8
+ #include <stdatomic.h>
9
+ #include <stdlib.h>
10
+ #include <string.h>
11
+
12
+ typedef enum napi_transform_handle_type {
13
+ NAPI_TRANSFORM_RECIPE = 1,
14
+ NAPI_TRANSFORM_ANALYZER = 2,
15
+ NAPI_TRANSFORM_PLAN = 3,
16
+ NAPI_TRANSFORM_APPLY = 4
17
+ } napi_transform_handle_type;
18
+
19
+ typedef struct napi_transform_handle {
20
+ napi_transform_handle_type type;
21
+ void *pointer;
22
+ size_t column_count;
23
+ size_t output_column_count;
24
+ uint32_t *dtypes;
25
+ tf_transform_limits_v1 limits;
26
+ struct napi_transform_cancel *cancel;
27
+ } napi_transform_handle;
28
+
29
+ typedef struct napi_transform_cancel {
30
+ const _Atomic int32_t *flag;
31
+ _Atomic uint32_t *poll_count;
32
+ napi_ref reference;
33
+ } napi_transform_cancel;
34
+
35
+ typedef struct napi_transform_bytes {
36
+ const uint8_t *data;
37
+ size_t len;
38
+ uint8_t *owned;
39
+ } napi_transform_bytes;
40
+
41
+ typedef struct napi_transform_schema_owner {
42
+ tf_schema_view_v1 view;
43
+ tf_field_view_v1 *fields;
44
+ char **ids;
45
+ char **names;
46
+ uint32_t *dtypes;
47
+ size_t count;
48
+ } napi_transform_schema_owner;
49
+
50
+ typedef struct napi_transform_table_owner {
51
+ tf_table_view_v1 view;
52
+ tf_column_view_v1 *columns;
53
+ } napi_transform_table_owner;
54
+
55
+ typedef struct napi_transform_dense_result_owner {
56
+ napi_value result;
57
+ void *data;
58
+ size_t data_bytes;
59
+ size_t rows;
60
+ size_t columns;
61
+ } napi_transform_dense_result_owner;
62
+
63
+ typedef struct napi_transform_limit_field {
64
+ const char *name;
65
+ size_t offset;
66
+ } napi_transform_limit_field;
67
+
68
+ static const napi_type_tag NAPI_TRANSFORM_HANDLE_TAG = {
69
+ UINT64_C(0x7ac65b1a42a84bc0),
70
+ UINT64_C(0xa116f19a14f96c52)
71
+ };
72
+
73
+ #define LIMIT_FIELD(js_name, member) \
74
+ {js_name, offsetof(tf_transform_limits_v1, member)}
75
+
76
+ static const napi_transform_limit_field LIMIT_FIELDS[] = {
77
+ LIMIT_FIELD("maxRecipeBytes", max_recipe_bytes),
78
+ LIMIT_FIELD("maxPlanBytes", max_plan_bytes),
79
+ LIMIT_FIELD("maxJsonDepth", max_json_depth),
80
+ LIMIT_FIELD("maxObjectKeys", max_object_keys),
81
+ LIMIT_FIELD("maxSteps", max_steps),
82
+ LIMIT_FIELD("maxInputColumns", max_input_columns),
83
+ LIMIT_FIELD("maxOutputColumns", max_output_columns),
84
+ LIMIT_FIELD("maxCategoriesPerColumn", max_categories_per_column),
85
+ LIMIT_FIELD("maxTotalCategories", max_total_categories),
86
+ LIMIT_FIELD("maxStringBytes", max_string_bytes),
87
+ LIMIT_FIELD("maxDecodedStringBytes", max_decoded_string_bytes),
88
+ LIMIT_FIELD("maxAnalyzerRows", max_analyzer_rows),
89
+ LIMIT_FIELD("maxAnalyzerInputBytes", max_analyzer_input_bytes),
90
+ LIMIT_FIELD("maxResidentStateBytes", max_resident_state_bytes),
91
+ LIMIT_FIELD("maxSpillBytes", max_spill_bytes),
92
+ LIMIT_FIELD("maxApplyRows", max_apply_rows),
93
+ LIMIT_FIELD("maxApplyInputBytes", max_apply_input_bytes),
94
+ LIMIT_FIELD("maxOutputElementsPerCall", max_output_elements_per_call),
95
+ LIMIT_FIELD("maxAllocationBytes", max_allocation_bytes),
96
+ LIMIT_FIELD("maxAllocationsPerSession", max_allocations_per_session),
97
+ LIMIT_FIELD("maxLiveHandles", max_live_handles),
98
+ LIMIT_FIELD("maxRetiredHandleSlots", max_retired_handle_slots),
99
+ };
100
+
101
+ static int napi_transform_check(napi_env env, napi_status status) {
102
+ if (status == napi_ok) return 1;
103
+ napi_throw_error(env, NULL, "N-API prepared-transform call failed");
104
+ return 0;
105
+ }
106
+
107
+ static napi_value napi_transform_undefined(napi_env env) {
108
+ napi_value value = NULL;
109
+ (void)napi_get_undefined(env, &value);
110
+ return value;
111
+ }
112
+
113
+ static napi_value napi_transform_throw_code(
114
+ napi_env env, tf_transform_code code, const char *message) {
115
+ napi_value message_value = NULL;
116
+ napi_value error_value = NULL;
117
+ napi_value name_value = NULL;
118
+ napi_value code_value = NULL;
119
+ const char *text = message ? message : "prepared transform failed";
120
+ if (napi_create_string_utf8(env, text, NAPI_AUTO_LENGTH, &message_value)
121
+ != napi_ok
122
+ || napi_create_error(env, NULL, message_value, &error_value) != napi_ok
123
+ || napi_create_string_utf8(
124
+ env, "TranfiTransformError", NAPI_AUTO_LENGTH, &name_value) != napi_ok
125
+ || napi_create_uint32(env, (uint32_t)code, &code_value) != napi_ok
126
+ || napi_set_named_property(env, error_value, "name", name_value) != napi_ok
127
+ || napi_set_named_property(env, error_value, "code", code_value) != napi_ok
128
+ || napi_throw(env, error_value) != napi_ok) {
129
+ napi_throw_error(env, NULL, "failed to construct TranfiTransformError");
130
+ }
131
+ return NULL;
132
+ }
133
+
134
+ static napi_value napi_transform_throw_error(
135
+ napi_env env, tf_transform_code code, tf_transform_error **error) {
136
+ char *message = NULL;
137
+ const uint8_t *span = NULL;
138
+ size_t len = 0;
139
+ napi_value result;
140
+ if (error && *error) {
141
+ span = tf_transform_error_message(*error, &len);
142
+ if (span && len <= SIZE_MAX - 1) {
143
+ message = (char *)malloc(len + 1);
144
+ if (message) {
145
+ memcpy(message, span, len);
146
+ message[len] = '\0';
147
+ }
148
+ }
149
+ }
150
+ if (!message) {
151
+ static const char fallback[] = "prepared transform failed";
152
+ message = (char *)malloc(sizeof(fallback));
153
+ if (message) memcpy(message, fallback, sizeof(fallback));
154
+ }
155
+ tf_transform_error_destroy(error);
156
+ result = napi_transform_throw_code(env, code, message);
157
+ free(message);
158
+ return result;
159
+ }
160
+
161
+ static int napi_transform_cancel_poll(void *user) {
162
+ const napi_transform_cancel *cancel = (const napi_transform_cancel *)user;
163
+ if (!cancel || !cancel->flag) return 0;
164
+ if (cancel->poll_count) {
165
+ (void)atomic_fetch_add_explicit(
166
+ cancel->poll_count, UINT32_C(1), memory_order_release);
167
+ }
168
+ return atomic_load_explicit(cancel->flag, memory_order_acquire) != 0;
169
+ }
170
+
171
+ static void napi_transform_cancel_destroy(
172
+ napi_env env, napi_transform_cancel **cancel) {
173
+ if (!cancel || !*cancel) return;
174
+ if ((*cancel)->reference) {
175
+ (void)napi_delete_reference(env, (*cancel)->reference);
176
+ }
177
+ free(*cancel);
178
+ *cancel = NULL;
179
+ }
180
+
181
+ static void napi_transform_destroy_payload(
182
+ napi_env env, napi_transform_handle *handle) {
183
+ if (!handle) return;
184
+ if (handle->pointer) {
185
+ if (handle->type == NAPI_TRANSFORM_RECIPE) {
186
+ tf_transform_recipe *value = (tf_transform_recipe *)handle->pointer;
187
+ tf_transform_recipe_destroy(&value);
188
+ } else if (handle->type == NAPI_TRANSFORM_ANALYZER) {
189
+ tf_transform_analyzer *value = (tf_transform_analyzer *)handle->pointer;
190
+ tf_transform_analyzer_destroy(&value);
191
+ } else if (handle->type == NAPI_TRANSFORM_PLAN) {
192
+ tf_transform_plan *value = (tf_transform_plan *)handle->pointer;
193
+ tf_transform_plan_destroy(&value);
194
+ } else if (handle->type == NAPI_TRANSFORM_APPLY) {
195
+ tf_transform_apply *value = (tf_transform_apply *)handle->pointer;
196
+ tf_transform_apply_destroy(&value);
197
+ }
198
+ }
199
+ handle->pointer = NULL;
200
+ free(handle->dtypes);
201
+ handle->dtypes = NULL;
202
+ handle->column_count = 0;
203
+ handle->output_column_count = 0;
204
+ memset(&handle->limits, 0, sizeof(handle->limits));
205
+ napi_transform_cancel_destroy(env, &handle->cancel);
206
+ }
207
+
208
+ static void napi_transform_finalize(
209
+ napi_env env, void *data, void *hint) {
210
+ napi_transform_handle *handle = (napi_transform_handle *)data;
211
+ (void)env;
212
+ (void)hint;
213
+ napi_transform_destroy_payload(env, handle);
214
+ free(handle);
215
+ }
216
+
217
+ static napi_value napi_transform_new_handle_with_cancel(
218
+ napi_env env, napi_transform_handle_type type, void *pointer,
219
+ const uint32_t *dtypes, size_t column_count,
220
+ size_t output_column_count, const tf_transform_limits_v1 *limits,
221
+ napi_transform_cancel *cancel) {
222
+ napi_transform_handle *handle = NULL;
223
+ napi_value object = NULL;
224
+ handle = (napi_transform_handle *)calloc(1, sizeof(*handle));
225
+ if (!handle) {
226
+ napi_transform_handle temporary = {
227
+ .type = type,
228
+ .pointer = pointer,
229
+ .cancel = cancel
230
+ };
231
+ napi_transform_destroy_payload(env, &temporary);
232
+ napi_throw_error(env, NULL, "prepared-transform handle allocation failed");
233
+ return NULL;
234
+ }
235
+ handle->type = type;
236
+ handle->pointer = pointer;
237
+ handle->cancel = cancel;
238
+ handle->output_column_count = output_column_count;
239
+ if (limits) handle->limits = *limits;
240
+ if (column_count != 0) {
241
+ if (!dtypes || column_count > SIZE_MAX / sizeof(*handle->dtypes)
242
+ || (limits && (uint64_t)column_count
243
+ > limits->max_allocation_bytes / sizeof(*handle->dtypes))) {
244
+ napi_transform_destroy_payload(env, handle);
245
+ free(handle);
246
+ napi_transform_throw_code(
247
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
248
+ "prepared-transform dtype metadata exceeds allocation limit");
249
+ return NULL;
250
+ }
251
+ handle->dtypes = (uint32_t *)malloc(
252
+ column_count * sizeof(*handle->dtypes));
253
+ if (!handle->dtypes) {
254
+ napi_transform_destroy_payload(env, handle);
255
+ free(handle);
256
+ napi_throw_error(env, NULL, "prepared-transform dtype allocation failed");
257
+ return NULL;
258
+ }
259
+ memcpy(handle->dtypes, dtypes, column_count * sizeof(*handle->dtypes));
260
+ handle->column_count = column_count;
261
+ }
262
+ if (napi_create_object(env, &object) != napi_ok
263
+ || napi_type_tag_object(env, object, &NAPI_TRANSFORM_HANDLE_TAG) != napi_ok
264
+ || napi_wrap(
265
+ env, object, handle, napi_transform_finalize, NULL, NULL) != napi_ok) {
266
+ napi_transform_destroy_payload(env, handle);
267
+ free(handle);
268
+ napi_throw_error(env, NULL, "prepared-transform handle object allocation failed");
269
+ return NULL;
270
+ }
271
+ return object;
272
+ }
273
+
274
+ static napi_value napi_transform_new_handle(
275
+ napi_env env, napi_transform_handle_type type, void *pointer,
276
+ const uint32_t *dtypes, size_t column_count) {
277
+ return napi_transform_new_handle_with_cancel(
278
+ env, type, pointer, dtypes, column_count, 0, NULL, NULL);
279
+ }
280
+
281
+ static napi_transform_handle *napi_transform_unwrap_handle(
282
+ napi_env env, napi_value value) {
283
+ napi_transform_handle *handle = NULL;
284
+ bool matches = false;
285
+ if (napi_check_object_type_tag(
286
+ env, value, &NAPI_TRANSFORM_HANDLE_TAG, &matches) != napi_ok
287
+ || !matches
288
+ || napi_unwrap(env, value, (void **)&handle) != napi_ok
289
+ || !handle) {
290
+ napi_throw_type_error(env, NULL, "expected a Tranfi prepared-transform handle");
291
+ return NULL;
292
+ }
293
+ return handle;
294
+ }
295
+
296
+ static napi_transform_handle *napi_transform_get_handle(
297
+ napi_env env, napi_value value, napi_transform_handle_type expected) {
298
+ napi_transform_handle *handle = napi_transform_unwrap_handle(env, value);
299
+ if (!handle) return NULL;
300
+ if (handle->type != expected) {
301
+ napi_throw_type_error(env, NULL, "prepared-transform handle type mismatch");
302
+ return NULL;
303
+ }
304
+ if (!handle->pointer) {
305
+ napi_transform_throw_code(
306
+ env, TF_TRANSFORM_INVALID_STATE,
307
+ "prepared-transform handle is disposed");
308
+ return NULL;
309
+ }
310
+ return handle;
311
+ }
312
+
313
+ static int napi_transform_get_optional_uint64(
314
+ napi_env env, napi_value object, const char *name,
315
+ uint64_t *out, int *present) {
316
+ bool has = false;
317
+ napi_value value;
318
+ napi_valuetype type;
319
+ double number;
320
+ if (!napi_transform_check(
321
+ env, napi_has_named_property(env, object, name, &has))) return 0;
322
+ if (!has) {
323
+ *present = 0;
324
+ return 1;
325
+ }
326
+ if (!napi_transform_check(
327
+ env, napi_get_named_property(env, object, name, &value))
328
+ || !napi_transform_check(env, napi_typeof(env, value, &type))) return 0;
329
+ if (type == napi_undefined || type == napi_null) {
330
+ *present = 0;
331
+ return 1;
332
+ }
333
+ if (type != napi_number
334
+ || !napi_transform_check(env, napi_get_value_double(env, value, &number))) {
335
+ napi_throw_type_error(env, NULL, "transform limits must be numbers");
336
+ return 0;
337
+ }
338
+ if (!isfinite(number) || number < 0.0 || floor(number) != number
339
+ || number > 9007199254740991.0) {
340
+ napi_throw_range_error(
341
+ env, NULL, "transform limits must be nonnegative safe integers");
342
+ return 0;
343
+ }
344
+ *out = (uint64_t)number;
345
+ *present = 1;
346
+ return 1;
347
+ }
348
+
349
+ static int napi_transform_parse_limits(
350
+ napi_env env, napi_value value, tf_transform_limits_v1 *limits,
351
+ int *provided) {
352
+ napi_valuetype type;
353
+ if (tf_transform_limits_init_safe_v1(limits, sizeof(*limits))
354
+ != TF_TRANSFORM_OK) {
355
+ napi_throw_error(env, NULL, "failed to initialize transform limits");
356
+ return 0;
357
+ }
358
+ *provided = 0;
359
+ if (!value) return 1;
360
+ if (!napi_transform_check(env, napi_typeof(env, value, &type))) return 0;
361
+ if (type == napi_undefined || type == napi_null) return 1;
362
+ if (type != napi_object) {
363
+ napi_throw_type_error(env, NULL, "transform limits must be an object");
364
+ return 0;
365
+ }
366
+ *provided = 1;
367
+ for (size_t i = 0; i < sizeof(LIMIT_FIELDS) / sizeof(LIMIT_FIELDS[0]); ++i) {
368
+ uint64_t value_u64 = 0;
369
+ int present = 0;
370
+ uint64_t *field;
371
+ if (!napi_transform_get_optional_uint64(
372
+ env, value, LIMIT_FIELDS[i].name, &value_u64, &present)) return 0;
373
+ if (!present) continue;
374
+ if (value_u64 == 0) {
375
+ napi_transform_throw_code(
376
+ env, TF_TRANSFORM_INVALID_ARGUMENT,
377
+ "transform limits must be positive safe integers");
378
+ return 0;
379
+ }
380
+ field = (uint64_t *)((uint8_t *)limits + LIMIT_FIELDS[i].offset);
381
+ *field = value_u64;
382
+ }
383
+ return 1;
384
+ }
385
+
386
+ static int napi_transform_parse_cancel_flag(
387
+ napi_env env, napi_value value, napi_transform_cancel **out) {
388
+ bool is_typedarray = false;
389
+ napi_typedarray_type typed_type;
390
+ size_t count = 0;
391
+ void *data = NULL;
392
+ napi_value arraybuffer;
393
+ size_t byte_offset = 0;
394
+ napi_valuetype value_type;
395
+ napi_transform_cancel *cancel;
396
+ *out = NULL;
397
+ if (!value) return 1;
398
+ if (!napi_transform_check(env, napi_typeof(env, value, &value_type)))
399
+ return 0;
400
+ if (value_type == napi_undefined || value_type == napi_null) return 1;
401
+ if (!napi_transform_check(env, napi_is_typedarray(
402
+ env, value, &is_typedarray))
403
+ || !is_typedarray
404
+ || !napi_transform_check(env, napi_get_typedarray_info(
405
+ env, value, &typed_type, &count, &data,
406
+ &arraybuffer, &byte_offset))) {
407
+ napi_throw_type_error(
408
+ env, NULL, "cancelFlag must be a nonempty Int32Array");
409
+ return 0;
410
+ }
411
+ (void)arraybuffer;
412
+ (void)byte_offset;
413
+ if (typed_type != napi_int32_array || count == 0 || !data
414
+ || (uintptr_t)data % _Alignof(_Atomic int32_t) != 0) {
415
+ napi_throw_type_error(
416
+ env, NULL, "cancelFlag must be an aligned nonempty Int32Array");
417
+ return 0;
418
+ }
419
+ cancel = (napi_transform_cancel *)calloc(1, sizeof(*cancel));
420
+ if (!cancel) {
421
+ napi_throw_error(env, NULL, "cancelFlag reference allocation failed");
422
+ return 0;
423
+ }
424
+ cancel->flag = (const _Atomic int32_t *)data;
425
+ cancel->poll_count = count >= 2
426
+ ? (_Atomic uint32_t *)((uint8_t *)data + sizeof(int32_t))
427
+ : NULL;
428
+ if (!atomic_is_lock_free(cancel->flag)
429
+ || (cancel->poll_count && !atomic_is_lock_free(cancel->poll_count))) {
430
+ free(cancel);
431
+ napi_transform_throw_code(
432
+ env, TF_TRANSFORM_UNSUPPORTED_RUNTIME,
433
+ "cancelFlag does not provide lock-free atomic int32 access");
434
+ return 0;
435
+ }
436
+ if (napi_create_reference(env, value, 1, &cancel->reference) != napi_ok) {
437
+ free(cancel);
438
+ napi_throw_error(env, NULL, "cancelFlag reference creation failed");
439
+ return 0;
440
+ }
441
+ *out = cancel;
442
+ return 1;
443
+ }
444
+
445
+ static int napi_transform_parse_runtime_options(
446
+ napi_env env, napi_value value,
447
+ tf_transform_limits_v1 *limits, int *provided,
448
+ napi_transform_cancel **cancel) {
449
+ bool has_limits = false;
450
+ bool has_cancel = false;
451
+ napi_value limits_value = NULL;
452
+ napi_value cancel_value = NULL;
453
+ napi_valuetype type;
454
+ *cancel = NULL;
455
+ if (!value) return napi_transform_parse_limits(
456
+ env, NULL, limits, provided);
457
+ if (!napi_transform_check(env, napi_typeof(env, value, &type))) return 0;
458
+ if (type == napi_undefined || type == napi_null)
459
+ return napi_transform_parse_limits(env, NULL, limits, provided);
460
+ if (type != napi_object) {
461
+ napi_throw_type_error(env, NULL, "transform runtime options must be an object");
462
+ return 0;
463
+ }
464
+ if (!napi_transform_check(env, napi_has_named_property(
465
+ env, value, "limits", &has_limits))
466
+ || !napi_transform_check(env, napi_has_named_property(
467
+ env, value, "cancelFlag", &has_cancel))) return 0;
468
+ if (!has_limits && !has_cancel)
469
+ return napi_transform_parse_limits(env, value, limits, provided);
470
+ if (has_limits && !napi_transform_check(env, napi_get_named_property(
471
+ env, value, "limits", &limits_value))) return 0;
472
+ if (has_cancel && !napi_transform_check(env, napi_get_named_property(
473
+ env, value, "cancelFlag", &cancel_value))) return 0;
474
+ if (!napi_transform_parse_limits(
475
+ env, has_limits ? limits_value : NULL, limits, provided)
476
+ || !napi_transform_parse_cancel_flag(
477
+ env, has_cancel ? cancel_value : NULL, cancel)) {
478
+ napi_transform_cancel_destroy(env, cancel);
479
+ return 0;
480
+ }
481
+ return 1;
482
+ }
483
+
484
+ static void napi_transform_runtime_init(
485
+ tf_transform_runtime_v1 *runtime,
486
+ const tf_transform_limits_v1 *limits, int provided,
487
+ const napi_transform_cancel *cancel) {
488
+ memset(runtime, 0, sizeof(*runtime));
489
+ runtime->abi_version = 1;
490
+ runtime->struct_size = (uint32_t)sizeof(*runtime);
491
+ runtime->limits = provided ? limits : NULL;
492
+ if (cancel) {
493
+ runtime->cancel = napi_transform_cancel_poll;
494
+ runtime->cancel_user = (void *)cancel;
495
+ }
496
+ }
497
+
498
+ static int napi_transform_get_size(
499
+ napi_env env, napi_value value, const char *label, size_t *out) {
500
+ double number;
501
+ if (napi_get_value_double(env, value, &number) != napi_ok
502
+ || !isfinite(number) || number < 0.0 || floor(number) != number
503
+ || number > 9007199254740991.0
504
+ || number > (double)SIZE_MAX) {
505
+ napi_throw_range_error(env, NULL, label);
506
+ return 0;
507
+ }
508
+ *out = (size_t)number;
509
+ return 1;
510
+ }
511
+
512
+ static int napi_transform_get_named_size(
513
+ napi_env env, napi_value object, const char *name,
514
+ size_t default_value, int required, size_t *out) {
515
+ bool has = false;
516
+ napi_value value;
517
+ if (!napi_transform_check(
518
+ env, napi_has_named_property(env, object, name, &has))) return 0;
519
+ if (!has) {
520
+ if (required) {
521
+ napi_throw_type_error(env, NULL, "required numeric property is missing");
522
+ return 0;
523
+ }
524
+ *out = default_value;
525
+ return 1;
526
+ }
527
+ if (!napi_transform_check(
528
+ env, napi_get_named_property(env, object, name, &value))) return 0;
529
+ return napi_transform_get_size(
530
+ env, value, "numeric property must be a nonnegative safe integer", out);
531
+ }
532
+
533
+ static int napi_transform_parse_bytes(
534
+ napi_env env, napi_value value, int allow_string,
535
+ uint64_t max_len, uint64_t max_string_allocation,
536
+ napi_transform_bytes *bytes);
537
+
538
+ static int napi_transform_get_string(
539
+ napi_env env, napi_value value, char **out, size_t *out_len,
540
+ uint64_t max_len, uint64_t max_allocation,
541
+ uint64_t *decoded_total, uint64_t max_decoded,
542
+ const napi_transform_cancel *cancel) {
543
+ size_t len = 0;
544
+ size_t offset = 0;
545
+ char *copy;
546
+ napi_valuetype type;
547
+ if (!napi_transform_check(env, napi_typeof(env, value, &type))) return 0;
548
+ if (napi_transform_cancel_poll((void *)cancel)) {
549
+ napi_transform_throw_code(
550
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
551
+ return 0;
552
+ }
553
+ if (type == napi_string) {
554
+ if (!napi_transform_check(
555
+ env, napi_get_value_string_utf8(
556
+ env, value, NULL, 0, &len))) return 0;
557
+ } else {
558
+ napi_transform_bytes bytes = {0};
559
+ if (!napi_transform_parse_bytes(
560
+ env, value, 0, max_len, max_allocation, &bytes)) return 0;
561
+ len = bytes.len;
562
+ }
563
+ if ((uint64_t)len > max_len
564
+ || (uint64_t)len >= max_allocation
565
+ || (decoded_total
566
+ && ((uint64_t)len > max_decoded - *decoded_total))) {
567
+ napi_transform_throw_code(
568
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
569
+ "schema strings exceed configured transform limits");
570
+ return 0;
571
+ }
572
+ copy = (char *)malloc(len + 1);
573
+ if (!copy) {
574
+ napi_throw_error(env, NULL, "schema string allocation failed");
575
+ return 0;
576
+ }
577
+ if (type == napi_string) {
578
+ if (!napi_transform_check(env, napi_get_value_string_utf8(
579
+ env, value, copy, len + 1, &len))) {
580
+ free(copy);
581
+ return 0;
582
+ }
583
+ } else {
584
+ napi_transform_bytes bytes = {0};
585
+ if (!napi_transform_parse_bytes(
586
+ env, value, 0, max_len, max_allocation, &bytes)) {
587
+ free(copy);
588
+ return 0;
589
+ }
590
+ while (offset < len) {
591
+ size_t chunk = len - offset;
592
+ if (chunk > TF_TRANSFORM_CANCEL_BYTES_V1)
593
+ chunk = TF_TRANSFORM_CANCEL_BYTES_V1;
594
+ if (napi_transform_cancel_poll((void *)cancel)) {
595
+ free(copy);
596
+ napi_transform_throw_code(
597
+ env, TF_TRANSFORM_CANCELLED,
598
+ "prepared transform cancelled");
599
+ return 0;
600
+ }
601
+ memcpy(copy + offset, bytes.data + offset, chunk);
602
+ offset += chunk;
603
+ }
604
+ copy[len] = '\0';
605
+ }
606
+ if (napi_transform_cancel_poll((void *)cancel)) {
607
+ free(copy);
608
+ napi_transform_throw_code(
609
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
610
+ return 0;
611
+ }
612
+ if (len == 0 || memchr(copy, '\0', len) != NULL) {
613
+ free(copy);
614
+ napi_throw_type_error(env, NULL, "schema ID/name must be nonempty UTF-8");
615
+ return 0;
616
+ }
617
+ *out = copy;
618
+ *out_len = len;
619
+ if (decoded_total) *decoded_total += (uint64_t)len;
620
+ return 1;
621
+ }
622
+
623
+ static int napi_transform_get_named_string(
624
+ napi_env env, napi_value object, const char *name,
625
+ char **out, size_t *out_len, const tf_transform_limits_v1 *limits,
626
+ uint64_t *decoded_total, const napi_transform_cancel *cancel) {
627
+ napi_value value;
628
+ if (!napi_transform_check(
629
+ env, napi_get_named_property(env, object, name, &value))) return 0;
630
+ return napi_transform_get_string(
631
+ env, value, out, out_len, limits->max_string_bytes,
632
+ limits->max_allocation_bytes,
633
+ decoded_total, limits->max_decoded_string_bytes, cancel);
634
+ }
635
+
636
+ static void napi_transform_schema_clear(
637
+ napi_transform_schema_owner *owner) {
638
+ if (!owner) return;
639
+ for (size_t i = 0; i < owner->count; ++i) {
640
+ free(owner->ids ? owner->ids[i] : NULL);
641
+ free(owner->names ? owner->names[i] : NULL);
642
+ }
643
+ free(owner->fields);
644
+ free(owner->ids);
645
+ free(owner->names);
646
+ free(owner->dtypes);
647
+ memset(owner, 0, sizeof(*owner));
648
+ }
649
+
650
+ static int napi_transform_parse_schema(
651
+ napi_env env, napi_value value, const tf_transform_limits_v1 *limits,
652
+ const napi_transform_cancel *cancel,
653
+ napi_transform_schema_owner *owner) {
654
+ napi_value fields_value;
655
+ bool is_array = false;
656
+ uint32_t count_u32 = 0;
657
+ uint64_t decoded_total = 0;
658
+ memset(owner, 0, sizeof(*owner));
659
+ if (!napi_transform_check(
660
+ env, napi_get_named_property(env, value, "fields", &fields_value))
661
+ || !napi_transform_check(env, napi_is_array(env, fields_value, &is_array)))
662
+ return 0;
663
+ if (!is_array
664
+ || !napi_transform_check(
665
+ env, napi_get_array_length(env, fields_value, &count_u32))) {
666
+ napi_throw_type_error(env, NULL, "schema.fields must be an array");
667
+ return 0;
668
+ }
669
+ if (count_u32 == 0) {
670
+ napi_throw_type_error(env, NULL, "schema.fields must be nonempty");
671
+ return 0;
672
+ }
673
+ if ((uint64_t)count_u32 > limits->max_input_columns) {
674
+ napi_transform_throw_code(
675
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
676
+ "schema column count exceeds configured transform limits");
677
+ return 0;
678
+ }
679
+ owner->count = (size_t)count_u32;
680
+ if ((uint64_t)owner->count > limits->max_allocation_bytes
681
+ / sizeof(*owner->fields)
682
+ || (uint64_t)owner->count > limits->max_allocation_bytes
683
+ / sizeof(*owner->ids)
684
+ || (uint64_t)owner->count > limits->max_allocation_bytes
685
+ / sizeof(*owner->names)
686
+ || (uint64_t)owner->count > limits->max_allocation_bytes
687
+ / sizeof(*owner->dtypes)) {
688
+ napi_transform_throw_code(
689
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
690
+ "schema descriptors exceed configured allocation limit");
691
+ return 0;
692
+ }
693
+ owner->fields = (tf_field_view_v1 *)calloc(
694
+ owner->count, sizeof(*owner->fields));
695
+ owner->ids = (char **)calloc(owner->count, sizeof(*owner->ids));
696
+ owner->names = (char **)calloc(owner->count, sizeof(*owner->names));
697
+ owner->dtypes = (uint32_t *)calloc(owner->count, sizeof(*owner->dtypes));
698
+ if (!owner->fields || !owner->ids || !owner->names || !owner->dtypes) {
699
+ napi_transform_schema_clear(owner);
700
+ napi_throw_error(env, NULL, "schema descriptor allocation failed");
701
+ return 0;
702
+ }
703
+ for (size_t i = 0; i < owner->count; ++i) {
704
+ napi_value field;
705
+ napi_value dtype_value;
706
+ char *dtype = NULL;
707
+ size_t dtype_len = 0;
708
+ size_t id_len = 0;
709
+ size_t name_len = 0;
710
+ if (i % TF_TRANSFORM_CANCEL_ITERS_V1 == 0
711
+ && napi_transform_cancel_poll((void *)cancel)) {
712
+ napi_transform_schema_clear(owner);
713
+ napi_transform_throw_code(
714
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
715
+ return 0;
716
+ }
717
+ if (!napi_transform_check(
718
+ env, napi_get_element(env, fields_value, (uint32_t)i, &field))
719
+ || !napi_transform_get_named_string(
720
+ env, field, "id", &owner->ids[i], &id_len,
721
+ limits, &decoded_total, cancel)
722
+ || !napi_transform_get_named_string(
723
+ env, field, "name", &owner->names[i], &name_len,
724
+ limits, &decoded_total, cancel)
725
+ || !napi_transform_check(
726
+ env, napi_get_named_property(env, field, "dtype", &dtype_value))
727
+ || !napi_transform_get_string(
728
+ env, dtype_value, &dtype, &dtype_len,
729
+ UINT64_MAX, limits->max_allocation_bytes,
730
+ NULL, UINT64_MAX, cancel)) {
731
+ free(dtype);
732
+ napi_transform_schema_clear(owner);
733
+ return 0;
734
+ }
735
+ if (dtype_len == 7 && memcmp(dtype, "float32", 7) == 0)
736
+ owner->dtypes[i] = TF_VIEW_FLOAT32;
737
+ else if (dtype_len == 7 && memcmp(dtype, "float64", 7) == 0)
738
+ owner->dtypes[i] = TF_VIEW_FLOAT64;
739
+ else {
740
+ free(dtype);
741
+ napi_transform_schema_clear(owner);
742
+ napi_throw_type_error(env, NULL, "schema dtype must be float32 or float64");
743
+ return 0;
744
+ }
745
+ free(dtype);
746
+ owner->fields[i].abi_version = 1;
747
+ owner->fields[i].struct_size = (uint32_t)sizeof(owner->fields[i]);
748
+ owner->fields[i].dtype = owner->dtypes[i];
749
+ owner->fields[i].id_utf8 = (const uint8_t *)owner->ids[i];
750
+ owner->fields[i].id_bytes = id_len;
751
+ owner->fields[i].name_utf8 = (const uint8_t *)owner->names[i];
752
+ owner->fields[i].name_bytes = name_len;
753
+ }
754
+ owner->view.abi_version = 1;
755
+ owner->view.struct_size = (uint32_t)sizeof(owner->view);
756
+ owner->view.column_count = owner->count;
757
+ owner->view.fields = owner->fields;
758
+ owner->view.fields_bytes = owner->count * sizeof(*owner->fields);
759
+ if (napi_transform_cancel_poll((void *)cancel)) {
760
+ napi_transform_schema_clear(owner);
761
+ napi_transform_throw_code(
762
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
763
+ return 0;
764
+ }
765
+ return 1;
766
+ }
767
+
768
+ static void napi_transform_bytes_clear(napi_transform_bytes *bytes) {
769
+ if (!bytes) return;
770
+ free(bytes->owned);
771
+ memset(bytes, 0, sizeof(*bytes));
772
+ }
773
+
774
+ static int napi_transform_parse_bytes(
775
+ napi_env env, napi_value value, int allow_string,
776
+ uint64_t max_len, uint64_t max_string_allocation,
777
+ napi_transform_bytes *bytes) {
778
+ bool is_buffer = false;
779
+ bool is_typedarray = false;
780
+ napi_valuetype type;
781
+ memset(bytes, 0, sizeof(*bytes));
782
+ if (!napi_transform_check(env, napi_typeof(env, value, &type))) return 0;
783
+ if (allow_string && type == napi_string) {
784
+ size_t written = 0;
785
+ if (!napi_transform_check(
786
+ env, napi_get_value_string_utf8(env, value, NULL, 0, &bytes->len)))
787
+ return 0;
788
+ if ((uint64_t)bytes->len > max_len
789
+ || (uint64_t)bytes->len >= max_string_allocation) {
790
+ napi_transform_throw_code(
791
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
792
+ "prepared-transform input exceeds configured byte limit");
793
+ return 0;
794
+ }
795
+ bytes->owned = (uint8_t *)malloc(bytes->len + 1);
796
+ if (!bytes->owned) {
797
+ napi_throw_error(env, NULL, "prepared-transform byte allocation failed");
798
+ return 0;
799
+ }
800
+ if (!napi_transform_check(env, napi_get_value_string_utf8(
801
+ env, value, (char *)bytes->owned, bytes->len + 1, &written))) {
802
+ napi_transform_bytes_clear(bytes);
803
+ return 0;
804
+ }
805
+ bytes->len = written;
806
+ bytes->data = bytes->owned;
807
+ return 1;
808
+ }
809
+ if (!napi_transform_check(env, napi_is_buffer(env, value, &is_buffer))) return 0;
810
+ if (is_buffer) {
811
+ void *data = NULL;
812
+ if (!napi_transform_check(
813
+ env, napi_get_buffer_info(env, value, &data, &bytes->len))) return 0;
814
+ if ((uint64_t)bytes->len > max_len) {
815
+ napi_transform_throw_code(
816
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
817
+ "prepared-transform input exceeds configured byte limit");
818
+ return 0;
819
+ }
820
+ bytes->data = (const uint8_t *)data;
821
+ return 1;
822
+ }
823
+ if (!napi_transform_check(
824
+ env, napi_is_typedarray(env, value, &is_typedarray))) return 0;
825
+ if (is_typedarray) {
826
+ napi_typedarray_type typed_type;
827
+ size_t count;
828
+ void *data = NULL;
829
+ napi_value arraybuffer;
830
+ size_t byte_offset;
831
+ if (!napi_transform_check(env, napi_get_typedarray_info(
832
+ env, value, &typed_type, &count, &data,
833
+ &arraybuffer, &byte_offset))) return 0;
834
+ (void)arraybuffer;
835
+ (void)byte_offset;
836
+ if (typed_type != napi_uint8_array
837
+ && typed_type != napi_uint8_clamped_array) {
838
+ napi_throw_type_error(env, NULL, "expected a UTF-8 string or byte array");
839
+ return 0;
840
+ }
841
+ if ((uint64_t)count > max_len) {
842
+ napi_transform_throw_code(
843
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
844
+ "prepared-transform input exceeds configured byte limit");
845
+ return 0;
846
+ }
847
+ bytes->data = (const uint8_t *)data;
848
+ bytes->len = count;
849
+ return 1;
850
+ }
851
+ napi_throw_type_error(env, NULL, "expected a UTF-8 string or byte array");
852
+ return 0;
853
+ }
854
+
855
+ static int napi_transform_parse_numeric_array(
856
+ napi_env env, napi_value value, uint32_t expected_dtype,
857
+ const void **data, size_t *data_bytes, size_t *item_size) {
858
+ bool is_typedarray = false;
859
+ napi_typedarray_type typed_type;
860
+ size_t count;
861
+ void *pointer = NULL;
862
+ napi_value arraybuffer;
863
+ size_t byte_offset;
864
+ if (!napi_transform_check(
865
+ env, napi_is_typedarray(env, value, &is_typedarray))) return 0;
866
+ if (!is_typedarray
867
+ || !napi_transform_check(env, napi_get_typedarray_info(
868
+ env, value, &typed_type, &count, &pointer,
869
+ &arraybuffer, &byte_offset))) {
870
+ napi_throw_type_error(env, NULL, "table data must be a typed array");
871
+ return 0;
872
+ }
873
+ (void)arraybuffer;
874
+ (void)byte_offset;
875
+ if (expected_dtype == TF_VIEW_FLOAT32 && typed_type == napi_float32_array)
876
+ *item_size = sizeof(float);
877
+ else if (expected_dtype == TF_VIEW_FLOAT64 && typed_type == napi_float64_array)
878
+ *item_size = sizeof(double);
879
+ else {
880
+ napi_throw_type_error(env, NULL, "table typed array dtype mismatches schema");
881
+ return 0;
882
+ }
883
+ if (count > SIZE_MAX / *item_size) {
884
+ napi_throw_range_error(env, NULL, "table typed array is too large");
885
+ return 0;
886
+ }
887
+ *data_bytes = count * *item_size;
888
+ *data = *data_bytes == 0 ? NULL : pointer;
889
+ return 1;
890
+ }
891
+
892
+ static int napi_transform_parse_validity(
893
+ napi_env env, napi_value value, const uint8_t **data, size_t *len) {
894
+ napi_transform_bytes bytes = {0};
895
+ if (!napi_transform_parse_bytes(
896
+ env, value, 0, UINT64_MAX, UINT64_MAX, &bytes)) return 0;
897
+ *data = bytes.len == 0 ? NULL : bytes.data;
898
+ *len = bytes.len;
899
+ return 1;
900
+ }
901
+
902
+ static void napi_transform_table_clear(napi_transform_table_owner *owner) {
903
+ if (!owner) return;
904
+ free(owner->columns);
905
+ memset(owner, 0, sizeof(*owner));
906
+ }
907
+
908
+ static int napi_transform_parse_table(
909
+ napi_env env, napi_value value,
910
+ const napi_transform_handle *handle,
911
+ napi_transform_table_owner *owner, int *cancelled) {
912
+ napi_value rows_value;
913
+ napi_value columns_value;
914
+ bool is_array = false;
915
+ uint32_t count_u32 = 0;
916
+ size_t rows;
917
+ memset(owner, 0, sizeof(*owner));
918
+ if (cancelled) *cancelled = 0;
919
+ if (!napi_transform_check(
920
+ env, napi_get_named_property(env, value, "rows", &rows_value))
921
+ || !napi_transform_get_size(
922
+ env, rows_value, "table.rows must be a nonnegative safe integer", &rows)
923
+ || !napi_transform_check(
924
+ env, napi_get_named_property(env, value, "columns", &columns_value))
925
+ || !napi_transform_check(env, napi_is_array(env, columns_value, &is_array))
926
+ || !is_array
927
+ || !napi_transform_check(
928
+ env, napi_get_array_length(env, columns_value, &count_u32))) {
929
+ if (!is_array) napi_throw_type_error(env, NULL, "table.columns must be an array");
930
+ return 0;
931
+ }
932
+ if ((size_t)count_u32 != handle->column_count) {
933
+ napi_throw_range_error(env, NULL, "table column count mismatches schema");
934
+ return 0;
935
+ }
936
+ if ((uint64_t)handle->column_count > handle->limits.max_allocation_bytes
937
+ / sizeof(*owner->columns)) {
938
+ napi_transform_throw_code(
939
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
940
+ "table descriptors exceed configured allocation limit");
941
+ return 0;
942
+ }
943
+ owner->columns = (tf_column_view_v1 *)calloc(
944
+ handle->column_count, sizeof(*owner->columns));
945
+ if (!owner->columns) {
946
+ napi_throw_error(env, NULL, "table descriptor allocation failed");
947
+ return 0;
948
+ }
949
+ for (size_t i = 0; i < handle->column_count; ++i) {
950
+ napi_value config;
951
+ napi_value data_value;
952
+ napi_value validity_value;
953
+ napi_valuetype config_type;
954
+ bool direct_typed = false;
955
+ bool has_validity = false;
956
+ const void *data = NULL;
957
+ const uint8_t *validity = NULL;
958
+ size_t data_bytes = 0;
959
+ size_t validity_bytes = 0;
960
+ size_t item_size = 0;
961
+ size_t stride;
962
+ size_t bit_offset = 0;
963
+ size_t bit_stride = 0;
964
+ if (i % TF_TRANSFORM_CANCEL_ITERS_V1 == 0
965
+ && napi_transform_cancel_poll(handle->cancel)) {
966
+ if (cancelled) *cancelled = 1;
967
+ napi_transform_throw_code(
968
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
969
+ goto fail;
970
+ }
971
+ if (!napi_transform_check(
972
+ env, napi_get_element(env, columns_value, (uint32_t)i, &config))
973
+ || !napi_transform_check(env, napi_is_typedarray(
974
+ env, config, &direct_typed))) goto fail;
975
+ if (direct_typed) {
976
+ data_value = config;
977
+ } else {
978
+ if (!napi_transform_check(env, napi_typeof(
979
+ env, config, &config_type)) || config_type != napi_object) {
980
+ napi_throw_type_error(
981
+ env, NULL, "table columns must be typed arrays or objects");
982
+ goto fail;
983
+ }
984
+ if (!napi_transform_check(env, napi_get_named_property(
985
+ env, config, "data", &data_value))) goto fail;
986
+ }
987
+ if (!napi_transform_parse_numeric_array(
988
+ env, data_value, handle->dtypes[i],
989
+ &data, &data_bytes, &item_size)) goto fail;
990
+ stride = item_size;
991
+ if (!direct_typed) {
992
+ if (!napi_transform_get_named_size(
993
+ env, config, "strideBytes", item_size, 0, &stride)
994
+ || !napi_transform_check(env, napi_has_named_property(
995
+ env, config, "validity", &has_validity))) goto fail;
996
+ }
997
+ if (stride == 0) {
998
+ napi_throw_range_error(env, NULL, "table strideBytes must be positive");
999
+ goto fail;
1000
+ }
1001
+ if (has_validity) {
1002
+ if (!napi_transform_check(env, napi_get_named_property(
1003
+ env, config, "validity", &validity_value))
1004
+ || !napi_transform_parse_validity(
1005
+ env, validity_value, &validity, &validity_bytes)
1006
+ || !napi_transform_get_named_size(
1007
+ env, config, "validityBitOffset", 0, 0, &bit_offset)
1008
+ || !napi_transform_get_named_size(
1009
+ env, config, "validityBitStride", 1, 0, &bit_stride)) goto fail;
1010
+ if (bit_stride == 0) {
1011
+ napi_throw_range_error(
1012
+ env, NULL, "table validityBitStride must be positive");
1013
+ goto fail;
1014
+ }
1015
+ }
1016
+ owner->columns[i].abi_version = 1;
1017
+ owner->columns[i].struct_size = (uint32_t)sizeof(owner->columns[i]);
1018
+ owner->columns[i].data = data;
1019
+ owner->columns[i].data_bytes = data_bytes;
1020
+ owner->columns[i].stride_bytes = stride;
1021
+ owner->columns[i].validity = validity;
1022
+ owner->columns[i].validity_bytes = validity_bytes;
1023
+ owner->columns[i].validity_bit_offset = bit_offset;
1024
+ owner->columns[i].validity_bit_stride = bit_stride;
1025
+ }
1026
+ owner->view.abi_version = 1;
1027
+ owner->view.struct_size = (uint32_t)sizeof(owner->view);
1028
+ owner->view.row_count = rows;
1029
+ owner->view.column_count = handle->column_count;
1030
+ owner->view.columns = owner->columns;
1031
+ owner->view.columns_bytes = handle->column_count * sizeof(*owner->columns);
1032
+ if (napi_transform_cancel_poll(handle->cancel)) {
1033
+ if (cancelled) *cancelled = 1;
1034
+ napi_transform_throw_code(
1035
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1036
+ goto fail;
1037
+ }
1038
+ return 1;
1039
+ fail:
1040
+ napi_transform_table_clear(owner);
1041
+ return 0;
1042
+ }
1043
+
1044
+ static napi_value napi_transform_buffer_copy(
1045
+ napi_env env, const uint8_t *bytes, size_t len) {
1046
+ napi_value result = NULL;
1047
+ if (!napi_transform_check(
1048
+ env, napi_create_buffer_copy(env, len, bytes, NULL, &result))) return NULL;
1049
+ return result;
1050
+ }
1051
+
1052
+ static int napi_transform_prepare_dense_result(
1053
+ napi_env env, size_t rows, size_t columns,
1054
+ const tf_transform_limits_v1 *limits,
1055
+ const napi_transform_cancel *cancel, int *cancelled,
1056
+ napi_transform_dense_result_owner *owner) {
1057
+ napi_value rows_value = NULL;
1058
+ napi_value columns_value = NULL;
1059
+ napi_value arraybuffer = NULL;
1060
+ napi_value data = NULL;
1061
+ size_t elements;
1062
+ memset(owner, 0, sizeof(*owner));
1063
+ if (cancelled) *cancelled = 0;
1064
+ if (columns == 0 || rows > SIZE_MAX / columns) {
1065
+ napi_transform_throw_code(
1066
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
1067
+ "prepared-transform output shape exceeds host limits");
1068
+ return 0;
1069
+ }
1070
+ elements = rows * columns;
1071
+ if ((uint64_t)elements > limits->max_output_elements_per_call
1072
+ || elements > SIZE_MAX / sizeof(double)
1073
+ || (uint64_t)(elements * sizeof(double))
1074
+ > limits->max_allocation_bytes) {
1075
+ napi_transform_throw_code(
1076
+ env, TF_TRANSFORM_RESOURCE_LIMIT,
1077
+ "prepared-transform output exceeds configured limits");
1078
+ return 0;
1079
+ }
1080
+ owner->rows = rows;
1081
+ owner->columns = columns;
1082
+ owner->data_bytes = elements * sizeof(double);
1083
+ if (!napi_transform_check(env, napi_create_object(env, &owner->result))
1084
+ || !napi_transform_check(env, napi_create_double(
1085
+ env, (double)rows, &rows_value))
1086
+ || !napi_transform_check(env, napi_create_double(
1087
+ env, (double)columns, &columns_value))) return 0;
1088
+ if (napi_transform_cancel_poll((void *)cancel)) {
1089
+ if (cancelled) *cancelled = 1;
1090
+ napi_transform_throw_code(
1091
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1092
+ return 0;
1093
+ }
1094
+ if (!napi_transform_check(env, napi_create_arraybuffer(
1095
+ env, owner->data_bytes, &owner->data, &arraybuffer))
1096
+ || !napi_transform_check(env, napi_create_typedarray(
1097
+ env, napi_float64_array, elements, arraybuffer, 0, &data))) return 0;
1098
+ if (!napi_transform_check(env, napi_set_named_property(
1099
+ env, owner->result, "rows", rows_value))
1100
+ || !napi_transform_check(env, napi_set_named_property(
1101
+ env, owner->result, "columns", columns_value))
1102
+ || !napi_transform_check(env, napi_set_named_property(
1103
+ env, owner->result, "data", data))) return 0;
1104
+ return 1;
1105
+ }
1106
+
1107
+ static int napi_transform_copy_dense_result(
1108
+ napi_env env, napi_transform_dense_result_owner *owner,
1109
+ const tf_owned_dense_v1 *dense,
1110
+ const napi_transform_cancel *cancel) {
1111
+ size_t offset = 0;
1112
+ if (dense->dtype != TF_VIEW_FLOAT64
1113
+ || dense->rows != owner->rows
1114
+ || dense->columns != owner->columns
1115
+ || dense->data_bytes != owner->data_bytes
1116
+ || (dense->data_bytes != 0 && !dense->data)) {
1117
+ napi_throw_error(env, NULL, "prepared-transform dense output is invalid");
1118
+ return 0;
1119
+ }
1120
+ while (offset < dense->data_bytes) {
1121
+ size_t chunk = dense->data_bytes - offset;
1122
+ if (chunk > TF_TRANSFORM_CANCEL_BYTES_V1)
1123
+ chunk = TF_TRANSFORM_CANCEL_BYTES_V1;
1124
+ if (napi_transform_cancel_poll((void *)cancel)) {
1125
+ napi_transform_throw_code(
1126
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1127
+ return 0;
1128
+ }
1129
+ memcpy((uint8_t *)owner->data + offset,
1130
+ (const uint8_t *)dense->data + offset, chunk);
1131
+ offset += chunk;
1132
+ }
1133
+ if (napi_transform_cancel_poll((void *)cancel)) {
1134
+ napi_transform_throw_code(
1135
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1136
+ return 0;
1137
+ }
1138
+ return 1;
1139
+ }
1140
+
1141
+ static napi_value napi_transform_safe_limits(
1142
+ napi_env env, napi_callback_info info) {
1143
+ tf_transform_limits_v1 limits;
1144
+ napi_value result = NULL;
1145
+ (void)info;
1146
+ if (tf_transform_limits_init_safe_v1(&limits, sizeof(limits))
1147
+ != TF_TRANSFORM_OK
1148
+ || !napi_transform_check(env, napi_create_object(env, &result))) return NULL;
1149
+ for (size_t i = 0; i < sizeof(LIMIT_FIELDS) / sizeof(LIMIT_FIELDS[0]); ++i) {
1150
+ const uint64_t *field = (const uint64_t *)(
1151
+ (const uint8_t *)&limits + LIMIT_FIELDS[i].offset);
1152
+ napi_value value = NULL;
1153
+ if (*field > UINT64_C(9007199254740991)
1154
+ || !napi_transform_check(
1155
+ env, napi_create_double(env, (double)*field, &value))
1156
+ || !napi_transform_check(env, napi_set_named_property(
1157
+ env, result, LIMIT_FIELDS[i].name, value))) return NULL;
1158
+ }
1159
+ return result;
1160
+ }
1161
+
1162
+ static napi_value napi_transform_recipe_from_json(
1163
+ napi_env env, napi_callback_info info) {
1164
+ napi_value argv[2] = {0};
1165
+ size_t argc = 2;
1166
+ napi_transform_bytes bytes = {0};
1167
+ tf_transform_limits_v1 limits;
1168
+ tf_transform_recipe *recipe = NULL;
1169
+ tf_transform_error *error = NULL;
1170
+ tf_transform_code code;
1171
+ int provided;
1172
+ if (!napi_transform_check(
1173
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1174
+ if (argc < 1) {
1175
+ napi_throw_type_error(env, NULL, "transformRecipeFromJson requires bytes");
1176
+ return NULL;
1177
+ }
1178
+ if (!napi_transform_parse_limits(
1179
+ env, argc >= 2 ? argv[1] : NULL, &limits, &provided)
1180
+ || !napi_transform_parse_bytes(
1181
+ env, argv[0], 1,
1182
+ limits.max_recipe_bytes < limits.max_allocation_bytes
1183
+ ? limits.max_recipe_bytes : limits.max_allocation_bytes,
1184
+ limits.max_allocation_bytes,
1185
+ &bytes)) {
1186
+ napi_transform_bytes_clear(&bytes);
1187
+ return NULL;
1188
+ }
1189
+ code = tf_transform_recipe_from_json(
1190
+ bytes.data, bytes.len, provided ? &limits : NULL, &recipe, &error);
1191
+ napi_transform_bytes_clear(&bytes);
1192
+ if (code != TF_TRANSFORM_OK)
1193
+ return napi_transform_throw_error(env, code, &error);
1194
+ return napi_transform_new_handle(
1195
+ env, NAPI_TRANSFORM_RECIPE, recipe, NULL, 0);
1196
+ }
1197
+
1198
+ static napi_value napi_transform_analyzer_create(
1199
+ napi_env env, napi_callback_info info) {
1200
+ napi_value argv[3] = {0};
1201
+ size_t argc = 3;
1202
+ napi_transform_handle *recipe;
1203
+ napi_transform_schema_owner schema = {0};
1204
+ tf_transform_limits_v1 limits;
1205
+ tf_transform_runtime_v1 runtime;
1206
+ tf_transform_analyzer *analyzer = NULL;
1207
+ tf_transform_error *error = NULL;
1208
+ napi_transform_cancel *cancel = NULL;
1209
+ tf_transform_code code;
1210
+ int provided;
1211
+ if (!napi_transform_check(
1212
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1213
+ if (argc < 2) {
1214
+ napi_throw_type_error(env, NULL, "transformAnalyzerCreate requires recipe and schema");
1215
+ return NULL;
1216
+ }
1217
+ recipe = napi_transform_get_handle(env, argv[0], NAPI_TRANSFORM_RECIPE);
1218
+ if (!recipe) return NULL;
1219
+ if (!napi_transform_parse_runtime_options(
1220
+ env, argc >= 3 ? argv[2] : NULL,
1221
+ &limits, &provided, &cancel)) {
1222
+ napi_transform_cancel_destroy(env, &cancel);
1223
+ return NULL;
1224
+ }
1225
+ if (napi_transform_cancel_poll(cancel)) {
1226
+ napi_transform_cancel_destroy(env, &cancel);
1227
+ return napi_transform_throw_code(
1228
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1229
+ }
1230
+ if (!napi_transform_parse_schema(
1231
+ env, argv[1], &limits, cancel, &schema)) {
1232
+ napi_transform_schema_clear(&schema);
1233
+ napi_transform_cancel_destroy(env, &cancel);
1234
+ return NULL;
1235
+ }
1236
+ if (napi_transform_cancel_poll(cancel)) {
1237
+ napi_transform_schema_clear(&schema);
1238
+ napi_transform_cancel_destroy(env, &cancel);
1239
+ return napi_transform_throw_code(
1240
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1241
+ }
1242
+ napi_transform_runtime_init(&runtime, &limits, provided, cancel);
1243
+ code = tf_transform_analyzer_create(
1244
+ (tf_transform_recipe *)recipe->pointer, &schema.view,
1245
+ &runtime, &analyzer, &error);
1246
+ if (code != TF_TRANSFORM_OK) {
1247
+ napi_transform_schema_clear(&schema);
1248
+ napi_transform_cancel_destroy(env, &cancel);
1249
+ return napi_transform_throw_error(env, code, &error);
1250
+ }
1251
+ {
1252
+ napi_value result = napi_transform_new_handle_with_cancel(
1253
+ env, NAPI_TRANSFORM_ANALYZER, analyzer,
1254
+ schema.dtypes, schema.count, 0, &limits, cancel);
1255
+ napi_transform_schema_clear(&schema);
1256
+ return result;
1257
+ }
1258
+ }
1259
+
1260
+ static napi_value napi_transform_analyzer_push(
1261
+ napi_env env, napi_callback_info info) {
1262
+ napi_value argv[2] = {0};
1263
+ size_t argc = 2;
1264
+ napi_transform_handle *analyzer;
1265
+ napi_transform_table_owner table;
1266
+ tf_transform_error *error = NULL;
1267
+ tf_transform_code code;
1268
+ int cancelled = 0;
1269
+ if (!napi_transform_check(
1270
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1271
+ if (argc < 2) {
1272
+ napi_throw_type_error(env, NULL, "transformAnalyzerPush requires analyzer and table");
1273
+ return NULL;
1274
+ }
1275
+ analyzer = napi_transform_get_handle(env, argv[0], NAPI_TRANSFORM_ANALYZER);
1276
+ if (!analyzer) return NULL;
1277
+ if (napi_transform_cancel_poll(analyzer->cancel)) {
1278
+ napi_transform_destroy_payload(env, analyzer);
1279
+ return napi_transform_throw_code(
1280
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1281
+ }
1282
+ if (!napi_transform_parse_table(
1283
+ env, argv[1], analyzer, &table, &cancelled)) {
1284
+ if (cancelled) napi_transform_destroy_payload(env, analyzer);
1285
+ return NULL;
1286
+ }
1287
+ if (napi_transform_cancel_poll(analyzer->cancel)) {
1288
+ napi_transform_table_clear(&table);
1289
+ napi_transform_destroy_payload(env, analyzer);
1290
+ return napi_transform_throw_code(
1291
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1292
+ }
1293
+ code = tf_transform_analyzer_push(
1294
+ (tf_transform_analyzer *)analyzer->pointer, &table.view, &error);
1295
+ napi_transform_table_clear(&table);
1296
+ if (code != TF_TRANSFORM_OK) {
1297
+ if (code == TF_TRANSFORM_CANCELLED)
1298
+ napi_transform_destroy_payload(env, analyzer);
1299
+ return napi_transform_throw_error(env, code, &error);
1300
+ }
1301
+ return napi_transform_undefined(env);
1302
+ }
1303
+
1304
+ static napi_value napi_transform_analyzer_finalize(
1305
+ napi_env env, napi_callback_info info) {
1306
+ napi_value argv[1] = {0};
1307
+ size_t argc = 1;
1308
+ napi_transform_handle *analyzer;
1309
+ tf_transform_plan *plan = NULL;
1310
+ tf_transform_error *error = NULL;
1311
+ tf_transform_code code;
1312
+ if (!napi_transform_check(
1313
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1314
+ if (argc < 1) {
1315
+ napi_throw_type_error(env, NULL, "transformAnalyzerFinalize requires analyzer");
1316
+ return NULL;
1317
+ }
1318
+ analyzer = napi_transform_get_handle(env, argv[0], NAPI_TRANSFORM_ANALYZER);
1319
+ if (!analyzer) return NULL;
1320
+ code = tf_transform_analyzer_finalize(
1321
+ (tf_transform_analyzer *)analyzer->pointer, &plan, &error);
1322
+ if (code != TF_TRANSFORM_OK) {
1323
+ if (code == TF_TRANSFORM_CANCELLED)
1324
+ napi_transform_destroy_payload(env, analyzer);
1325
+ return napi_transform_throw_error(env, code, &error);
1326
+ }
1327
+ return napi_transform_new_handle(env, NAPI_TRANSFORM_PLAN, plan, NULL, 0);
1328
+ }
1329
+
1330
+ static napi_value napi_transform_plan_export(
1331
+ napi_env env, napi_callback_info info) {
1332
+ napi_value argv[2] = {0};
1333
+ size_t argc = 2;
1334
+ napi_transform_handle *plan;
1335
+ tf_transform_limits_v1 limits;
1336
+ uint8_t *bytes = NULL;
1337
+ size_t len = 0;
1338
+ tf_transform_error *error = NULL;
1339
+ tf_transform_code code;
1340
+ int provided;
1341
+ napi_value result;
1342
+ if (!napi_transform_check(
1343
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1344
+ if (argc < 1) {
1345
+ napi_throw_type_error(env, NULL, "transformPlanExport requires plan");
1346
+ return NULL;
1347
+ }
1348
+ plan = napi_transform_get_handle(env, argv[0], NAPI_TRANSFORM_PLAN);
1349
+ if (!plan || !napi_transform_parse_limits(
1350
+ env, argc >= 2 ? argv[1] : NULL, &limits, &provided)) return NULL;
1351
+ code = tf_transform_plan_export(
1352
+ (tf_transform_plan *)plan->pointer, provided ? &limits : NULL,
1353
+ &bytes, &len, &error);
1354
+ if (code != TF_TRANSFORM_OK)
1355
+ return napi_transform_throw_error(env, code, &error);
1356
+ result = napi_transform_buffer_copy(env, bytes, len);
1357
+ tf_transform_bytes_free(&bytes, &len);
1358
+ return result;
1359
+ }
1360
+
1361
+ static napi_value napi_transform_plan_import(
1362
+ napi_env env, napi_callback_info info) {
1363
+ napi_value argv[2] = {0};
1364
+ size_t argc = 2;
1365
+ napi_transform_bytes bytes = {0};
1366
+ tf_transform_limits_v1 limits;
1367
+ tf_transform_runtime_v1 runtime;
1368
+ tf_transform_plan *plan = NULL;
1369
+ tf_transform_error *error = NULL;
1370
+ napi_transform_cancel *cancel = NULL;
1371
+ tf_transform_code code;
1372
+ int provided;
1373
+ if (!napi_transform_check(
1374
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1375
+ if (argc < 1) {
1376
+ napi_throw_type_error(env, NULL, "transformPlanImport requires bytes");
1377
+ return NULL;
1378
+ }
1379
+ if (!napi_transform_parse_runtime_options(
1380
+ env, argc >= 2 ? argv[1] : NULL,
1381
+ &limits, &provided, &cancel)) {
1382
+ napi_transform_cancel_destroy(env, &cancel);
1383
+ return NULL;
1384
+ }
1385
+ if (napi_transform_cancel_poll(cancel)) {
1386
+ napi_transform_cancel_destroy(env, &cancel);
1387
+ return napi_transform_throw_code(
1388
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1389
+ }
1390
+ if (!napi_transform_parse_bytes(
1391
+ env, argv[0], 0,
1392
+ limits.max_plan_bytes < limits.max_allocation_bytes
1393
+ ? limits.max_plan_bytes : limits.max_allocation_bytes,
1394
+ limits.max_allocation_bytes,
1395
+ &bytes)) {
1396
+ napi_transform_bytes_clear(&bytes);
1397
+ napi_transform_cancel_destroy(env, &cancel);
1398
+ return NULL;
1399
+ }
1400
+ napi_transform_runtime_init(&runtime, &limits, provided, cancel);
1401
+ code = tf_transform_plan_import(
1402
+ bytes.data, bytes.len, &runtime, &plan, &error);
1403
+ napi_transform_bytes_clear(&bytes);
1404
+ napi_transform_cancel_destroy(env, &cancel);
1405
+ if (code != TF_TRANSFORM_OK)
1406
+ return napi_transform_throw_error(env, code, &error);
1407
+ return napi_transform_new_handle(env, NAPI_TRANSFORM_PLAN, plan, NULL, 0);
1408
+ }
1409
+
1410
+ static napi_value napi_transform_plan_schema_json(
1411
+ napi_env env, napi_callback_info info) {
1412
+ napi_value argv[3] = {0};
1413
+ size_t argc = 3;
1414
+ napi_transform_handle *plan;
1415
+ uint32_t which;
1416
+ tf_transform_limits_v1 limits;
1417
+ uint8_t *bytes = NULL;
1418
+ size_t len = 0;
1419
+ tf_transform_error *error = NULL;
1420
+ tf_transform_code code;
1421
+ int provided;
1422
+ napi_value result;
1423
+ if (!napi_transform_check(
1424
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1425
+ if (argc < 2) {
1426
+ napi_throw_type_error(env, NULL, "transformPlanSchemaJson requires plan and selector");
1427
+ return NULL;
1428
+ }
1429
+ plan = napi_transform_get_handle(env, argv[0], NAPI_TRANSFORM_PLAN);
1430
+ if (!plan
1431
+ || !napi_transform_check(env, napi_get_value_uint32(env, argv[1], &which))
1432
+ || !napi_transform_parse_limits(
1433
+ env, argc >= 3 ? argv[2] : NULL, &limits, &provided)) return NULL;
1434
+ code = tf_transform_plan_schema_json(
1435
+ (tf_transform_plan *)plan->pointer, which,
1436
+ provided ? &limits : NULL, &bytes, &len, &error);
1437
+ if (code != TF_TRANSFORM_OK)
1438
+ return napi_transform_throw_error(env, code, &error);
1439
+ result = napi_transform_buffer_copy(env, bytes, len);
1440
+ tf_transform_bytes_free(&bytes, &len);
1441
+ return result;
1442
+ }
1443
+
1444
+ static napi_value napi_transform_plan_recipe_sha256(
1445
+ napi_env env, napi_callback_info info) {
1446
+ napi_value argv[2] = {0};
1447
+ size_t argc = 2;
1448
+ napi_transform_handle *plan;
1449
+ tf_transform_limits_v1 limits;
1450
+ uint8_t fingerprint[65];
1451
+ tf_transform_error *error = NULL;
1452
+ tf_transform_code code;
1453
+ int provided;
1454
+ napi_value result;
1455
+ if (!napi_transform_check(
1456
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1457
+ if (argc < 1) {
1458
+ napi_throw_type_error(env, NULL, "transformPlanRecipeSha256 requires a plan");
1459
+ return NULL;
1460
+ }
1461
+ plan = napi_transform_get_handle(env, argv[0], NAPI_TRANSFORM_PLAN);
1462
+ if (!plan || !napi_transform_parse_limits(
1463
+ env, argc >= 2 ? argv[1] : NULL, &limits, &provided)) return NULL;
1464
+ code = tf_transform_plan_recipe_sha256(
1465
+ (tf_transform_plan *)plan->pointer,
1466
+ provided ? &limits : NULL, fingerprint, &error);
1467
+ if (code != TF_TRANSFORM_OK)
1468
+ return napi_transform_throw_error(env, code, &error);
1469
+ if (!napi_transform_check(env, napi_create_string_utf8(
1470
+ env, (const char *)fingerprint, 64, &result))) return NULL;
1471
+ return result;
1472
+ }
1473
+
1474
+ static napi_value napi_transform_apply_create(
1475
+ napi_env env, napi_callback_info info) {
1476
+ napi_value argv[3] = {0};
1477
+ size_t argc = 3;
1478
+ napi_transform_handle *plan;
1479
+ napi_transform_schema_owner schema = {0};
1480
+ tf_transform_limits_v1 limits;
1481
+ tf_transform_runtime_v1 runtime;
1482
+ tf_transform_apply *apply = NULL;
1483
+ const tf_transform_schema *output_schema = NULL;
1484
+ size_t output_column_count = 0;
1485
+ tf_transform_error *error = NULL;
1486
+ napi_transform_cancel *cancel = NULL;
1487
+ tf_transform_code code;
1488
+ int provided;
1489
+ if (!napi_transform_check(
1490
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1491
+ if (argc < 2) {
1492
+ napi_throw_type_error(env, NULL, "transformApplyCreate requires plan and schema");
1493
+ return NULL;
1494
+ }
1495
+ plan = napi_transform_get_handle(env, argv[0], NAPI_TRANSFORM_PLAN);
1496
+ if (!plan) return NULL;
1497
+ if (!napi_transform_parse_runtime_options(
1498
+ env, argc >= 3 ? argv[2] : NULL,
1499
+ &limits, &provided, &cancel)) {
1500
+ napi_transform_cancel_destroy(env, &cancel);
1501
+ return NULL;
1502
+ }
1503
+ if (napi_transform_cancel_poll(cancel)) {
1504
+ napi_transform_cancel_destroy(env, &cancel);
1505
+ return napi_transform_throw_code(
1506
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1507
+ }
1508
+ if (!napi_transform_parse_schema(
1509
+ env, argv[1], &limits, cancel, &schema)) {
1510
+ napi_transform_schema_clear(&schema);
1511
+ napi_transform_cancel_destroy(env, &cancel);
1512
+ return NULL;
1513
+ }
1514
+ if (napi_transform_cancel_poll(cancel)) {
1515
+ napi_transform_schema_clear(&schema);
1516
+ napi_transform_cancel_destroy(env, &cancel);
1517
+ return napi_transform_throw_code(
1518
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1519
+ }
1520
+ napi_transform_runtime_init(&runtime, &limits, provided, cancel);
1521
+ code = tf_transform_plan_schema(
1522
+ (tf_transform_plan *)plan->pointer, TF_TRANSFORM_SCHEMA_OUTPUT,
1523
+ &output_schema, &error);
1524
+ if (code != TF_TRANSFORM_OK) {
1525
+ napi_transform_schema_clear(&schema);
1526
+ napi_transform_cancel_destroy(env, &cancel);
1527
+ return napi_transform_throw_error(env, code, &error);
1528
+ }
1529
+ output_column_count = tf_transform_schema_field_count(output_schema);
1530
+ code = tf_transform_apply_create(
1531
+ (tf_transform_plan *)plan->pointer, &schema.view,
1532
+ &runtime, &apply, &error);
1533
+ if (code != TF_TRANSFORM_OK) {
1534
+ napi_transform_schema_clear(&schema);
1535
+ napi_transform_cancel_destroy(env, &cancel);
1536
+ return napi_transform_throw_error(env, code, &error);
1537
+ }
1538
+ {
1539
+ napi_value result = napi_transform_new_handle_with_cancel(
1540
+ env, NAPI_TRANSFORM_APPLY, apply,
1541
+ schema.dtypes, schema.count, output_column_count, &limits, cancel);
1542
+ napi_transform_schema_clear(&schema);
1543
+ return result;
1544
+ }
1545
+ }
1546
+
1547
+ static napi_value napi_transform_apply_run(
1548
+ napi_env env, napi_callback_info info) {
1549
+ napi_value argv[2] = {0};
1550
+ size_t argc = 2;
1551
+ napi_transform_handle *apply;
1552
+ napi_transform_table_owner table;
1553
+ napi_transform_dense_result_owner result_owner;
1554
+ tf_owned_dense_v1 dense;
1555
+ tf_transform_error *error = NULL;
1556
+ tf_transform_code code;
1557
+ int cancelled = 0;
1558
+ memset(&dense, 0, sizeof(dense));
1559
+ if (!napi_transform_check(
1560
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1561
+ if (argc < 2) {
1562
+ napi_throw_type_error(env, NULL, "transformApplyRun requires apply and table");
1563
+ return NULL;
1564
+ }
1565
+ apply = napi_transform_get_handle(env, argv[0], NAPI_TRANSFORM_APPLY);
1566
+ if (!apply) return NULL;
1567
+ if (napi_transform_cancel_poll(apply->cancel)) {
1568
+ napi_transform_destroy_payload(env, apply);
1569
+ return napi_transform_throw_code(
1570
+ env, TF_TRANSFORM_CANCELLED, "prepared transform cancelled");
1571
+ }
1572
+ if (!napi_transform_parse_table(
1573
+ env, argv[1], apply, &table, &cancelled)) {
1574
+ if (cancelled) napi_transform_destroy_payload(env, apply);
1575
+ return NULL;
1576
+ }
1577
+ if (!napi_transform_prepare_dense_result(
1578
+ env, table.view.row_count, apply->output_column_count,
1579
+ &apply->limits, apply->cancel, &cancelled, &result_owner)) {
1580
+ napi_transform_table_clear(&table);
1581
+ if (cancelled) napi_transform_destroy_payload(env, apply);
1582
+ return NULL;
1583
+ }
1584
+ code = tf_transform_apply_run(
1585
+ (tf_transform_apply *)apply->pointer, &table.view, &dense, &error);
1586
+ napi_transform_table_clear(&table);
1587
+ if (code != TF_TRANSFORM_OK) {
1588
+ if (code == TF_TRANSFORM_CANCELLED)
1589
+ napi_transform_destroy_payload(env, apply);
1590
+ return napi_transform_throw_error(env, code, &error);
1591
+ }
1592
+ if (!napi_transform_copy_dense_result(
1593
+ env, &result_owner, &dense, apply->cancel)) {
1594
+ tf_owned_dense_free(&dense);
1595
+ napi_transform_destroy_payload(env, apply);
1596
+ return NULL;
1597
+ }
1598
+ tf_owned_dense_free(&dense);
1599
+ return result_owner.result;
1600
+ }
1601
+
1602
+ static napi_value napi_transform_dispose(
1603
+ napi_env env, napi_callback_info info) {
1604
+ napi_value argv[1] = {0};
1605
+ size_t argc = 1;
1606
+ napi_transform_handle *handle = NULL;
1607
+ if (!napi_transform_check(
1608
+ env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL))) return NULL;
1609
+ if (argc < 1) {
1610
+ napi_throw_type_error(env, NULL, "transformDispose requires a transform handle");
1611
+ return NULL;
1612
+ }
1613
+ handle = napi_transform_unwrap_handle(env, argv[0]);
1614
+ if (!handle) return NULL;
1615
+ napi_transform_destroy_payload(env, handle);
1616
+ return napi_transform_undefined(env);
1617
+ }
1618
+
1619
+ napi_status tranfi_napi_define_transform(napi_env env, napi_value exports) {
1620
+ napi_property_descriptor properties[] = {
1621
+ {"transformSafeLimits", NULL, napi_transform_safe_limits,
1622
+ NULL, NULL, NULL, napi_default, NULL},
1623
+ {"transformRecipeFromJson", NULL, napi_transform_recipe_from_json,
1624
+ NULL, NULL, NULL, napi_default, NULL},
1625
+ {"transformAnalyzerCreate", NULL, napi_transform_analyzer_create,
1626
+ NULL, NULL, NULL, napi_default, NULL},
1627
+ {"transformAnalyzerPush", NULL, napi_transform_analyzer_push,
1628
+ NULL, NULL, NULL, napi_default, NULL},
1629
+ {"transformAnalyzerFinalize", NULL, napi_transform_analyzer_finalize,
1630
+ NULL, NULL, NULL, napi_default, NULL},
1631
+ {"transformPlanExport", NULL, napi_transform_plan_export,
1632
+ NULL, NULL, NULL, napi_default, NULL},
1633
+ {"transformPlanImport", NULL, napi_transform_plan_import,
1634
+ NULL, NULL, NULL, napi_default, NULL},
1635
+ {"transformPlanSchemaJson", NULL, napi_transform_plan_schema_json,
1636
+ NULL, NULL, NULL, napi_default, NULL},
1637
+ {"transformPlanRecipeSha256", NULL, napi_transform_plan_recipe_sha256,
1638
+ NULL, NULL, NULL, napi_default, NULL},
1639
+ {"transformApplyCreate", NULL, napi_transform_apply_create,
1640
+ NULL, NULL, NULL, napi_default, NULL},
1641
+ {"transformApplyRun", NULL, napi_transform_apply_run,
1642
+ NULL, NULL, NULL, napi_default, NULL},
1643
+ {"transformDispose", NULL, napi_transform_dispose,
1644
+ NULL, NULL, NULL, napi_default, NULL},
1645
+ };
1646
+ return napi_define_properties(
1647
+ env, exports, sizeof(properties) / sizeof(properties[0]), properties);
1648
+ }