@thi.ng/wasm-api 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/api.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { BigType, FloatType, Fn, IDeref, ILength, IObjectOf } from "@thi.ng/api";
1
+ import type { BigType, FloatType, Fn, Fn2, IDeref, ILength, IObjectOf } from "@thi.ng/api";
2
+ import type { Pow2 } from "@thi.ng/binary";
2
3
  import type { WasmBridge } from "./bridge.js";
3
4
  export declare const PKG_NAME = "@thi.ng/wasm-api";
4
5
  export declare const EVENT_MEMORY_CHANGED = "memory-changed";
@@ -91,6 +92,11 @@ export interface IWasmMemoryAccess {
91
92
  u64: BigUint64Array;
92
93
  f32: Float32Array;
93
94
  f64: Float64Array;
95
+ /**
96
+ * Initializes and/or updates the various typed WASM memory views (e.g.
97
+ * after growing the WASM memory and the previous buffer becoming detached).
98
+ */
99
+ ensureMemory(): void;
94
100
  /**
95
101
  * Reads UTF-8 encoded string from given address and optional byte length.
96
102
  * The default length is 0, which will be interpreted as a zero-terminated
@@ -119,8 +125,12 @@ export interface IWasmMemoryAccess {
119
125
  }
120
126
  /**
121
127
  * Core API of WASM imports defined by the {@link WasmBridge}. The same
122
- * functions are declared as bindings in `/zig/core.zig`. Also see this file for
123
- * documentation of each function...
128
+ * functions are declared as bindings in `/zig/wasmapi.zig`. **Also see this
129
+ * file for documentation of each function...**
130
+ *
131
+ * @remarks
132
+ * Zig API:
133
+ * https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/wasmapi.zig
124
134
  */
125
135
  export interface CoreAPI extends WebAssembly.ModuleImports {
126
136
  printI8: Fn<number, void>;
@@ -187,17 +197,17 @@ export interface TypeInfo {
187
197
  */
188
198
  __size?: number;
189
199
  /**
190
- * Auto-computed offset (in bytes) in parent struct
200
+ * Auto-computed offset (in bytes) in parent struct.
191
201
  *
192
202
  * @internal
193
203
  */
194
204
  __offset?: number;
195
205
  /**
196
- * Auto-computed alignment (in bytes)
206
+ * Auto-computed alignment (in bytes) actually used.
197
207
  *
198
208
  * @internal
199
209
  */
200
- __align?: number;
210
+ __align?: Pow2;
201
211
  }
202
212
  export interface TopLevelType extends TypeInfo {
203
213
  /**
@@ -211,7 +221,7 @@ export interface TopLevelType extends TypeInfo {
211
221
  /**
212
222
  * Type / kind
213
223
  */
214
- type: "struct" | "enum";
224
+ type: "enum" | "struct" | "union";
215
225
  /**
216
226
  * Optional object of user provided source codes to be injected into the
217
227
  * generated type. Keys are language IDs (same name as respective codegen).
@@ -224,10 +234,10 @@ export interface TopLevelType extends TypeInfo {
224
234
  export interface Struct extends TopLevelType {
225
235
  type: "struct";
226
236
  /**
227
- * List of struct fields (might be re-ordered if {@link Struct.auto} is
237
+ * Array of struct fields (might be re-ordered if {@link Struct.auto} is
228
238
  * enabled).
229
239
  */
230
- fields: StructField[];
240
+ fields: Field[];
231
241
  /**
232
242
  * If true, struct fields will be re-ordered in descending order based on
233
243
  * their {@link TypeInfo.__align} size. This might result in overall smaller
@@ -243,8 +253,14 @@ export interface Struct extends TopLevelType {
243
253
  * interpretation, currently only used by {@link ZIG}).
244
254
  */
245
255
  tag?: "extern" | "packed";
256
+ /**
257
+ * Optional user supplied {@link AlignStrategy}. By default uses
258
+ * {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" structs).
259
+ */
260
+ align?: AlignStrategy;
246
261
  }
247
- export interface StructField extends TypeInfo {
262
+ export declare type FieldTag = "scalar" | "array" | "ptr" | "slice" | "vec";
263
+ export interface Field extends TypeInfo {
248
264
  /**
249
265
  * Field name (prefix: "__" is reserved)
250
266
  */
@@ -259,7 +275,7 @@ export interface StructField extends TypeInfo {
259
275
  *
260
276
  * @remarks
261
277
  * - Array & vector fields are statically sized (using
262
- * {@link StructField.len})
278
+ * {@link Field.len})
263
279
  * - Pointers are emitted as single-value pointers (where this distinction
264
280
  * exist), i.e. even if they're pointing to multiple values, there's no
265
281
  * explicit length encoded/available
@@ -269,7 +285,7 @@ export interface StructField extends TypeInfo {
269
285
  *
270
286
  * @defaultValue "scalar"
271
287
  */
272
- tag?: "scalar" | "array" | "ptr" | "slice" | "vec";
288
+ tag?: FieldTag;
273
289
  /**
274
290
  * Field base type. If not a {@link WasmPrim}, `string` or `opaque`, the
275
291
  * value is interpreted as another type name in the {@link TypeColl}.
@@ -292,7 +308,7 @@ export interface StructField extends TypeInfo {
292
308
  */
293
309
  sentinel?: number;
294
310
  /**
295
- * Array or vector length (see {@link StructField.tag})
311
+ * Array or vector length (see {@link Field.tag})
296
312
  */
297
313
  len?: number;
298
314
  /**
@@ -301,11 +317,29 @@ export interface StructField extends TypeInfo {
301
317
  */
302
318
  default?: number;
303
319
  /**
304
- * If defined and > 0, the field will be considered for padding purposes only and
305
- * the value provided is the number of bytes used.
320
+ * If defined and > 0, the field will be considered for padding purposes
321
+ * only and the value provided is the number of bytes used. All other config
322
+ * for this field will be ignored!
306
323
  */
307
324
  pad?: number;
308
325
  }
326
+ export interface Union extends TopLevelType {
327
+ type: "union";
328
+ /**
329
+ * Array of union fields.
330
+ */
331
+ fields: Field[];
332
+ /**
333
+ * Optional qualifier for the kind of struct to be emitted (codegen specific
334
+ * interpretation, currently only used by {@link ZIG}).
335
+ */
336
+ tag?: "extern" | "packed";
337
+ /**
338
+ * Optional user supplied {@link AlignStrategy}. By default uses
339
+ * {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" union).
340
+ */
341
+ align?: AlignStrategy;
342
+ }
309
343
  export interface Enum extends TopLevelType {
310
344
  type: "enum";
311
345
  /**
@@ -335,22 +369,42 @@ export interface EnumValue {
335
369
  */
336
370
  doc?: string;
337
371
  }
372
+ export interface AlignStrategy {
373
+ /**
374
+ * Returns implementation specific alignment for given struct field.
375
+ */
376
+ align: Fn<Field, Pow2>;
377
+ /**
378
+ * Returns possibly rounded value for given base size & alignment.
379
+ */
380
+ size: Fn2<number, Pow2, number>;
381
+ /**
382
+ * Returns possibly rounded value for given base offset & alignment.
383
+ */
384
+ offset: Fn2<number, Pow2, number>;
385
+ }
338
386
  export interface CodeGenOptsBase {
339
387
  /**
340
388
  * Optional string to be injected before generated type defs (but after
341
389
  * codegen's own prelude, if any)
342
390
  */
343
- pre: string;
391
+ pre?: string;
344
392
  /**
345
393
  * Optional string to be injected after generated type defs (but before
346
394
  * codegen's own epilogue, if any)
347
395
  */
348
- post: string;
396
+ post?: string;
349
397
  }
350
398
  /**
351
399
  * Global/shared code generator options.
352
400
  */
353
401
  export interface CodeGenOpts extends CodeGenOptsBase {
402
+ /**
403
+ * WASM target specification.
404
+ *
405
+ * @defaultValue {@link WASM32}
406
+ */
407
+ target: WasmTarget;
354
408
  /**
355
409
  * Identifier how strings are stored on WASM side, e.g. in Zig string
356
410
  * literals are slices (8 bytes), in C just plain pointers (4 bytes).
@@ -404,13 +458,21 @@ export interface ICodeGen {
404
458
  * Codegen for struct types.
405
459
  */
406
460
  struct: (type: Struct, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
461
+ /**
462
+ * Codegen for union types.
463
+ */
464
+ union: (type: Union, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
465
+ }
466
+ export interface WasmTarget {
467
+ usize: "u32" | "u64";
468
+ usizeBytes: number;
407
469
  }
408
470
  /**
409
- * WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
471
+ * WASM32 target spec
410
472
  */
411
- export declare const USIZE = "u32";
473
+ export declare const WASM32: WasmTarget;
412
474
  /**
413
- * Byte size of {@link USIZE}.
475
+ * WASM64 target spec
414
476
  */
415
- export declare const USIZE_SIZE = 4;
477
+ export declare const WASM64: WasmTarget;
416
478
  //# sourceMappingURL=api.d.ts.map
package/api.js CHANGED
@@ -1,10 +1,16 @@
1
1
  export const PKG_NAME = "@thi.ng/wasm-api";
2
2
  export const EVENT_MEMORY_CHANGED = "memory-changed";
3
3
  /**
4
- * WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
4
+ * WASM32 target spec
5
5
  */
6
- export const USIZE = "u32";
6
+ export const WASM32 = {
7
+ usize: "u32",
8
+ usizeBytes: 4,
9
+ };
7
10
  /**
8
- * Byte size of {@link USIZE}.
11
+ * WASM64 target spec
9
12
  */
10
- export const USIZE_SIZE = 4;
13
+ export const WASM64 = {
14
+ usize: "u64",
15
+ usizeBytes: 8,
16
+ };
package/bridge.js CHANGED
@@ -414,6 +414,7 @@ let WasmBridge = class WasmBridge {
414
414
  * @param terminate
415
415
  */
416
416
  setString(str, addr, maxBytes, terminate = true) {
417
+ this.ensureMemory();
417
418
  maxBytes = Math.min(maxBytes, this.u8.length - addr);
418
419
  const len = this.utf8Encoder.encodeInto(str, this.u8.subarray(addr, addr + maxBytes)).written;
419
420
  if (len == null || len >= maxBytes + (terminate ? 0 : 1)) {
package/cli.js CHANGED
@@ -22,7 +22,11 @@ const argOpts = {
22
22
  hint: "FILE",
23
23
  desc: "JSON config file with codegen options",
24
24
  }),
25
- debug: flag({ alias: "d", default: false, desc: "enable debug output" }),
25
+ debug: flag({
26
+ alias: "d",
27
+ default: false,
28
+ desc: "enable debug output & functions",
29
+ }),
26
30
  dryRun: flag({
27
31
  default: false,
28
32
  desc: "enable dry run (don't overwrite files)",
@@ -71,7 +75,7 @@ const invalidSpec = (path, msg) => {
71
75
  const addTypeSpec = (ctx, path, coll, spec) => {
72
76
  if (!(spec.name && spec.type))
73
77
  invalidSpec(path);
74
- if (!(spec.type === "enum" || spec.type === "struct"))
78
+ if (!["enum", "struct", "union"].includes(spec.type))
75
79
  invalidSpec(path, `${spec.name} type: ${spec.type}`);
76
80
  if (coll[spec.name])
77
81
  invalidSpec(path, `duplicate name: ${spec.name}`);
@@ -0,0 +1,14 @@
1
+ import type { AlignStrategy, TopLevelType } from "../api.js";
2
+ /**
3
+ * C ABI compatible alignment
4
+ */
5
+ export declare const ALIGN_C: AlignStrategy;
6
+ export declare const ALIGN_PACKED: AlignStrategy;
7
+ /**
8
+ * Returns a suitable alignment strategy for given type, either via user
9
+ * supplied impl defined for the type or derived via a struct's tag.
10
+ *
11
+ * @param type
12
+ */
13
+ export declare const selectAlignment: (type: TopLevelType) => AlignStrategy;
14
+ //# sourceMappingURL=align.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { SIZEOF } from "@thi.ng/api/typedarray";
2
+ import { align as $align } from "@thi.ng/binary/align";
3
+ import { ceilPow2 } from "@thi.ng/binary/pow";
4
+ /**
5
+ * C ABI compatible alignment
6
+ */
7
+ export const ALIGN_C = {
8
+ align: (field) => {
9
+ let align = SIZEOF[field.type];
10
+ if (field.tag === "vec") {
11
+ align *= ceilPow2(field.len);
12
+ }
13
+ return align;
14
+ },
15
+ size: (size, align) => $align(size, align),
16
+ offset: (offset, align) => $align(offset, align),
17
+ };
18
+ export const ALIGN_PACKED = {
19
+ align: () => 1,
20
+ size: (size) => size,
21
+ offset: (offset) => offset,
22
+ };
23
+ /**
24
+ * Returns a suitable alignment strategy for given type, either via user
25
+ * supplied impl defined for the type or derived via a struct's tag.
26
+ *
27
+ * @param type
28
+ */
29
+ export const selectAlignment = (type) => {
30
+ if (type.type === "struct" || type.type === "union") {
31
+ let $type = type;
32
+ return $type.align || ($type.tag === "packed" ? ALIGN_PACKED : ALIGN_C);
33
+ }
34
+ return ALIGN_C;
35
+ };
package/codegen/c11.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { isString } from "@thi.ng/checks/is-string";
2
2
  import { unsupported } from "@thi.ng/errors/unsupported";
3
- import { enumName, isPadding, isStringSlice, prefixLines, withIndentation, } from "./utils.js";
3
+ import { enumName, isPadding, isStringSlice, isWasmString, prefixLines, withIndentation, } from "./utils.js";
4
4
  const PRIM_ALIASES = {
5
5
  i8: "int8_t",
6
6
  u8: "uint8_t",
@@ -68,62 +68,78 @@ ${opts.debug ? "\n#include <stdalign.h>" : ""}
68
68
  },
69
69
  struct: (struct, coll, acc, opts) => {
70
70
  const name = typePrefix + struct.name;
71
- const res = [];
72
- res.push(`typedef struct ${name} ${name};`, `struct ${name} {`);
73
- const ftypes = {};
74
- let padID = 0;
75
- for (let f of struct.fields) {
76
- // autolabel explicit padding fields
77
- if (isPadding(f)) {
78
- res.push(`uint8_t __pad${padID++}[${f.pad}];`);
79
- continue;
80
- }
81
- f.doc && gen.doc(f.doc, res, opts);
82
- const fconst = f.const ? "const " : "";
83
- let ftype = f.type === "string"
84
- ? isStringSlice(opts.stringType)
85
- ? __slice("char", fconst)
86
- : `${f.const !== false ? "const " : ""}char*`
87
- : PRIM_ALIASES[f.type] || f.type;
88
- if (coll[ftype])
89
- ftype = typePrefix + ftype;
90
- switch (f.tag) {
91
- case "array":
92
- case "vec":
93
- res.push(`${fconst}${ftype} ${f.name}[${f.len}];`);
94
- ftype = `${ftype}[${f.len}]`;
95
- break;
96
- case "slice":
97
- ftype = __slice(ftype, fconst);
98
- res.push(`${ftype} ${f.name};`);
99
- break;
100
- case "ptr":
101
- ftype = `${fconst}${ftype}*`;
102
- res.push(`${ftype} ${f.name};`);
103
- break;
104
- case "scalar":
105
- default:
106
- res.push(`${ftype} ${f.name};`);
107
- }
108
- ftypes[f.name] = ftype;
109
- }
110
- res.push("};");
111
- if (opts.debug) {
112
- const fn = (fname, body) => res.push("", `size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
113
- fn("align", `alignof(${name})`);
114
- fn("size", `sizeof(${name})`);
115
- for (let f of struct.fields) {
116
- if (isPadding(f))
117
- continue;
118
- fn(f.name + "_align", `alignof(${ftypes[f.name]})`);
119
- fn(f.name + "_offset", `offsetof(${name}, ${f.name})`);
120
- fn(f.name + "_size", `sizeof(${ftypes[f.name]})`);
121
- }
122
- }
123
- res.push("");
124
- acc.push(...withIndentation(res, INDENT, ...SCOPES));
71
+ acc.push(...withIndentation([
72
+ `typedef struct ${name} ${name};`,
73
+ `struct ${name} {`,
74
+ ...__generateFields(gen, struct, coll, opts, typePrefix),
75
+ ], INDENT, ...SCOPES));
76
+ },
77
+ union: (union, coll, acc, opts) => {
78
+ const name = typePrefix + union.name;
79
+ acc.push(...withIndentation([
80
+ `typedef union ${name} ${name};`,
81
+ `union ${name} {`,
82
+ ...__generateFields(gen, union, coll, opts, typePrefix),
83
+ ], INDENT, ...SCOPES));
125
84
  },
126
85
  };
127
86
  return gen;
128
87
  };
88
+ const __generateFields = (gen, parent, coll, opts, typePrefix) => {
89
+ const res = [];
90
+ const ftypes = {};
91
+ const isUnion = parent.type === "union";
92
+ const name = typePrefix + parent.name;
93
+ let padID = 0;
94
+ for (let f of parent.fields) {
95
+ // autolabel explicit padding fields
96
+ if (isPadding(f)) {
97
+ res.push(`uint8_t __pad${padID++}[${f.pad}];`);
98
+ continue;
99
+ }
100
+ f.doc && gen.doc(f.doc, res, opts);
101
+ const fconst = f.const ? "const " : "";
102
+ let ftype = isWasmString(f.type)
103
+ ? isStringSlice(opts.stringType)
104
+ ? __slice("char", fconst)
105
+ : `${f.const !== false ? "const " : ""}char*`
106
+ : PRIM_ALIASES[f.type] || f.type;
107
+ if (coll[ftype])
108
+ ftype = typePrefix + ftype;
109
+ switch (f.tag) {
110
+ case "array":
111
+ case "vec":
112
+ res.push(`${fconst}${ftype} ${f.name}[${f.len}];`);
113
+ ftype = `${ftype}[${f.len}]`;
114
+ break;
115
+ case "slice":
116
+ ftype = __slice(ftype, fconst);
117
+ res.push(`${ftype} ${f.name};`);
118
+ break;
119
+ case "ptr":
120
+ ftype = `${fconst}${ftype}*`;
121
+ res.push(`${ftype} ${f.name};`);
122
+ break;
123
+ case "scalar":
124
+ default:
125
+ res.push(`${ftype} ${f.name};`);
126
+ }
127
+ ftypes[f.name] = ftype;
128
+ }
129
+ res.push("};");
130
+ if (opts.debug) {
131
+ const fn = (fname, body) => res.push("", `size_t __attribute__((used)) ${name}_${fname}() {`, `return ${body};`, `}`);
132
+ fn("align", `alignof(${name})`);
133
+ fn("size", `sizeof(${name})`);
134
+ for (let f of parent.fields) {
135
+ if (isPadding(f))
136
+ continue;
137
+ fn(f.name + "_align", `alignof(${ftypes[f.name]})`);
138
+ !isUnion && fn(f.name + "_offset", `offsetof(${name}, ${f.name})`);
139
+ fn(f.name + "_size", `sizeof(${ftypes[f.name]})`);
140
+ }
141
+ }
142
+ res.push("");
143
+ return res;
144
+ };
129
145
  const __slice = (type, $const) => `struct { ${$const}${type} *ptr; size_t len; }`;
@@ -1,6 +1,6 @@
1
1
  import { BIGINT_ARRAY_CTORS, BIT_SHIFTS, TYPEDARRAY_CTORS, } from "@thi.ng/api/typedarray";
2
2
  import { isString } from "@thi.ng/checks/is-string";
3
- import { PKG_NAME, USIZE, USIZE_SIZE, } from "../api.js";
3
+ import { PKG_NAME, } from "../api.js";
4
4
  import { enumName, isBigNumeric, isNumeric, isPadding, isStringSlice, isWasmPrim, isWasmString, pointerFields, prefixLines, stringFields, withIndentation, } from "./utils.js";
5
5
  /**
6
6
  * TypeScript code generator. Call with options and then pass to
@@ -91,7 +91,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
91
91
  const fn = f.len
92
92
  ? [
93
93
  `(addr) => {`,
94
- ...__mapStringArray("buf", $stringImpl, f.len, isConst, true),
94
+ ...__mapStringArray(opts.target, "buf", $stringImpl, f.len, isConst, true),
95
95
  `}`,
96
96
  ]
97
97
  : [
@@ -107,15 +107,15 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
107
107
  }
108
108
  }
109
109
  else if (f.tag === "slice") {
110
- lines.push(`const len = ${__ptr(offset + 4)};`);
110
+ lines.push(`const len = ${__ptr(opts.target, offset + 4)};`);
111
111
  if (isPrim) {
112
- lines.push(`const addr = ${__ptrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
112
+ lines.push(`const addr = ${__ptrShift(opts.target, offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + len);`);
113
113
  }
114
114
  else if (isStr) {
115
- lines.push(`const addr = ${__ptr(offset)};`, ...__mapStringArray("buf", $stringImpl, "len", isConst));
115
+ lines.push(`const addr = ${__ptr(opts.target, offset)};`, ...__mapStringArray(opts.target, "buf", $stringImpl, "len", isConst));
116
116
  }
117
117
  else {
118
- lines.push(`const addr = ${__ptr(offset)};`, ...__mapArray(f));
118
+ lines.push(`const addr = ${__ptr(opts.target, offset)};`, ...__mapArray(f));
119
119
  }
120
120
  }
121
121
  else if (f.tag === "array" || f.tag === "vec") {
@@ -123,7 +123,7 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
123
123
  lines.push(`const addr = ${__addrShift(offset, f.type)};`, `return mem.${f.type}.subarray(addr, addr + ${f.len});`);
124
124
  }
125
125
  else if (isStr) {
126
- lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(f.name, $stringImpl, f.len, isConst));
126
+ lines.push(`if ($${f.name}) return $${f.name};`, `const addr = ${__addr(offset)};`, ...__mapStringArray(opts.target, f.name, $stringImpl, f.len, isConst));
127
127
  }
128
128
  else {
129
129
  lines.push(`const addr = ${__addr(offset)};`, ...__mapArray(f, f.len));
@@ -162,6 +162,9 @@ import { Pointer, ${__stringImpl(opts)}, WasmTypeBase, WasmTypeConstructor } fro
162
162
  lines.push("};", "}", "});", "");
163
163
  acc.push(...withIndentation(lines, indent, ...SCOPES));
164
164
  },
165
+ union: (type, coll, acc, opts) => {
166
+ gen.struct(type, coll, acc, opts);
167
+ },
165
168
  };
166
169
  return gen;
167
170
  };
@@ -201,9 +204,9 @@ const __addrShift = (offset, shift) => {
201
204
  return __addr(offset) + (bits ? " >>> " + bits : "");
202
205
  };
203
206
  /** @internal */
204
- const __ptr = (offset) => `mem.${USIZE}[${__addrShift(offset, USIZE)}]`;
207
+ const __ptr = (target, offset) => `mem.${target.usize}[${__addrShift(offset, target.usize)}]`;
205
208
  /** @internal */
206
- const __ptrShift = (offset, shift) => __ptr(offset) + " >>> " + __shift(shift);
209
+ const __ptrShift = (target, offset, shift) => __ptr(target, offset) + " >>> " + __shift(shift);
207
210
  const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`;
208
211
  /** @internal */
209
212
  const __mapArray = (f, len = "len") => [
@@ -213,8 +216,8 @@ const __mapArray = (f, len = "len") => [
213
216
  `return slice;`,
214
217
  ];
215
218
  /** @internal */
216
- const __mapStringArray = (name, type, len, isConst, isLocal = false) => [
219
+ const __mapStringArray = (target, name, type, len, isConst, isLocal = false) => [
217
220
  isLocal ? `const $${name}: ${type}[] = [];` : `$${name} = [];`,
218
- `for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${USIZE_SIZE * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
221
+ `for(let i = 0; i < ${len}; i++) $${name}.push(new ${type}(mem, addr + i * ${target.usizeBytes * (type === "WasmStringSlice" ? 2 : 1)}, ${isConst}));`,
219
222
  `return $${name};`,
220
223
  ];
@@ -1,5 +1,5 @@
1
1
  import type { BigType } from "@thi.ng/api";
2
- import type { CodeGenOpts, StructField, WasmPrim, WasmPrim32 } from "../api.js";
2
+ import type { CodeGenOpts, Field, WasmPrim, WasmPrim32 } from "../api.js";
3
3
  /**
4
4
  * Returns true iff `x` is a {@link WasmPrim32}.
5
5
  *
@@ -19,7 +19,15 @@ export declare const isBigNumeric: (x: string) => x is BigType;
19
19
  */
20
20
  export declare const isWasmPrim: (x: string) => x is WasmPrim;
21
21
  export declare const isWasmString: (x: string) => x is "string";
22
- export declare const isPadding: (f: StructField) => boolean;
22
+ export declare const isPadding: (f: Field) => boolean;
23
+ export declare const isPointer: (f: Field) => boolean;
24
+ export declare const isSlice: (f: Field) => boolean;
25
+ /**
26
+ * Returns true iff the struct field is a pointer, slice or "string" type
27
+ *
28
+ * @param f
29
+ */
30
+ export declare const isPointerLike: (f: Field) => boolean;
23
31
  /**
24
32
  * Takes an array of strings or splits given string into lines, word wraps and
25
33
  * then prefixes each line with given `width` and `prefix`. Returns array of new
@@ -43,7 +51,7 @@ export declare const isStringSlice: (type: CodeGenOpts["stringType"]) => type is
43
51
  *
44
52
  * @internal
45
53
  */
46
- export declare const pointerFields: (fields: StructField[]) => StructField[];
54
+ export declare const pointerFields: (fields: Field[]) => Field[];
47
55
  /**
48
56
  * Returns filtered array of struct fields of only "string" fields.
49
57
  *
@@ -51,7 +59,7 @@ export declare const pointerFields: (fields: StructField[]) => StructField[];
51
59
  *
52
60
  * @internal
53
61
  */
54
- export declare const stringFields: (fields: StructField[]) => StructField[];
62
+ export declare const stringFields: (fields: Field[]) => Field[];
55
63
  /**
56
64
  * Returns enum identifier formatted according to given opts.
57
65
  *
package/codegen/utils.js CHANGED
@@ -20,6 +20,14 @@ export const isBigNumeric = (x) => /^[iu]64$/.test(x);
20
20
  export const isWasmPrim = (x) => isNumeric(x) || isBigNumeric(x);
21
21
  export const isWasmString = (x) => x === "string";
22
22
  export const isPadding = (f) => f.pad != null && f.pad > 0;
23
+ export const isPointer = (f) => f.tag === "ptr";
24
+ export const isSlice = (f) => f.tag === "slice";
25
+ /**
26
+ * Returns true iff the struct field is a pointer, slice or "string" type
27
+ *
28
+ * @param f
29
+ */
30
+ export const isPointerLike = (f) => isPointer(f) || isSlice(f) || isWasmString(f.type);
23
31
  /**
24
32
  * Takes an array of strings or splits given string into lines, word wraps and
25
33
  * then prefixes each line with given `width` and `prefix`. Returns array of new