tranfi 0.1.2 → 0.2.1

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 +443 -51
  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 +352 -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 +81 -41
  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,1373 @@
1
+ /* Checked standalone wasm32 shim for the generic prepared-transform API. */
2
+
3
+ #include "transform_wasm.h"
4
+
5
+ #include "transform.h"
6
+ #include "transform_internal.h"
7
+
8
+ #include <stdatomic.h>
9
+ #include <stddef.h>
10
+ #include <stdint.h>
11
+ #include <stdlib.h>
12
+ #include <string.h>
13
+
14
+ #ifdef __EMSCRIPTEN__
15
+ #include <emscripten.h>
16
+ #include <emscripten/heap.h>
17
+ #include <emscripten/em_js.h>
18
+ #define TF_WASM_EXPORT EMSCRIPTEN_KEEPALIVE
19
+ EM_JS(int, tf_wasm_transform_host_cancel_poll, (uint32_t slot), {
20
+ var poll = Module['tfTransformCancelPoll'];
21
+ return typeof poll === 'function' && poll(slot) ? 1 : 0;
22
+ });
23
+ #else
24
+ #define TF_WASM_EXPORT
25
+ static uint8_t *tf_wasm_test_heap_base;
26
+ static uint32_t tf_wasm_test_heap_size;
27
+ static int tf_wasm_transform_host_cancel_poll(uint32_t slot) {
28
+ (void)slot;
29
+ return 0;
30
+ }
31
+ void tf_wasm_transform_test_set_heap(void *base, uint32_t size) {
32
+ tf_wasm_test_heap_base = (uint8_t *)base;
33
+ tf_wasm_test_heap_size = size;
34
+ }
35
+ #endif
36
+
37
+ _Static_assert(sizeof(tf_wasm_field_view_v1) == 32,
38
+ "wasm32 field descriptor drift");
39
+ _Static_assert(sizeof(tf_wasm_schema_view_v1) == 20,
40
+ "wasm32 schema descriptor drift");
41
+ _Static_assert(sizeof(tf_wasm_column_view_v1) == 36,
42
+ "wasm32 column descriptor drift");
43
+ _Static_assert(sizeof(tf_wasm_table_view_v1) == 24,
44
+ "wasm32 table descriptor drift");
45
+ _Static_assert(sizeof(tf_wasm_dense_info_v1) == 28,
46
+ "wasm32 dense info drift");
47
+ _Static_assert(sizeof(tf_wasm_schema_field_info_v1) == 20,
48
+ "wasm32 schema field info drift");
49
+ _Static_assert(sizeof(tf_transform_limits_v1) == 184,
50
+ "transform limits wire layout drift");
51
+
52
+ #define TF_WASM_SLOT_COUNT 65536u
53
+ #define TF_WASM_TYPE_SHIFT 28u
54
+ #define TF_WASM_GENERATION_SHIFT 16u
55
+ #define TF_WASM_TYPE_MASK 0x0fu
56
+ #define TF_WASM_GENERATION_MASK 0x0fffu
57
+ #define TF_WASM_SLOT_MASK 0xffffu
58
+
59
+ typedef struct tf_wasm_cancel_token {
60
+ atomic_uint references;
61
+ atomic_int requested;
62
+ uint32_t host_poll_slot;
63
+ } tf_wasm_cancel_token;
64
+
65
+ typedef struct tf_wasm_owned_bytes {
66
+ uint8_t *data;
67
+ size_t len;
68
+ } tf_wasm_owned_bytes;
69
+
70
+ typedef struct tf_wasm_owned_dense {
71
+ tf_owned_dense_v1 value;
72
+ } tf_wasm_owned_dense;
73
+
74
+ typedef struct tf_wasm_handle_slot {
75
+ void *pointer;
76
+ tf_wasm_cancel_token *token;
77
+ uint32_t *dtypes;
78
+ uint64_t max_live_handles;
79
+ uint64_t max_retired_slots;
80
+ uint64_t max_allocation_bytes;
81
+ size_t column_count;
82
+ uint16_t generation;
83
+ uint16_t next_free;
84
+ uint8_t type;
85
+ uint8_t retired;
86
+ } tf_wasm_handle_slot;
87
+
88
+ typedef struct tf_wasm_schema_owner {
89
+ tf_schema_view_v1 view;
90
+ tf_field_view_v1 *fields;
91
+ uint32_t *dtypes;
92
+ size_t count;
93
+ } tf_wasm_schema_owner;
94
+
95
+ typedef struct tf_wasm_table_owner {
96
+ tf_table_view_v1 view;
97
+ tf_column_view_v1 *columns;
98
+ } tf_wasm_table_owner;
99
+
100
+ static tf_wasm_handle_slot tf_wasm_slots[TF_WASM_SLOT_COUNT];
101
+ static uint32_t tf_wasm_next_virgin_slot = 1;
102
+ static uint16_t tf_wasm_free_slot_head;
103
+ static uint64_t tf_wasm_live_handles;
104
+ static uint64_t tf_wasm_retired_slots;
105
+
106
+ static int tf_wasm_has_available_slot(void) {
107
+ return tf_wasm_free_slot_head != 0
108
+ || tf_wasm_next_virgin_slot < TF_WASM_SLOT_COUNT;
109
+ }
110
+
111
+ static uint32_t tf_wasm_take_available_slot(void) {
112
+ uint32_t index;
113
+ if (tf_wasm_free_slot_head != 0) {
114
+ index = tf_wasm_free_slot_head;
115
+ tf_wasm_free_slot_head = tf_wasm_slots[index].next_free;
116
+ tf_wasm_slots[index].next_free = 0;
117
+ return index;
118
+ }
119
+ if (tf_wasm_next_virgin_slot >= TF_WASM_SLOT_COUNT) return 0;
120
+ index = tf_wasm_next_virgin_slot;
121
+ ++tf_wasm_next_virgin_slot;
122
+ return index;
123
+ }
124
+
125
+ static tf_transform_code tf_wasm_preflight_handle(
126
+ const tf_transform_limits_v1 *limits) {
127
+ if (!limits) return TF_TRANSFORM_INVALID_ARGUMENT;
128
+ if (tf_wasm_live_handles >= limits->max_live_handles
129
+ || tf_wasm_retired_slots > limits->max_retired_handle_slots
130
+ || !tf_wasm_has_available_slot())
131
+ return TF_TRANSFORM_RESOURCE_LIMIT;
132
+ return TF_TRANSFORM_OK;
133
+ }
134
+
135
+ static uint64_t tf_wasm_heap_size(void) {
136
+ #ifdef __EMSCRIPTEN__
137
+ return (uint64_t)emscripten_get_heap_size();
138
+ #else
139
+ return (uint64_t)tf_wasm_test_heap_size;
140
+ #endif
141
+ }
142
+
143
+ static uint8_t *tf_wasm_heap_span(uint32_t offset, uint32_t len) {
144
+ uint64_t end;
145
+ if (len == 0) {
146
+ if (offset == 0) return NULL;
147
+ end = (uint64_t)offset;
148
+ } else {
149
+ if (offset == 0) return NULL;
150
+ end = (uint64_t)offset + (uint64_t)len;
151
+ }
152
+ if (end > tf_wasm_heap_size()) return NULL;
153
+ #ifdef __EMSCRIPTEN__
154
+ return (uint8_t *)(uintptr_t)offset;
155
+ #else
156
+ if (!tf_wasm_test_heap_base) return NULL;
157
+ return tf_wasm_test_heap_base + offset;
158
+ #endif
159
+ }
160
+
161
+ static int tf_wasm_read(uint32_t offset, void *out, uint32_t len) {
162
+ uint8_t *source;
163
+ if (len == 0) return 1;
164
+ source = tf_wasm_heap_span(offset, len);
165
+ if (!source || !out) return 0;
166
+ memcpy(out, source, len);
167
+ return 1;
168
+ }
169
+
170
+ static int tf_wasm_write(uint32_t offset, const void *value, uint32_t len) {
171
+ uint8_t *destination;
172
+ if (len == 0) return 1;
173
+ destination = tf_wasm_heap_span(offset, len);
174
+ if (!destination || !value) return 0;
175
+ memcpy(destination, value, len);
176
+ return 1;
177
+ }
178
+
179
+ static int tf_wasm_write_u32(uint32_t offset, uint32_t value) {
180
+ return tf_wasm_write(offset, &value, (uint32_t)sizeof(value));
181
+ }
182
+
183
+ static int tf_wasm_spans_overlap(
184
+ uint32_t first, uint32_t first_len,
185
+ uint32_t second, uint32_t second_len) {
186
+ uint64_t first_end = (uint64_t)first + first_len;
187
+ uint64_t second_end = (uint64_t)second + second_len;
188
+ return first_len != 0 && second_len != 0
189
+ && (uint64_t)first < second_end && (uint64_t)second < first_end;
190
+ }
191
+
192
+ static tf_transform_code tf_wasm_prepare_error(uint32_t error_offset) {
193
+ if (!tf_wasm_heap_span(error_offset, 4))
194
+ return TF_TRANSFORM_INVALID_ARGUMENT;
195
+ return tf_wasm_write_u32(error_offset, 0)
196
+ ? TF_TRANSFORM_OK : TF_TRANSFORM_INVALID_ARGUMENT;
197
+ }
198
+
199
+ static tf_transform_code tf_wasm_prepare_outputs(
200
+ uint32_t output_offset, uint32_t error_offset) {
201
+ if (tf_wasm_spans_overlap(output_offset, 4, error_offset, 4)
202
+ || !tf_wasm_heap_span(output_offset, 4)
203
+ || !tf_wasm_heap_span(error_offset, 4))
204
+ return TF_TRANSFORM_INVALID_ARGUMENT;
205
+ if (!tf_wasm_write_u32(output_offset, 0)
206
+ || !tf_wasm_write_u32(error_offset, 0))
207
+ return TF_TRANSFORM_INVALID_ARGUMENT;
208
+ return TF_TRANSFORM_OK;
209
+ }
210
+
211
+ static uint32_t tf_wasm_make_handle(
212
+ uint8_t type, uint16_t generation, uint16_t slot) {
213
+ return ((uint32_t)type << TF_WASM_TYPE_SHIFT)
214
+ | ((uint32_t)generation << TF_WASM_GENERATION_SHIFT)
215
+ | (uint32_t)slot;
216
+ }
217
+
218
+ static tf_wasm_handle_slot *tf_wasm_get_slot(
219
+ uint32_t handle, uint8_t expected_type) {
220
+ uint8_t type = (uint8_t)((handle >> TF_WASM_TYPE_SHIFT) & TF_WASM_TYPE_MASK);
221
+ uint16_t generation = (uint16_t)(
222
+ (handle >> TF_WASM_GENERATION_SHIFT) & TF_WASM_GENERATION_MASK);
223
+ uint16_t index = (uint16_t)(handle & TF_WASM_SLOT_MASK);
224
+ tf_wasm_handle_slot *slot;
225
+ if (handle == 0 || index == 0 || generation == 0 || type == 0)
226
+ return NULL;
227
+ if (expected_type != 0 && type != expected_type) return NULL;
228
+ slot = &tf_wasm_slots[index];
229
+ if (!slot->pointer || slot->retired || slot->type != type
230
+ || slot->generation != generation) return NULL;
231
+ return slot;
232
+ }
233
+
234
+ static void tf_wasm_token_retain(tf_wasm_cancel_token *token) {
235
+ if (token) (void)atomic_fetch_add_explicit(
236
+ &token->references, 1u, memory_order_relaxed);
237
+ }
238
+
239
+ static void tf_wasm_token_release(tf_wasm_cancel_token *token) {
240
+ if (token && atomic_fetch_sub_explicit(
241
+ &token->references, 1u, memory_order_acq_rel) == 1u) free(token);
242
+ }
243
+
244
+ static int tf_wasm_cancel_poll(void *user) {
245
+ tf_wasm_cancel_token *token = (tf_wasm_cancel_token *)user;
246
+ if (!token) return 0;
247
+ if (atomic_load_explicit(&token->requested, memory_order_acquire) != 0)
248
+ return 1;
249
+ return token->host_poll_slot != TF_WASM_TRANSFORM_NO_HOST_CANCEL
250
+ && tf_wasm_transform_host_cancel_poll(token->host_poll_slot) != 0;
251
+ }
252
+
253
+ static void tf_wasm_destroy_pointer(uint8_t type, void *pointer) {
254
+ if (!pointer) return;
255
+ if (type == TF_WASM_HANDLE_RECIPE) {
256
+ tf_transform_recipe *value = (tf_transform_recipe *)pointer;
257
+ tf_transform_recipe_destroy(&value);
258
+ } else if (type == TF_WASM_HANDLE_ANALYZER) {
259
+ tf_transform_analyzer *value = (tf_transform_analyzer *)pointer;
260
+ tf_transform_analyzer_destroy(&value);
261
+ } else if (type == TF_WASM_HANDLE_PLAN) {
262
+ tf_transform_plan *value = (tf_transform_plan *)pointer;
263
+ tf_transform_plan_destroy(&value);
264
+ } else if (type == TF_WASM_HANDLE_APPLY) {
265
+ tf_transform_apply *value = (tf_transform_apply *)pointer;
266
+ tf_transform_apply_destroy(&value);
267
+ } else if (type == TF_WASM_HANDLE_ERROR) {
268
+ tf_transform_error *value = (tf_transform_error *)pointer;
269
+ tf_transform_error_destroy(&value);
270
+ } else if (type == TF_WASM_HANDLE_OWNED_BYTES) {
271
+ tf_wasm_owned_bytes *value = (tf_wasm_owned_bytes *)pointer;
272
+ tf_transform_bytes_free(&value->data, &value->len);
273
+ free(value);
274
+ } else if (type == TF_WASM_HANDLE_OWNED_DENSE) {
275
+ tf_wasm_owned_dense *value = (tf_wasm_owned_dense *)pointer;
276
+ tf_owned_dense_free(&value->value);
277
+ free(value);
278
+ } else if (type == TF_WASM_HANDLE_CANCEL_TOKEN) {
279
+ tf_wasm_token_release((tf_wasm_cancel_token *)pointer);
280
+ }
281
+ }
282
+
283
+ static tf_transform_code tf_wasm_install_handle(
284
+ uint8_t type, void *pointer, tf_wasm_cancel_token *token,
285
+ const uint32_t *dtypes, size_t column_count,
286
+ const tf_transform_limits_v1 *limits, uint32_t *out) {
287
+ uint32_t *dtype_copy = NULL;
288
+ uint32_t index = 0;
289
+ if (!pointer || !limits || !out || type == 0 || type > 8)
290
+ return TF_TRANSFORM_INVALID_ARGUMENT;
291
+ if (tf_wasm_live_handles >= limits->max_live_handles
292
+ || tf_wasm_retired_slots > limits->max_retired_handle_slots)
293
+ return TF_TRANSFORM_RESOURCE_LIMIT;
294
+ if (column_count != 0) {
295
+ uint64_t bytes = (uint64_t)column_count * sizeof(*dtype_copy);
296
+ if (!dtypes || bytes > SIZE_MAX || bytes > limits->max_allocation_bytes)
297
+ return TF_TRANSFORM_RESOURCE_LIMIT;
298
+ dtype_copy = (uint32_t *)malloc((size_t)bytes);
299
+ if (!dtype_copy) return TF_TRANSFORM_ALLOCATION;
300
+ memcpy(dtype_copy, dtypes, (size_t)bytes);
301
+ }
302
+ index = tf_wasm_take_available_slot();
303
+ if (index == 0) {
304
+ free(dtype_copy);
305
+ return TF_TRANSFORM_RESOURCE_LIMIT;
306
+ }
307
+ tf_wasm_handle_slot *slot = &tf_wasm_slots[index];
308
+ if (slot->generation == 0) slot->generation = 1;
309
+ slot->pointer = pointer;
310
+ slot->token = token;
311
+ slot->dtypes = dtype_copy;
312
+ slot->column_count = column_count;
313
+ slot->type = type;
314
+ slot->max_live_handles = limits->max_live_handles;
315
+ slot->max_retired_slots = limits->max_retired_handle_slots;
316
+ slot->max_allocation_bytes = limits->max_allocation_bytes;
317
+ tf_wasm_token_retain(token);
318
+ ++tf_wasm_live_handles;
319
+ *out = tf_wasm_make_handle(type, slot->generation, (uint16_t)index);
320
+ return TF_TRANSFORM_OK;
321
+ }
322
+
323
+ static tf_transform_code tf_wasm_read_limits(
324
+ uint32_t offset, tf_transform_limits_v1 *out,
325
+ tf_transform_error **error) {
326
+ tf_transform_limits_v1 raw;
327
+ if (offset == 0) return tf_transform_limits_init_safe_v1(out, sizeof(*out));
328
+ if (!tf_wasm_read(offset, &raw, (uint32_t)sizeof(raw)))
329
+ return TF_TRANSFORM_INVALID_ARGUMENT;
330
+ return tf_transform_copy_limits(&raw, out, error);
331
+ }
332
+
333
+ static tf_transform_limits_v1 tf_wasm_safe_limits(void) {
334
+ tf_transform_limits_v1 limits;
335
+ memset(&limits, 0, sizeof(limits));
336
+ (void)tf_transform_limits_init_safe_v1(&limits, sizeof(limits));
337
+ return limits;
338
+ }
339
+
340
+ static tf_transform_code tf_wasm_transfer_error(
341
+ tf_transform_error **error, const tf_transform_limits_v1 *limits,
342
+ uint32_t error_offset, tf_transform_code code) {
343
+ uint32_t handle = 0;
344
+ tf_transform_code install_code;
345
+ if (!error || !*error) return code;
346
+ install_code = tf_wasm_install_handle(
347
+ TF_WASM_HANDLE_ERROR, *error, NULL, NULL, 0, limits, &handle);
348
+ if (install_code != TF_TRANSFORM_OK) {
349
+ tf_transform_error_destroy(error);
350
+ return code;
351
+ }
352
+ *error = NULL;
353
+ if (!tf_wasm_write_u32(error_offset, handle)) {
354
+ (void)tf_wasm_transform_handle_destroy(handle);
355
+ return TF_TRANSFORM_INVALID_ARGUMENT;
356
+ }
357
+ return code;
358
+ }
359
+
360
+ static tf_transform_code tf_wasm_transfer_error_safe(
361
+ tf_transform_error **error, uint32_t error_offset,
362
+ tf_transform_code code) {
363
+ tf_transform_limits_v1 limits = tf_wasm_safe_limits();
364
+ return tf_wasm_transfer_error(error, &limits, error_offset, code);
365
+ }
366
+
367
+ static tf_wasm_cancel_token *tf_wasm_optional_token(uint32_t handle) {
368
+ tf_wasm_handle_slot *slot;
369
+ if (handle == 0) return NULL;
370
+ slot = tf_wasm_get_slot(handle, TF_WASM_HANDLE_CANCEL_TOKEN);
371
+ return slot ? (tf_wasm_cancel_token *)slot->pointer : NULL;
372
+ }
373
+
374
+ static void tf_wasm_runtime_init(
375
+ tf_transform_runtime_v1 *runtime,
376
+ const tf_transform_limits_v1 *limits, tf_wasm_cancel_token *token) {
377
+ memset(runtime, 0, sizeof(*runtime));
378
+ runtime->abi_version = 1;
379
+ runtime->struct_size = (uint32_t)sizeof(*runtime);
380
+ runtime->limits = limits;
381
+ if (token) {
382
+ runtime->cancel = tf_wasm_cancel_poll;
383
+ runtime->cancel_user = token;
384
+ }
385
+ }
386
+
387
+ static void tf_wasm_schema_clear(tf_wasm_schema_owner *owner) {
388
+ if (!owner) return;
389
+ free(owner->fields);
390
+ free(owner->dtypes);
391
+ memset(owner, 0, sizeof(*owner));
392
+ }
393
+
394
+ static tf_transform_code tf_wasm_parse_schema(
395
+ uint32_t offset, const tf_transform_limits_v1 *limits,
396
+ tf_wasm_cancel_token *token, tf_wasm_schema_owner *owner) {
397
+ tf_wasm_schema_view_v1 wire;
398
+ uint64_t expected_bytes;
399
+ memset(owner, 0, sizeof(*owner));
400
+ if (!tf_wasm_read(offset, &wire, sizeof(wire))
401
+ || wire.abi_version != 1 || wire.struct_size != sizeof(wire)
402
+ || wire.column_count == 0)
403
+ return TF_TRANSFORM_INVALID_ARGUMENT;
404
+ if ((uint64_t)wire.column_count > limits->max_input_columns)
405
+ return TF_TRANSFORM_RESOURCE_LIMIT;
406
+ expected_bytes = (uint64_t)wire.column_count * sizeof(tf_wasm_field_view_v1);
407
+ if (expected_bytes != wire.fields_bytes
408
+ || !tf_wasm_heap_span(wire.fields_offset, wire.fields_bytes))
409
+ return TF_TRANSFORM_INVALID_ARGUMENT;
410
+ if (expected_bytes > limits->max_allocation_bytes)
411
+ return TF_TRANSFORM_RESOURCE_LIMIT;
412
+ owner->count = wire.column_count;
413
+ if ((uint64_t)owner->count * sizeof(*owner->fields)
414
+ > limits->max_allocation_bytes
415
+ || (uint64_t)owner->count * sizeof(*owner->dtypes)
416
+ > limits->max_allocation_bytes)
417
+ return TF_TRANSFORM_RESOURCE_LIMIT;
418
+ owner->fields = (tf_field_view_v1 *)calloc(
419
+ owner->count, sizeof(*owner->fields));
420
+ owner->dtypes = (uint32_t *)calloc(owner->count, sizeof(*owner->dtypes));
421
+ if (!owner->fields || !owner->dtypes) {
422
+ tf_wasm_schema_clear(owner);
423
+ return TF_TRANSFORM_ALLOCATION;
424
+ }
425
+ for (size_t i = 0; i < owner->count; ++i) {
426
+ tf_wasm_field_view_v1 field;
427
+ uint32_t field_offset = (uint32_t)(
428
+ (uint64_t)wire.fields_offset + i * sizeof(field));
429
+ uint8_t *id;
430
+ uint8_t *name;
431
+ if (i % TF_TRANSFORM_CANCEL_ITERS_V1 == 0
432
+ && tf_wasm_cancel_poll(token)) {
433
+ tf_wasm_schema_clear(owner);
434
+ return TF_TRANSFORM_CANCELLED;
435
+ }
436
+ if (!tf_wasm_read(field_offset, &field, sizeof(field))
437
+ || field.abi_version != 1 || field.struct_size != sizeof(field)
438
+ || field.flags != 0
439
+ || (field.dtype != TF_VIEW_FLOAT32
440
+ && field.dtype != TF_VIEW_FLOAT64)
441
+ || field.id_bytes == 0 || field.name_bytes == 0)
442
+ goto invalid;
443
+ id = tf_wasm_heap_span(field.id_offset, field.id_bytes);
444
+ name = tf_wasm_heap_span(field.name_offset, field.name_bytes);
445
+ if (!id || !name) goto invalid;
446
+ owner->fields[i].abi_version = 1;
447
+ owner->fields[i].struct_size = (uint32_t)sizeof(owner->fields[i]);
448
+ owner->fields[i].dtype = field.dtype;
449
+ owner->fields[i].id_utf8 = id;
450
+ owner->fields[i].id_bytes = field.id_bytes;
451
+ owner->fields[i].name_utf8 = name;
452
+ owner->fields[i].name_bytes = field.name_bytes;
453
+ owner->dtypes[i] = field.dtype;
454
+ }
455
+ owner->view.abi_version = 1;
456
+ owner->view.struct_size = (uint32_t)sizeof(owner->view);
457
+ owner->view.column_count = owner->count;
458
+ owner->view.fields = owner->fields;
459
+ owner->view.fields_bytes = owner->count * sizeof(*owner->fields);
460
+ return TF_TRANSFORM_OK;
461
+ invalid:
462
+ tf_wasm_schema_clear(owner);
463
+ return TF_TRANSFORM_INVALID_ARGUMENT;
464
+ }
465
+
466
+ static void tf_wasm_table_clear(tf_wasm_table_owner *owner) {
467
+ if (!owner) return;
468
+ free(owner->columns);
469
+ memset(owner, 0, sizeof(*owner));
470
+ }
471
+
472
+ static tf_transform_code tf_wasm_parse_table(
473
+ uint32_t offset, const tf_wasm_handle_slot *slot,
474
+ tf_wasm_cancel_token *token, tf_wasm_table_owner *owner) {
475
+ tf_wasm_table_view_v1 wire;
476
+ uint64_t expected_bytes;
477
+ memset(owner, 0, sizeof(*owner));
478
+ if (!tf_wasm_read(offset, &wire, sizeof(wire))
479
+ || wire.abi_version != 1 || wire.struct_size != sizeof(wire)
480
+ || wire.column_count != slot->column_count)
481
+ return TF_TRANSFORM_INVALID_ARGUMENT;
482
+ expected_bytes = (uint64_t)wire.column_count * sizeof(tf_wasm_column_view_v1);
483
+ if (expected_bytes != wire.columns_bytes
484
+ || !tf_wasm_heap_span(wire.columns_offset, wire.columns_bytes))
485
+ return TF_TRANSFORM_INVALID_ARGUMENT;
486
+ if (expected_bytes > slot->max_allocation_bytes)
487
+ return TF_TRANSFORM_RESOURCE_LIMIT;
488
+ if ((uint64_t)wire.column_count * sizeof(*owner->columns)
489
+ > slot->max_allocation_bytes) return TF_TRANSFORM_RESOURCE_LIMIT;
490
+ owner->columns = (tf_column_view_v1 *)calloc(
491
+ wire.column_count, sizeof(*owner->columns));
492
+ if (!owner->columns) return TF_TRANSFORM_ALLOCATION;
493
+ for (size_t i = 0; i < wire.column_count; ++i) {
494
+ tf_wasm_column_view_v1 column;
495
+ uint32_t column_offset = (uint32_t)(
496
+ (uint64_t)wire.columns_offset + i * sizeof(column));
497
+ uint32_t item_size = slot->dtypes[i] == TF_VIEW_FLOAT32 ? 4u : 8u;
498
+ uint8_t *data;
499
+ uint8_t *validity = NULL;
500
+ if (i % TF_TRANSFORM_CANCEL_ITERS_V1 == 0
501
+ && tf_wasm_cancel_poll(token)) {
502
+ tf_wasm_table_clear(owner);
503
+ return TF_TRANSFORM_CANCELLED;
504
+ }
505
+ if (!tf_wasm_read(column_offset, &column, sizeof(column))
506
+ || column.abi_version != 1 || column.struct_size != sizeof(column)
507
+ || column.stride_bytes == 0
508
+ || column.stride_bytes % item_size != 0)
509
+ goto invalid;
510
+ data = tf_wasm_heap_span(column.data_offset, column.data_bytes);
511
+ if (wire.row_count != 0
512
+ && (!data || column.data_offset % item_size != 0)) goto invalid;
513
+ if (column.validity_offset != 0 || column.validity_bytes != 0) {
514
+ validity = tf_wasm_heap_span(
515
+ column.validity_offset, column.validity_bytes);
516
+ if (!validity || column.validity_bit_stride == 0) goto invalid;
517
+ } else if (column.validity_bit_offset != 0
518
+ || column.validity_bit_stride != 0) goto invalid;
519
+ owner->columns[i].abi_version = 1;
520
+ owner->columns[i].struct_size = (uint32_t)sizeof(owner->columns[i]);
521
+ owner->columns[i].data = data;
522
+ owner->columns[i].data_bytes = column.data_bytes;
523
+ owner->columns[i].stride_bytes = column.stride_bytes;
524
+ owner->columns[i].validity = validity;
525
+ owner->columns[i].validity_bytes = column.validity_bytes;
526
+ owner->columns[i].validity_bit_offset = column.validity_bit_offset;
527
+ owner->columns[i].validity_bit_stride = column.validity_bit_stride;
528
+ }
529
+ owner->view.abi_version = 1;
530
+ owner->view.struct_size = (uint32_t)sizeof(owner->view);
531
+ owner->view.row_count = wire.row_count;
532
+ owner->view.column_count = wire.column_count;
533
+ owner->view.columns = owner->columns;
534
+ owner->view.columns_bytes = wire.column_count * sizeof(*owner->columns);
535
+ return TF_TRANSFORM_OK;
536
+ invalid:
537
+ tf_wasm_table_clear(owner);
538
+ return TF_TRANSFORM_INVALID_ARGUMENT;
539
+ }
540
+
541
+ TF_WASM_EXPORT
542
+ uint32_t tf_wasm_transform_limits_init_safe_v1(
543
+ uint32_t out_offset, uint32_t out_size) {
544
+ tf_transform_limits_v1 limits;
545
+ tf_transform_code code;
546
+ if (out_size < sizeof(limits)
547
+ || !tf_wasm_heap_span(out_offset, (uint32_t)sizeof(limits)))
548
+ return TF_TRANSFORM_INVALID_ARGUMENT;
549
+ code = tf_transform_limits_init_safe_v1(&limits, sizeof(limits));
550
+ if (code != TF_TRANSFORM_OK) return code;
551
+ return tf_wasm_write(out_offset, &limits, sizeof(limits))
552
+ ? TF_TRANSFORM_OK : TF_TRANSFORM_INVALID_ARGUMENT;
553
+ }
554
+
555
+ TF_WASM_EXPORT
556
+ uint32_t tf_wasm_transform_cancel_create(
557
+ uint32_t host_poll_slot, uint32_t limits_offset,
558
+ uint32_t out_handle_offset, uint32_t out_error_offset) {
559
+ tf_transform_limits_v1 limits;
560
+ tf_transform_error *error = NULL;
561
+ tf_wasm_cancel_token *token;
562
+ tf_transform_code code = tf_wasm_prepare_outputs(
563
+ out_handle_offset, out_error_offset);
564
+ uint32_t handle = 0;
565
+ if (code != TF_TRANSFORM_OK) return code;
566
+ code = tf_wasm_read_limits(limits_offset, &limits, &error);
567
+ if (code != TF_TRANSFORM_OK)
568
+ return tf_wasm_transfer_error_safe(&error, out_error_offset, code);
569
+ token = (tf_wasm_cancel_token *)calloc(1, sizeof(*token));
570
+ if (!token) return TF_TRANSFORM_ALLOCATION;
571
+ atomic_init(&token->references, 1u);
572
+ atomic_init(&token->requested, 0);
573
+ token->host_poll_slot = host_poll_slot;
574
+ code = tf_wasm_install_handle(
575
+ TF_WASM_HANDLE_CANCEL_TOKEN, token, NULL, NULL, 0, &limits, &handle);
576
+ if (code != TF_TRANSFORM_OK) {
577
+ tf_wasm_token_release(token);
578
+ return code;
579
+ }
580
+ if (!tf_wasm_write_u32(out_handle_offset, handle)) {
581
+ (void)tf_wasm_transform_handle_destroy(handle);
582
+ return TF_TRANSFORM_INVALID_ARGUMENT;
583
+ }
584
+ return TF_TRANSFORM_OK;
585
+ }
586
+
587
+ TF_WASM_EXPORT
588
+ uint32_t tf_wasm_transform_cancel_request(uint32_t token_handle) {
589
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
590
+ token_handle, TF_WASM_HANDLE_CANCEL_TOKEN);
591
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
592
+ atomic_store_explicit(
593
+ &((tf_wasm_cancel_token *)slot->pointer)->requested,
594
+ 1, memory_order_release);
595
+ return TF_TRANSFORM_OK;
596
+ }
597
+
598
+ TF_WASM_EXPORT
599
+ uint32_t tf_wasm_transform_recipe_from_json(
600
+ uint32_t json_offset, uint32_t json_bytes, uint32_t limits_offset,
601
+ uint32_t out_handle_offset, uint32_t out_error_offset) {
602
+ tf_transform_limits_v1 limits;
603
+ tf_transform_error *error = NULL;
604
+ tf_transform_recipe *recipe = NULL;
605
+ uint8_t *json;
606
+ uint32_t handle = 0;
607
+ tf_transform_code code = tf_wasm_prepare_outputs(
608
+ out_handle_offset, out_error_offset);
609
+ if (code != TF_TRANSFORM_OK) return code;
610
+ code = tf_wasm_read_limits(limits_offset, &limits, &error);
611
+ if (code != TF_TRANSFORM_OK)
612
+ return tf_wasm_transfer_error_safe(&error, out_error_offset, code);
613
+ json = tf_wasm_heap_span(json_offset, json_bytes);
614
+ if (json_bytes != 0 && !json) return TF_TRANSFORM_INVALID_ARGUMENT;
615
+ code = tf_transform_recipe_from_json(
616
+ json, json_bytes, &limits, &recipe, &error);
617
+ if (code != TF_TRANSFORM_OK)
618
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
619
+ code = tf_wasm_install_handle(
620
+ TF_WASM_HANDLE_RECIPE, recipe, NULL, NULL, 0, &limits, &handle);
621
+ if (code != TF_TRANSFORM_OK) {
622
+ tf_transform_recipe_destroy(&recipe);
623
+ return code;
624
+ }
625
+ if (!tf_wasm_write_u32(out_handle_offset, handle)) {
626
+ (void)tf_wasm_transform_handle_destroy(handle);
627
+ return TF_TRANSFORM_INVALID_ARGUMENT;
628
+ }
629
+ return TF_TRANSFORM_OK;
630
+ }
631
+
632
+ TF_WASM_EXPORT
633
+ uint32_t tf_wasm_transform_analyzer_create(
634
+ uint32_t recipe_handle, uint32_t schema_offset,
635
+ uint32_t limits_offset, uint32_t cancel_token_handle,
636
+ uint32_t out_handle_offset, uint32_t out_error_offset) {
637
+ tf_wasm_handle_slot *recipe_slot;
638
+ tf_wasm_cancel_token *token;
639
+ tf_wasm_schema_owner schema;
640
+ tf_transform_limits_v1 limits;
641
+ tf_transform_runtime_v1 runtime;
642
+ tf_transform_analyzer *analyzer = NULL;
643
+ tf_transform_error *error = NULL;
644
+ uint32_t handle = 0;
645
+ tf_transform_code code = tf_wasm_prepare_outputs(
646
+ out_handle_offset, out_error_offset);
647
+ memset(&schema, 0, sizeof(schema));
648
+ if (code != TF_TRANSFORM_OK) return code;
649
+ recipe_slot = tf_wasm_get_slot(recipe_handle, TF_WASM_HANDLE_RECIPE);
650
+ if (!recipe_slot) return TF_TRANSFORM_INVALID_ARGUMENT;
651
+ token = tf_wasm_optional_token(cancel_token_handle);
652
+ if (cancel_token_handle != 0 && !token) return TF_TRANSFORM_INVALID_ARGUMENT;
653
+ code = tf_wasm_read_limits(limits_offset, &limits, &error);
654
+ if (code != TF_TRANSFORM_OK)
655
+ return tf_wasm_transfer_error_safe(&error, out_error_offset, code);
656
+ tf_wasm_runtime_init(&runtime, &limits, token);
657
+ if (tf_wasm_cancel_poll(token)) {
658
+ code = tf_transform_analyzer_create(
659
+ (tf_transform_recipe *)recipe_slot->pointer, &schema.view,
660
+ &runtime, &analyzer, &error);
661
+ return tf_wasm_transfer_error(
662
+ &error, &limits, out_error_offset, code);
663
+ }
664
+ code = tf_wasm_parse_schema(schema_offset, &limits, token, &schema);
665
+ if (code == TF_TRANSFORM_CANCELLED) {
666
+ code = tf_transform_analyzer_create(
667
+ (tf_transform_recipe *)recipe_slot->pointer, &schema.view,
668
+ &runtime, &analyzer, &error);
669
+ tf_wasm_schema_clear(&schema);
670
+ return tf_wasm_transfer_error(
671
+ &error, &limits, out_error_offset, code);
672
+ }
673
+ if (code != TF_TRANSFORM_OK) return code;
674
+ if (tf_wasm_cancel_poll(token)) {
675
+ code = tf_transform_analyzer_create(
676
+ (tf_transform_recipe *)recipe_slot->pointer, &schema.view,
677
+ &runtime, &analyzer, &error);
678
+ tf_wasm_schema_clear(&schema);
679
+ return tf_wasm_transfer_error(
680
+ &error, &limits, out_error_offset, code);
681
+ }
682
+ code = tf_transform_analyzer_create(
683
+ (tf_transform_recipe *)recipe_slot->pointer, &schema.view,
684
+ &runtime, &analyzer, &error);
685
+ if (code != TF_TRANSFORM_OK) {
686
+ tf_wasm_schema_clear(&schema);
687
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
688
+ }
689
+ code = tf_wasm_install_handle(
690
+ TF_WASM_HANDLE_ANALYZER, analyzer, token,
691
+ schema.dtypes, schema.count, &limits, &handle);
692
+ tf_wasm_schema_clear(&schema);
693
+ if (code != TF_TRANSFORM_OK) {
694
+ tf_transform_analyzer_destroy(&analyzer);
695
+ return code;
696
+ }
697
+ if (!tf_wasm_write_u32(out_handle_offset, handle)) {
698
+ (void)tf_wasm_transform_handle_destroy(handle);
699
+ return TF_TRANSFORM_INVALID_ARGUMENT;
700
+ }
701
+ return TF_TRANSFORM_OK;
702
+ }
703
+
704
+ TF_WASM_EXPORT
705
+ uint32_t tf_wasm_transform_analyzer_push(
706
+ uint32_t analyzer_handle, uint32_t table_offset,
707
+ uint32_t out_error_offset) {
708
+ tf_wasm_handle_slot *slot;
709
+ tf_wasm_table_owner table;
710
+ tf_transform_error *error = NULL;
711
+ tf_transform_limits_v1 limits = tf_wasm_safe_limits();
712
+ tf_transform_code code = tf_wasm_prepare_error(out_error_offset);
713
+ memset(&table, 0, sizeof(table));
714
+ if (code != TF_TRANSFORM_OK) return code;
715
+ slot = tf_wasm_get_slot(analyzer_handle, TF_WASM_HANDLE_ANALYZER);
716
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
717
+ limits.max_live_handles = slot->max_live_handles;
718
+ limits.max_retired_handle_slots = slot->max_retired_slots;
719
+ if (tf_wasm_cancel_poll(slot->token)) {
720
+ code = tf_transform_analyzer_push(
721
+ (tf_transform_analyzer *)slot->pointer, &table.view, &error);
722
+ return tf_wasm_transfer_error(
723
+ &error, &limits, out_error_offset, code);
724
+ }
725
+ code = tf_wasm_parse_table(table_offset, slot, slot->token, &table);
726
+ if (code == TF_TRANSFORM_CANCELLED) {
727
+ code = tf_transform_analyzer_push(
728
+ (tf_transform_analyzer *)slot->pointer, &table.view, &error);
729
+ tf_wasm_table_clear(&table);
730
+ return tf_wasm_transfer_error(
731
+ &error, &limits, out_error_offset, code);
732
+ }
733
+ if (code != TF_TRANSFORM_OK) return code;
734
+ if (tf_wasm_cancel_poll(slot->token)) {
735
+ code = tf_transform_analyzer_push(
736
+ (tf_transform_analyzer *)slot->pointer, &table.view, &error);
737
+ tf_wasm_table_clear(&table);
738
+ return tf_wasm_transfer_error(
739
+ &error, &limits, out_error_offset, code);
740
+ }
741
+ code = tf_transform_analyzer_push(
742
+ (tf_transform_analyzer *)slot->pointer, &table.view, &error);
743
+ tf_wasm_table_clear(&table);
744
+ if (code != TF_TRANSFORM_OK)
745
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
746
+ return TF_TRANSFORM_OK;
747
+ }
748
+
749
+ TF_WASM_EXPORT
750
+ uint32_t tf_wasm_transform_analyzer_finalize(
751
+ uint32_t analyzer_handle, uint32_t out_handle_offset,
752
+ uint32_t out_error_offset) {
753
+ tf_wasm_handle_slot *slot;
754
+ tf_transform_plan *plan = NULL;
755
+ tf_transform_error *error = NULL;
756
+ tf_transform_limits_v1 limits = tf_wasm_safe_limits();
757
+ uint32_t handle = 0;
758
+ tf_transform_code code = tf_wasm_prepare_outputs(
759
+ out_handle_offset, out_error_offset);
760
+ if (code != TF_TRANSFORM_OK) return code;
761
+ slot = tf_wasm_get_slot(analyzer_handle, TF_WASM_HANDLE_ANALYZER);
762
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
763
+ limits.max_live_handles = slot->max_live_handles;
764
+ limits.max_retired_handle_slots = slot->max_retired_slots;
765
+ limits.max_allocation_bytes = slot->max_allocation_bytes;
766
+ if (tf_wasm_cancel_poll(slot->token)) {
767
+ code = tf_transform_analyzer_finalize(
768
+ (tf_transform_analyzer *)slot->pointer, &plan, &error);
769
+ return tf_wasm_transfer_error(
770
+ &error, &limits, out_error_offset, code);
771
+ }
772
+ code = tf_wasm_preflight_handle(&limits);
773
+ if (code != TF_TRANSFORM_OK) return code;
774
+ code = tf_transform_analyzer_finalize(
775
+ (tf_transform_analyzer *)slot->pointer, &plan, &error);
776
+ if (code != TF_TRANSFORM_OK)
777
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
778
+ code = tf_wasm_install_handle(
779
+ TF_WASM_HANDLE_PLAN, plan, NULL, NULL, 0, &limits, &handle);
780
+ if (code != TF_TRANSFORM_OK) {
781
+ tf_transform_plan_destroy(&plan);
782
+ return code;
783
+ }
784
+ if (!tf_wasm_write_u32(out_handle_offset, handle)) {
785
+ (void)tf_wasm_transform_handle_destroy(handle);
786
+ return TF_TRANSFORM_INVALID_ARGUMENT;
787
+ }
788
+ return TF_TRANSFORM_OK;
789
+ }
790
+
791
+ static tf_transform_code tf_wasm_wrap_bytes(
792
+ uint8_t **data, size_t *len, const tf_transform_limits_v1 *limits,
793
+ uint32_t output_offset) {
794
+ tf_wasm_owned_bytes *owned;
795
+ uint32_t handle = 0;
796
+ tf_transform_code code;
797
+ owned = (tf_wasm_owned_bytes *)calloc(1, sizeof(*owned));
798
+ if (!owned) {
799
+ tf_transform_bytes_free(data, len);
800
+ return TF_TRANSFORM_ALLOCATION;
801
+ }
802
+ owned->data = *data;
803
+ owned->len = *len;
804
+ *data = NULL;
805
+ *len = 0;
806
+ code = tf_wasm_install_handle(
807
+ TF_WASM_HANDLE_OWNED_BYTES, owned, NULL, NULL, 0, limits, &handle);
808
+ if (code != TF_TRANSFORM_OK) {
809
+ tf_wasm_destroy_pointer(TF_WASM_HANDLE_OWNED_BYTES, owned);
810
+ return code;
811
+ }
812
+ if (!tf_wasm_write_u32(output_offset, handle)) {
813
+ (void)tf_wasm_transform_handle_destroy(handle);
814
+ return TF_TRANSFORM_INVALID_ARGUMENT;
815
+ }
816
+ return TF_TRANSFORM_OK;
817
+ }
818
+
819
+ TF_WASM_EXPORT
820
+ uint32_t tf_wasm_transform_plan_export(
821
+ uint32_t plan_handle, uint32_t limits_offset,
822
+ uint32_t out_handle_offset, uint32_t out_error_offset) {
823
+ tf_wasm_handle_slot *slot;
824
+ tf_transform_limits_v1 limits;
825
+ tf_transform_error *error = NULL;
826
+ uint8_t *data = NULL;
827
+ size_t len = 0;
828
+ tf_transform_code code = tf_wasm_prepare_outputs(
829
+ out_handle_offset, out_error_offset);
830
+ if (code != TF_TRANSFORM_OK) return code;
831
+ slot = tf_wasm_get_slot(plan_handle, TF_WASM_HANDLE_PLAN);
832
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
833
+ code = tf_wasm_read_limits(limits_offset, &limits, &error);
834
+ if (code != TF_TRANSFORM_OK)
835
+ return tf_wasm_transfer_error_safe(&error, out_error_offset, code);
836
+ code = tf_transform_plan_export(
837
+ (tf_transform_plan *)slot->pointer, &limits, &data, &len, &error);
838
+ if (code != TF_TRANSFORM_OK)
839
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
840
+ return tf_wasm_wrap_bytes(&data, &len, &limits, out_handle_offset);
841
+ }
842
+
843
+ TF_WASM_EXPORT
844
+ uint32_t tf_wasm_transform_plan_import(
845
+ uint32_t bytes_offset, uint32_t bytes_len, uint32_t limits_offset,
846
+ uint32_t cancel_token_handle, uint32_t out_handle_offset,
847
+ uint32_t out_error_offset) {
848
+ tf_transform_limits_v1 limits;
849
+ tf_transform_runtime_v1 runtime;
850
+ tf_wasm_cancel_token *token;
851
+ tf_transform_plan *plan = NULL;
852
+ tf_transform_error *error = NULL;
853
+ uint8_t *bytes;
854
+ uint32_t handle = 0;
855
+ tf_transform_code code = tf_wasm_prepare_outputs(
856
+ out_handle_offset, out_error_offset);
857
+ if (code != TF_TRANSFORM_OK) return code;
858
+ token = tf_wasm_optional_token(cancel_token_handle);
859
+ if (cancel_token_handle != 0 && !token) return TF_TRANSFORM_INVALID_ARGUMENT;
860
+ code = tf_wasm_read_limits(limits_offset, &limits, &error);
861
+ if (code != TF_TRANSFORM_OK)
862
+ return tf_wasm_transfer_error_safe(&error, out_error_offset, code);
863
+ bytes = tf_wasm_heap_span(bytes_offset, bytes_len);
864
+ if (bytes_len != 0 && !bytes) return TF_TRANSFORM_INVALID_ARGUMENT;
865
+ tf_wasm_runtime_init(&runtime, &limits, token);
866
+ code = tf_transform_plan_import(
867
+ bytes, bytes_len, &runtime, &plan, &error);
868
+ if (code != TF_TRANSFORM_OK)
869
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
870
+ code = tf_wasm_install_handle(
871
+ TF_WASM_HANDLE_PLAN, plan, NULL, NULL, 0, &limits, &handle);
872
+ if (code != TF_TRANSFORM_OK) {
873
+ tf_transform_plan_destroy(&plan);
874
+ return code;
875
+ }
876
+ if (!tf_wasm_write_u32(out_handle_offset, handle)) {
877
+ (void)tf_wasm_transform_handle_destroy(handle);
878
+ return TF_TRANSFORM_INVALID_ARGUMENT;
879
+ }
880
+ return TF_TRANSFORM_OK;
881
+ }
882
+
883
+ TF_WASM_EXPORT
884
+ uint32_t tf_wasm_transform_plan_schema_json(
885
+ uint32_t plan_handle, uint32_t which, uint32_t limits_offset,
886
+ uint32_t out_handle_offset, uint32_t out_error_offset) {
887
+ tf_wasm_handle_slot *slot;
888
+ tf_transform_limits_v1 limits;
889
+ tf_transform_error *error = NULL;
890
+ uint8_t *data = NULL;
891
+ size_t len = 0;
892
+ tf_transform_code code = tf_wasm_prepare_outputs(
893
+ out_handle_offset, out_error_offset);
894
+ if (code != TF_TRANSFORM_OK) return code;
895
+ slot = tf_wasm_get_slot(plan_handle, TF_WASM_HANDLE_PLAN);
896
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
897
+ code = tf_wasm_read_limits(limits_offset, &limits, &error);
898
+ if (code != TF_TRANSFORM_OK)
899
+ return tf_wasm_transfer_error_safe(&error, out_error_offset, code);
900
+ code = tf_transform_plan_schema_json(
901
+ (tf_transform_plan *)slot->pointer, which, &limits,
902
+ &data, &len, &error);
903
+ if (code != TF_TRANSFORM_OK)
904
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
905
+ return tf_wasm_wrap_bytes(&data, &len, &limits, out_handle_offset);
906
+ }
907
+
908
+ TF_WASM_EXPORT
909
+ uint32_t tf_wasm_transform_plan_recipe_sha256(
910
+ uint32_t plan_handle, uint32_t limits_offset,
911
+ uint32_t out_handle_offset, uint32_t out_error_offset) {
912
+ tf_wasm_handle_slot *slot;
913
+ tf_transform_limits_v1 limits;
914
+ tf_transform_error *error = NULL;
915
+ uint8_t fingerprint[65];
916
+ uint8_t *data = NULL;
917
+ size_t len = 64;
918
+ tf_transform_code code = tf_wasm_prepare_outputs(
919
+ out_handle_offset, out_error_offset);
920
+ if (code != TF_TRANSFORM_OK) return code;
921
+ slot = tf_wasm_get_slot(plan_handle, TF_WASM_HANDLE_PLAN);
922
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
923
+ code = tf_wasm_read_limits(limits_offset, &limits, &error);
924
+ if (code != TF_TRANSFORM_OK)
925
+ return tf_wasm_transfer_error_safe(&error, out_error_offset, code);
926
+ if (limits.max_allocation_bytes < len) {
927
+ code = tf_transform_set_error(
928
+ &error, TF_TRANSFORM_RESOURCE_LIMIT,
929
+ "recipe fingerprint output exceeds allocation limit");
930
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
931
+ }
932
+ code = tf_transform_plan_recipe_sha256(
933
+ (tf_transform_plan *)slot->pointer, &limits, fingerprint, &error);
934
+ if (code != TF_TRANSFORM_OK)
935
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
936
+ data = (uint8_t *)malloc(len);
937
+ if (!data) {
938
+ code = tf_transform_set_error(
939
+ &error, TF_TRANSFORM_ALLOCATION,
940
+ "recipe fingerprint output allocation failed");
941
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
942
+ }
943
+ memcpy(data, fingerprint, len);
944
+ return tf_wasm_wrap_bytes(&data, &len, &limits, out_handle_offset);
945
+ }
946
+
947
+ static tf_transform_code tf_wasm_get_schema_field(
948
+ uint32_t plan_handle, uint32_t which, uint32_t index,
949
+ tf_field_view_v1 *field, tf_transform_error **error,
950
+ tf_wasm_handle_slot **slot_out) {
951
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
952
+ plan_handle, TF_WASM_HANDLE_PLAN);
953
+ const tf_transform_schema *schema = NULL;
954
+ tf_transform_code code;
955
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
956
+ code = tf_transform_plan_schema(
957
+ (tf_transform_plan *)slot->pointer, which, &schema, error);
958
+ if (code != TF_TRANSFORM_OK) return code;
959
+ code = tf_transform_schema_field(schema, index, field, error);
960
+ if (code == TF_TRANSFORM_OK && slot_out) *slot_out = slot;
961
+ return code;
962
+ }
963
+
964
+ TF_WASM_EXPORT
965
+ uint32_t tf_wasm_transform_plan_schema_field_count(
966
+ uint32_t plan_handle, uint32_t which, uint32_t out_count_offset,
967
+ uint32_t out_error_offset) {
968
+ tf_wasm_handle_slot *slot;
969
+ const tf_transform_schema *schema = NULL;
970
+ tf_transform_error *error = NULL;
971
+ tf_transform_limits_v1 limits = tf_wasm_safe_limits();
972
+ size_t count;
973
+ tf_transform_code code = tf_wasm_prepare_outputs(
974
+ out_count_offset, out_error_offset);
975
+ if (code != TF_TRANSFORM_OK) return code;
976
+ slot = tf_wasm_get_slot(plan_handle, TF_WASM_HANDLE_PLAN);
977
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
978
+ limits.max_live_handles = slot->max_live_handles;
979
+ limits.max_retired_handle_slots = slot->max_retired_slots;
980
+ code = tf_transform_plan_schema(
981
+ (tf_transform_plan *)slot->pointer, which, &schema, &error);
982
+ if (code != TF_TRANSFORM_OK)
983
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
984
+ count = tf_transform_schema_field_count(schema);
985
+ if (count > UINT32_MAX) return TF_TRANSFORM_INTERNAL;
986
+ return tf_wasm_write_u32(out_count_offset, (uint32_t)count)
987
+ ? TF_TRANSFORM_OK : TF_TRANSFORM_INVALID_ARGUMENT;
988
+ }
989
+
990
+ TF_WASM_EXPORT
991
+ uint32_t tf_wasm_transform_plan_schema_field_info(
992
+ uint32_t plan_handle, uint32_t which, uint32_t index,
993
+ uint32_t out_info_offset, uint32_t out_error_offset) {
994
+ tf_field_view_v1 field;
995
+ tf_wasm_schema_field_info_v1 info;
996
+ tf_wasm_handle_slot *slot = NULL;
997
+ tf_transform_error *error = NULL;
998
+ tf_transform_limits_v1 limits = tf_wasm_safe_limits();
999
+ tf_transform_code code;
1000
+ if (tf_wasm_spans_overlap(
1001
+ out_info_offset, sizeof(info), out_error_offset, 4)
1002
+ || !tf_wasm_heap_span(out_info_offset, sizeof(info)))
1003
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1004
+ code = tf_wasm_prepare_error(out_error_offset);
1005
+ if (code != TF_TRANSFORM_OK) return code;
1006
+ memset(&field, 0, sizeof(field));
1007
+ code = tf_wasm_get_schema_field(
1008
+ plan_handle, which, index, &field, &error, &slot);
1009
+ if (slot) {
1010
+ limits.max_live_handles = slot->max_live_handles;
1011
+ limits.max_retired_handle_slots = slot->max_retired_slots;
1012
+ }
1013
+ if (code != TF_TRANSFORM_OK)
1014
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
1015
+ if (field.id_bytes > UINT32_MAX || field.name_bytes > UINT32_MAX)
1016
+ return TF_TRANSFORM_INTERNAL;
1017
+ info.abi_version = 1;
1018
+ info.struct_size = sizeof(info);
1019
+ info.dtype = field.dtype;
1020
+ info.id_bytes = (uint32_t)field.id_bytes;
1021
+ info.name_bytes = (uint32_t)field.name_bytes;
1022
+ return tf_wasm_write(out_info_offset, &info, sizeof(info))
1023
+ ? TF_TRANSFORM_OK : TF_TRANSFORM_INVALID_ARGUMENT;
1024
+ }
1025
+
1026
+ TF_WASM_EXPORT
1027
+ uint32_t tf_wasm_transform_plan_schema_field_copy(
1028
+ uint32_t plan_handle, uint32_t which, uint32_t index,
1029
+ uint32_t id_offset, uint32_t id_capacity,
1030
+ uint32_t name_offset, uint32_t name_capacity,
1031
+ uint32_t out_error_offset) {
1032
+ tf_field_view_v1 field;
1033
+ tf_wasm_handle_slot *slot = NULL;
1034
+ tf_transform_error *error = NULL;
1035
+ tf_transform_limits_v1 limits = tf_wasm_safe_limits();
1036
+ uint8_t *id_destination;
1037
+ uint8_t *name_destination;
1038
+ tf_transform_code code = tf_wasm_prepare_error(out_error_offset);
1039
+ if (code != TF_TRANSFORM_OK) return code;
1040
+ memset(&field, 0, sizeof(field));
1041
+ code = tf_wasm_get_schema_field(
1042
+ plan_handle, which, index, &field, &error, &slot);
1043
+ if (slot) {
1044
+ limits.max_live_handles = slot->max_live_handles;
1045
+ limits.max_retired_handle_slots = slot->max_retired_slots;
1046
+ }
1047
+ if (code != TF_TRANSFORM_OK)
1048
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
1049
+ if (field.id_bytes > id_capacity || field.name_bytes > name_capacity)
1050
+ return TF_TRANSFORM_RESOURCE_LIMIT;
1051
+ if (tf_wasm_spans_overlap(id_offset, id_capacity, name_offset, name_capacity)
1052
+ || tf_wasm_spans_overlap(id_offset, id_capacity, out_error_offset, 4)
1053
+ || tf_wasm_spans_overlap(name_offset, name_capacity, out_error_offset, 4))
1054
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1055
+ id_destination = tf_wasm_heap_span(id_offset, id_capacity);
1056
+ name_destination = tf_wasm_heap_span(name_offset, name_capacity);
1057
+ if ((id_capacity != 0 && !id_destination)
1058
+ || (name_capacity != 0 && !name_destination))
1059
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1060
+ if (field.id_bytes != 0) memcpy(id_destination, field.id_utf8, field.id_bytes);
1061
+ if (field.name_bytes != 0)
1062
+ memcpy(name_destination, field.name_utf8, field.name_bytes);
1063
+ return TF_TRANSFORM_OK;
1064
+ }
1065
+
1066
+ TF_WASM_EXPORT
1067
+ uint32_t tf_wasm_transform_apply_create(
1068
+ uint32_t plan_handle, uint32_t schema_offset,
1069
+ uint32_t limits_offset, uint32_t cancel_token_handle,
1070
+ uint32_t out_handle_offset, uint32_t out_error_offset) {
1071
+ tf_wasm_handle_slot *plan_slot;
1072
+ tf_wasm_cancel_token *token;
1073
+ tf_wasm_schema_owner schema;
1074
+ tf_transform_limits_v1 limits;
1075
+ tf_transform_runtime_v1 runtime;
1076
+ tf_transform_apply *apply = NULL;
1077
+ tf_transform_error *error = NULL;
1078
+ uint32_t handle = 0;
1079
+ tf_transform_code code = tf_wasm_prepare_outputs(
1080
+ out_handle_offset, out_error_offset);
1081
+ memset(&schema, 0, sizeof(schema));
1082
+ if (code != TF_TRANSFORM_OK) return code;
1083
+ plan_slot = tf_wasm_get_slot(plan_handle, TF_WASM_HANDLE_PLAN);
1084
+ if (!plan_slot) return TF_TRANSFORM_INVALID_ARGUMENT;
1085
+ token = tf_wasm_optional_token(cancel_token_handle);
1086
+ if (cancel_token_handle != 0 && !token) return TF_TRANSFORM_INVALID_ARGUMENT;
1087
+ code = tf_wasm_read_limits(limits_offset, &limits, &error);
1088
+ if (code != TF_TRANSFORM_OK)
1089
+ return tf_wasm_transfer_error_safe(&error, out_error_offset, code);
1090
+ tf_wasm_runtime_init(&runtime, &limits, token);
1091
+ if (tf_wasm_cancel_poll(token)) {
1092
+ code = tf_transform_apply_create(
1093
+ (tf_transform_plan *)plan_slot->pointer, &schema.view,
1094
+ &runtime, &apply, &error);
1095
+ return tf_wasm_transfer_error(
1096
+ &error, &limits, out_error_offset, code);
1097
+ }
1098
+ code = tf_wasm_parse_schema(schema_offset, &limits, token, &schema);
1099
+ if (code == TF_TRANSFORM_CANCELLED) {
1100
+ code = tf_transform_apply_create(
1101
+ (tf_transform_plan *)plan_slot->pointer, &schema.view,
1102
+ &runtime, &apply, &error);
1103
+ tf_wasm_schema_clear(&schema);
1104
+ return tf_wasm_transfer_error(
1105
+ &error, &limits, out_error_offset, code);
1106
+ }
1107
+ if (code != TF_TRANSFORM_OK) return code;
1108
+ if (tf_wasm_cancel_poll(token)) {
1109
+ code = tf_transform_apply_create(
1110
+ (tf_transform_plan *)plan_slot->pointer, &schema.view,
1111
+ &runtime, &apply, &error);
1112
+ tf_wasm_schema_clear(&schema);
1113
+ return tf_wasm_transfer_error(
1114
+ &error, &limits, out_error_offset, code);
1115
+ }
1116
+ code = tf_transform_apply_create(
1117
+ (tf_transform_plan *)plan_slot->pointer, &schema.view,
1118
+ &runtime, &apply, &error);
1119
+ if (code != TF_TRANSFORM_OK) {
1120
+ tf_wasm_schema_clear(&schema);
1121
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
1122
+ }
1123
+ code = tf_wasm_install_handle(
1124
+ TF_WASM_HANDLE_APPLY, apply, token,
1125
+ schema.dtypes, schema.count, &limits, &handle);
1126
+ tf_wasm_schema_clear(&schema);
1127
+ if (code != TF_TRANSFORM_OK) {
1128
+ tf_transform_apply_destroy(&apply);
1129
+ return code;
1130
+ }
1131
+ if (!tf_wasm_write_u32(out_handle_offset, handle)) {
1132
+ (void)tf_wasm_transform_handle_destroy(handle);
1133
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1134
+ }
1135
+ return TF_TRANSFORM_OK;
1136
+ }
1137
+
1138
+ TF_WASM_EXPORT
1139
+ uint32_t tf_wasm_transform_apply_run(
1140
+ uint32_t apply_handle, uint32_t table_offset,
1141
+ uint32_t out_handle_offset, uint32_t out_error_offset) {
1142
+ tf_wasm_handle_slot *slot;
1143
+ tf_wasm_table_owner table;
1144
+ tf_wasm_owned_dense *owned;
1145
+ tf_transform_error *error = NULL;
1146
+ tf_transform_limits_v1 limits = tf_wasm_safe_limits();
1147
+ uint32_t handle = 0;
1148
+ tf_transform_code code = tf_wasm_prepare_outputs(
1149
+ out_handle_offset, out_error_offset);
1150
+ memset(&table, 0, sizeof(table));
1151
+ if (code != TF_TRANSFORM_OK) return code;
1152
+ slot = tf_wasm_get_slot(apply_handle, TF_WASM_HANDLE_APPLY);
1153
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
1154
+ limits.max_live_handles = slot->max_live_handles;
1155
+ limits.max_retired_handle_slots = slot->max_retired_slots;
1156
+ limits.max_allocation_bytes = slot->max_allocation_bytes;
1157
+ if (tf_wasm_cancel_poll(slot->token)) {
1158
+ tf_owned_dense_v1 cancelled = {0};
1159
+ code = tf_transform_apply_run(
1160
+ (tf_transform_apply *)slot->pointer, &table.view,
1161
+ &cancelled, &error);
1162
+ return tf_wasm_transfer_error(
1163
+ &error, &limits, out_error_offset, code);
1164
+ }
1165
+ code = tf_wasm_parse_table(table_offset, slot, slot->token, &table);
1166
+ if (code == TF_TRANSFORM_CANCELLED) {
1167
+ tf_owned_dense_v1 cancelled = {0};
1168
+ code = tf_transform_apply_run(
1169
+ (tf_transform_apply *)slot->pointer, &table.view,
1170
+ &cancelled, &error);
1171
+ tf_wasm_table_clear(&table);
1172
+ return tf_wasm_transfer_error(
1173
+ &error, &limits, out_error_offset, code);
1174
+ }
1175
+ if (code != TF_TRANSFORM_OK) return code;
1176
+ if (tf_wasm_cancel_poll(slot->token)) {
1177
+ tf_owned_dense_v1 cancelled = {0};
1178
+ code = tf_transform_apply_run(
1179
+ (tf_transform_apply *)slot->pointer, &table.view,
1180
+ &cancelled, &error);
1181
+ tf_wasm_table_clear(&table);
1182
+ return tf_wasm_transfer_error(
1183
+ &error, &limits, out_error_offset, code);
1184
+ }
1185
+ code = tf_wasm_preflight_handle(&limits);
1186
+ if (code != TF_TRANSFORM_OK) {
1187
+ tf_wasm_table_clear(&table);
1188
+ return code;
1189
+ }
1190
+ owned = (tf_wasm_owned_dense *)calloc(1, sizeof(*owned));
1191
+ if (!owned) {
1192
+ tf_wasm_table_clear(&table);
1193
+ return TF_TRANSFORM_ALLOCATION;
1194
+ }
1195
+ code = tf_transform_apply_run(
1196
+ (tf_transform_apply *)slot->pointer, &table.view,
1197
+ &owned->value, &error);
1198
+ tf_wasm_table_clear(&table);
1199
+ if (code != TF_TRANSFORM_OK) {
1200
+ tf_wasm_destroy_pointer(TF_WASM_HANDLE_OWNED_DENSE, owned);
1201
+ return tf_wasm_transfer_error(&error, &limits, out_error_offset, code);
1202
+ }
1203
+ code = tf_wasm_install_handle(
1204
+ TF_WASM_HANDLE_OWNED_DENSE, owned, slot->token, NULL, 0,
1205
+ &limits, &handle);
1206
+ if (code != TF_TRANSFORM_OK) {
1207
+ tf_wasm_destroy_pointer(TF_WASM_HANDLE_OWNED_DENSE, owned);
1208
+ return code;
1209
+ }
1210
+ if (!tf_wasm_write_u32(out_handle_offset, handle)) {
1211
+ (void)tf_wasm_transform_handle_destroy(handle);
1212
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1213
+ }
1214
+ return TF_TRANSFORM_OK;
1215
+ }
1216
+
1217
+ TF_WASM_EXPORT
1218
+ uint32_t tf_wasm_transform_error_code(
1219
+ uint32_t error_handle, uint32_t out_code_offset) {
1220
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
1221
+ error_handle, TF_WASM_HANDLE_ERROR);
1222
+ tf_transform_code code;
1223
+ if (!slot || !tf_wasm_heap_span(out_code_offset, 4))
1224
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1225
+ code = tf_transform_error_get_code((tf_transform_error *)slot->pointer);
1226
+ return tf_wasm_write_u32(out_code_offset, (uint32_t)code)
1227
+ ? TF_TRANSFORM_OK : TF_TRANSFORM_INVALID_ARGUMENT;
1228
+ }
1229
+
1230
+ TF_WASM_EXPORT
1231
+ uint32_t tf_wasm_transform_error_message_size(
1232
+ uint32_t error_handle, uint32_t out_size_offset) {
1233
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
1234
+ error_handle, TF_WASM_HANDLE_ERROR);
1235
+ size_t len = 0;
1236
+ if (!slot || !tf_wasm_heap_span(out_size_offset, 4))
1237
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1238
+ (void)tf_transform_error_message((tf_transform_error *)slot->pointer, &len);
1239
+ if (len > UINT32_MAX) return TF_TRANSFORM_INTERNAL;
1240
+ return tf_wasm_write_u32(out_size_offset, (uint32_t)len)
1241
+ ? TF_TRANSFORM_OK : TF_TRANSFORM_INVALID_ARGUMENT;
1242
+ }
1243
+
1244
+ TF_WASM_EXPORT
1245
+ uint32_t tf_wasm_transform_error_message_copy(
1246
+ uint32_t error_handle, uint32_t destination_offset,
1247
+ uint32_t destination_capacity) {
1248
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
1249
+ error_handle, TF_WASM_HANDLE_ERROR);
1250
+ size_t len = 0;
1251
+ const uint8_t *message;
1252
+ uint8_t *destination;
1253
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
1254
+ message = tf_transform_error_message(
1255
+ (tf_transform_error *)slot->pointer, &len);
1256
+ if (len > destination_capacity) return TF_TRANSFORM_RESOURCE_LIMIT;
1257
+ destination = tf_wasm_heap_span(destination_offset, destination_capacity);
1258
+ if (destination_capacity != 0 && !destination)
1259
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1260
+ if (len != 0) memcpy(destination, message, len);
1261
+ return TF_TRANSFORM_OK;
1262
+ }
1263
+
1264
+ TF_WASM_EXPORT
1265
+ uint32_t tf_wasm_transform_owned_bytes_size(
1266
+ uint32_t bytes_handle, uint32_t out_size_offset) {
1267
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
1268
+ bytes_handle, TF_WASM_HANDLE_OWNED_BYTES);
1269
+ tf_wasm_owned_bytes *owned;
1270
+ if (!slot || !tf_wasm_heap_span(out_size_offset, 4))
1271
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1272
+ owned = (tf_wasm_owned_bytes *)slot->pointer;
1273
+ if (owned->len > UINT32_MAX) return TF_TRANSFORM_INTERNAL;
1274
+ return tf_wasm_write_u32(out_size_offset, (uint32_t)owned->len)
1275
+ ? TF_TRANSFORM_OK : TF_TRANSFORM_INVALID_ARGUMENT;
1276
+ }
1277
+
1278
+ TF_WASM_EXPORT
1279
+ uint32_t tf_wasm_transform_owned_bytes_copy(
1280
+ uint32_t bytes_handle, uint32_t destination_offset,
1281
+ uint32_t destination_capacity) {
1282
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
1283
+ bytes_handle, TF_WASM_HANDLE_OWNED_BYTES);
1284
+ tf_wasm_owned_bytes *owned;
1285
+ uint8_t *destination;
1286
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
1287
+ owned = (tf_wasm_owned_bytes *)slot->pointer;
1288
+ if (owned->len > destination_capacity) return TF_TRANSFORM_RESOURCE_LIMIT;
1289
+ destination = tf_wasm_heap_span(destination_offset, destination_capacity);
1290
+ if (destination_capacity != 0 && !destination)
1291
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1292
+ if (owned->len != 0) memcpy(destination, owned->data, owned->len);
1293
+ return TF_TRANSFORM_OK;
1294
+ }
1295
+
1296
+ TF_WASM_EXPORT
1297
+ uint32_t tf_wasm_transform_owned_dense_info(
1298
+ uint32_t dense_handle, uint32_t out_info_offset) {
1299
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
1300
+ dense_handle, TF_WASM_HANDLE_OWNED_DENSE);
1301
+ tf_wasm_owned_dense *owned;
1302
+ tf_wasm_dense_info_v1 info;
1303
+ if (!slot || !tf_wasm_heap_span(out_info_offset, sizeof(info)))
1304
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1305
+ owned = (tf_wasm_owned_dense *)slot->pointer;
1306
+ if (owned->value.rows > UINT32_MAX
1307
+ || owned->value.columns > UINT32_MAX
1308
+ || owned->value.data_bytes > UINT32_MAX)
1309
+ return TF_TRANSFORM_INTERNAL;
1310
+ info.abi_version = 1;
1311
+ info.struct_size = sizeof(info);
1312
+ info.dtype = owned->value.dtype;
1313
+ info.flags = owned->value.flags;
1314
+ info.rows = (uint32_t)owned->value.rows;
1315
+ info.columns = (uint32_t)owned->value.columns;
1316
+ info.data_bytes = (uint32_t)owned->value.data_bytes;
1317
+ return tf_wasm_write(out_info_offset, &info, sizeof(info))
1318
+ ? TF_TRANSFORM_OK : TF_TRANSFORM_INVALID_ARGUMENT;
1319
+ }
1320
+
1321
+ TF_WASM_EXPORT
1322
+ uint32_t tf_wasm_transform_owned_dense_copy(
1323
+ uint32_t dense_handle, uint32_t destination_offset,
1324
+ uint32_t destination_capacity) {
1325
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(
1326
+ dense_handle, TF_WASM_HANDLE_OWNED_DENSE);
1327
+ tf_wasm_owned_dense *owned;
1328
+ uint8_t *destination;
1329
+ size_t offset = 0;
1330
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
1331
+ owned = (tf_wasm_owned_dense *)slot->pointer;
1332
+ if (owned->value.data_bytes > destination_capacity)
1333
+ return TF_TRANSFORM_RESOURCE_LIMIT;
1334
+ destination = tf_wasm_heap_span(destination_offset, destination_capacity);
1335
+ if (destination_capacity != 0 && !destination)
1336
+ return TF_TRANSFORM_INVALID_ARGUMENT;
1337
+ while (offset < owned->value.data_bytes) {
1338
+ size_t chunk = owned->value.data_bytes - offset;
1339
+ if (chunk > TF_TRANSFORM_CANCEL_BYTES_V1)
1340
+ chunk = TF_TRANSFORM_CANCEL_BYTES_V1;
1341
+ if (tf_wasm_cancel_poll(slot->token)) return TF_TRANSFORM_CANCELLED;
1342
+ memcpy(destination + offset,
1343
+ (const uint8_t *)owned->value.data + offset, chunk);
1344
+ offset += chunk;
1345
+ }
1346
+ if (tf_wasm_cancel_poll(slot->token)) return TF_TRANSFORM_CANCELLED;
1347
+ return TF_TRANSFORM_OK;
1348
+ }
1349
+
1350
+ TF_WASM_EXPORT
1351
+ uint32_t tf_wasm_transform_handle_destroy(uint32_t handle) {
1352
+ tf_wasm_handle_slot *slot = tf_wasm_get_slot(handle, 0);
1353
+ uint16_t index = (uint16_t)(handle & TF_WASM_SLOT_MASK);
1354
+ if (!slot) return TF_TRANSFORM_INVALID_ARGUMENT;
1355
+ tf_wasm_destroy_pointer(slot->type, slot->pointer);
1356
+ tf_wasm_token_release(slot->token);
1357
+ free(slot->dtypes);
1358
+ slot->pointer = NULL;
1359
+ slot->token = NULL;
1360
+ slot->dtypes = NULL;
1361
+ slot->column_count = 0;
1362
+ slot->type = 0;
1363
+ if (tf_wasm_live_handles != 0) --tf_wasm_live_handles;
1364
+ if (slot->generation == TF_WASM_GENERATION_MASK) {
1365
+ slot->retired = 1;
1366
+ ++tf_wasm_retired_slots;
1367
+ } else {
1368
+ ++slot->generation;
1369
+ slot->next_free = tf_wasm_free_slot_head;
1370
+ tf_wasm_free_slot_head = index;
1371
+ }
1372
+ return TF_TRANSFORM_OK;
1373
+ }