@thi.ng/wasm-api 0.14.0 → 0.16.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";
@@ -52,7 +53,7 @@ export interface WasmExports {
52
53
  * @remarks
53
54
  * #### Zig
54
55
  *
55
- * Using the supplied Zig bindings (see `/include/wasmapi.zig`), it's the
56
+ * Using the supplied Zig bindings (see `/zig/wasmapi.zig`), it's the
56
57
  * user's responsibility to define a public `WASM_ALLOCATOR` in the root
57
58
  * source file to enable allocations, e.g. using the
58
59
  * [`std.heap.GeneralPurposeAllocator`](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
@@ -80,6 +81,7 @@ export interface WasmExports {
80
81
  */
81
82
  _wasm_free(addr: number, numBytes: number): void;
82
83
  }
84
+ export declare type MemorySlice = [addr: number, len: number];
83
85
  export interface IWasmMemoryAccess {
84
86
  i8: Int8Array;
85
87
  u8: Uint8Array;
@@ -96,6 +98,41 @@ export interface IWasmMemoryAccess {
96
98
  * after growing the WASM memory and the previous buffer becoming detached).
97
99
  */
98
100
  ensureMemory(): void;
101
+ /**
102
+ * Attempts to grow the WASM memory by an additional `numPages` (64KB/page)
103
+ * and if successful updates all typed memory views to use the new
104
+ * underlying buffer.
105
+ *
106
+ * @param numPages
107
+ */
108
+ growMemory(numPages: number): void;
109
+ /**
110
+ * Attempts to allocate `numBytes` using the exported WASM core API function
111
+ * {@link WasmExports._wasm_allocate} (implementation specific) and returns
112
+ * start address of the new memory block. If unsuccessful, throws an
113
+ * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
114
+ * be zero-filled.
115
+ *
116
+ * @remarks
117
+ * See {@link WasmExports._wasm_allocate} docs for further details.
118
+ *
119
+ * @param numBytes
120
+ * @param clear
121
+ */
122
+ allocate(numBytes: number, clear?: boolean): MemorySlice;
123
+ /**
124
+ * Frees a previous allocated memory region using the exported WASM core API
125
+ * function {@link WasmExports._wasm_free} (implementation specific). The
126
+ * `numBytes` value must be the same as previously given to
127
+ * {@link IWasmMemoryAccess.allocate}.
128
+ *
129
+ * @remarks
130
+ * This function always succeeds, regardless of presence of an active
131
+ * allocator on the WASM side or validity of given arguments.
132
+ *
133
+ * @param slice
134
+ */
135
+ free(slice: MemorySlice): void;
99
136
  /**
100
137
  * Reads UTF-8 encoded string from given address and optional byte length.
101
138
  * The default length is 0, which will be interpreted as a zero-terminated
@@ -124,8 +161,12 @@ export interface IWasmMemoryAccess {
124
161
  }
125
162
  /**
126
163
  * Core API of WASM imports defined by the {@link WasmBridge}. The same
127
- * functions are declared as bindings in `/zig/core.zig`. Also see this file for
128
- * documentation of each function...
164
+ * functions are declared as bindings in `/zig/wasmapi.zig`. **Also see this
165
+ * file for documentation of each function...**
166
+ *
167
+ * @remarks
168
+ * Zig API:
169
+ * https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/wasmapi.zig
129
170
  */
130
171
  export interface CoreAPI extends WebAssembly.ModuleImports {
131
172
  printI8: Fn<number, void>;
@@ -192,17 +233,17 @@ export interface TypeInfo {
192
233
  */
193
234
  __size?: number;
194
235
  /**
195
- * Auto-computed offset (in bytes) in parent struct
236
+ * Auto-computed offset (in bytes) in parent struct.
196
237
  *
197
238
  * @internal
198
239
  */
199
240
  __offset?: number;
200
241
  /**
201
- * Auto-computed alignment (in bytes)
242
+ * Auto-computed alignment (in bytes) actually used.
202
243
  *
203
244
  * @internal
204
245
  */
205
- __align?: number;
246
+ __align?: Pow2;
206
247
  }
207
248
  export interface TopLevelType extends TypeInfo {
208
249
  /**
@@ -212,27 +253,32 @@ export interface TopLevelType extends TypeInfo {
212
253
  /**
213
254
  * Optional (multi-line) docstring for this type
214
255
  */
215
- doc?: string;
256
+ doc?: string | string[];
216
257
  /**
217
258
  * Type / kind
218
259
  */
219
- type: "struct" | "enum";
260
+ type: "enum" | "struct" | "union";
220
261
  /**
221
262
  * Optional object of user provided source codes to be injected into the
222
- * generated type. Keys are language IDs (same name as respective codegen).
263
+ * generated type (after generated fields). Keys of this object are language
264
+ * IDs (`ts` for {@link TYPESCRIPT}, `zig` for {@link ZIG}).
223
265
  *
224
266
  * @remarks
225
- * Currently only supported by the {@link ZIG} codegen, ignored otherwise.
267
+ * Currently only supported by the code gens mentioned, ignored otherwise.
226
268
  */
227
- body?: IObjectOf<string>;
269
+ body?: IObjectOf<string | string[] | InjectedBody>;
270
+ }
271
+ export interface InjectedBody {
272
+ decl?: string | string[];
273
+ impl?: string | string[];
228
274
  }
229
275
  export interface Struct extends TopLevelType {
230
276
  type: "struct";
231
277
  /**
232
- * List of struct fields (might be re-ordered if {@link Struct.auto} is
278
+ * Array of struct fields (might be re-ordered if {@link Struct.auto} is
233
279
  * enabled).
234
280
  */
235
- fields: StructField[];
281
+ fields: Field[];
236
282
  /**
237
283
  * If true, struct fields will be re-ordered in descending order based on
238
284
  * their {@link TypeInfo.__align} size. This might result in overall smaller
@@ -248,8 +294,31 @@ export interface Struct extends TopLevelType {
248
294
  * interpretation, currently only used by {@link ZIG}).
249
295
  */
250
296
  tag?: "extern" | "packed";
297
+ /**
298
+ * Optional user supplied {@link AlignStrategy}. By default uses
299
+ * {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" structs).
300
+ */
301
+ align?: AlignStrategy;
302
+ }
303
+ export interface Union extends TopLevelType {
304
+ type: "union";
305
+ /**
306
+ * Array of union fields.
307
+ */
308
+ fields: Field[];
309
+ /**
310
+ * Optional qualifier for the kind of struct to be emitted (codegen specific
311
+ * interpretation, currently only used by {@link ZIG}).
312
+ */
313
+ tag?: "extern" | "packed";
314
+ /**
315
+ * Optional user supplied {@link AlignStrategy}. By default uses
316
+ * {@link ALIGN_C} or {@link ALIGN_PACKED} (if using "packed" union).
317
+ */
318
+ align?: AlignStrategy;
251
319
  }
252
- export interface StructField extends TypeInfo {
320
+ export declare type FieldTag = "scalar" | "array" | "ptr" | "slice" | "vec";
321
+ export interface Field extends TypeInfo {
253
322
  /**
254
323
  * Field name (prefix: "__" is reserved)
255
324
  */
@@ -257,14 +326,14 @@ export interface StructField extends TypeInfo {
257
326
  /**
258
327
  * Field docstring (can be multiline, will be formatted)
259
328
  */
260
- doc?: string;
329
+ doc?: string | string[];
261
330
  /**
262
331
  * Field type tag/qualifier (note: `slice` & `vec` are only supported by Zig
263
332
  * & TS).
264
333
  *
265
334
  * @remarks
266
335
  * - Array & vector fields are statically sized (using
267
- * {@link StructField.len})
336
+ * {@link Field.len})
268
337
  * - Pointers are emitted as single-value pointers (where this distinction
269
338
  * exist), i.e. even if they're pointing to multiple values, there's no
270
339
  * explicit length encoded/available
@@ -274,7 +343,7 @@ export interface StructField extends TypeInfo {
274
343
  *
275
344
  * @defaultValue "scalar"
276
345
  */
277
- tag?: "scalar" | "array" | "ptr" | "slice" | "vec";
346
+ tag?: FieldTag;
278
347
  /**
279
348
  * Field base type. If not a {@link WasmPrim}, `string` or `opaque`, the
280
349
  * value is interpreted as another type name in the {@link TypeColl}.
@@ -297,7 +366,7 @@ export interface StructField extends TypeInfo {
297
366
  */
298
367
  sentinel?: number;
299
368
  /**
300
- * Array or vector length (see {@link StructField.tag})
369
+ * Array or vector length (see {@link Field.tag})
301
370
  */
302
371
  len?: number;
303
372
  /**
@@ -306,8 +375,9 @@ export interface StructField extends TypeInfo {
306
375
  */
307
376
  default?: number;
308
377
  /**
309
- * If defined and > 0, the field will be considered for padding purposes only and
310
- * the value provided is the number of bytes used.
378
+ * If defined and > 0, the field will be considered for padding purposes
379
+ * only and the value provided is the number of bytes used. All other config
380
+ * for this field will be ignored!
311
381
  */
312
382
  pad?: number;
313
383
  }
@@ -340,22 +410,42 @@ export interface EnumValue {
340
410
  */
341
411
  doc?: string;
342
412
  }
413
+ export interface AlignStrategy {
414
+ /**
415
+ * Returns implementation specific alignment for given struct field.
416
+ */
417
+ align: Fn<Field, Pow2>;
418
+ /**
419
+ * Returns possibly rounded value for given base size & alignment.
420
+ */
421
+ size: Fn2<number, Pow2, number>;
422
+ /**
423
+ * Returns possibly rounded value for given base offset & alignment.
424
+ */
425
+ offset: Fn2<number, Pow2, number>;
426
+ }
343
427
  export interface CodeGenOptsBase {
344
428
  /**
345
429
  * Optional string to be injected before generated type defs (but after
346
430
  * codegen's own prelude, if any)
347
431
  */
348
- pre: string;
432
+ pre?: string;
349
433
  /**
350
434
  * Optional string to be injected after generated type defs (but before
351
435
  * codegen's own epilogue, if any)
352
436
  */
353
- post: string;
437
+ post?: string;
354
438
  }
355
439
  /**
356
440
  * Global/shared code generator options.
357
441
  */
358
442
  export interface CodeGenOpts extends CodeGenOptsBase {
443
+ /**
444
+ * WASM target specification.
445
+ *
446
+ * @defaultValue {@link WASM32}
447
+ */
448
+ target: WasmTarget;
359
449
  /**
360
450
  * Identifier how strings are stored on WASM side, e.g. in Zig string
361
451
  * literals are slices (8 bytes), in C just plain pointers (4 bytes).
@@ -400,7 +490,7 @@ export interface ICodeGen {
400
490
  /**
401
491
  * Docstring codegen
402
492
  */
403
- doc: (doc: string, acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
493
+ doc: (doc: string | string[], acc: string[], opts: CodeGenOpts, topLevel?: boolean) => void;
404
494
  /**
405
495
  * Codegen for enum types.
406
496
  */
@@ -409,13 +499,21 @@ export interface ICodeGen {
409
499
  * Codegen for struct types.
410
500
  */
411
501
  struct: (type: Struct, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
502
+ /**
503
+ * Codegen for union types.
504
+ */
505
+ union: (type: Union, types: TypeColl, acc: string[], opts: CodeGenOpts) => void;
506
+ }
507
+ export interface WasmTarget {
508
+ usize: "u32" | "u64";
509
+ usizeBytes: number;
412
510
  }
413
511
  /**
414
- * WASM usize type. Assuming wasm32 until wasm64 surfaces, then need an option.
512
+ * WASM32 target spec
415
513
  */
416
- export declare const USIZE = "u32";
514
+ export declare const WASM32: WasmTarget;
417
515
  /**
418
- * Byte size of {@link USIZE}.
516
+ * WASM64 target spec
419
517
  */
420
- export declare const USIZE_SIZE = 4;
518
+ export declare const WASM64: WasmTarget;
421
519
  //# 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.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import type { Event, INotify, Listener, NumericArray } from "@thi.ng/api";
3
3
  import type { ILogger } from "@thi.ng/logger";
4
- import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, WasmExports } from "./api.js";
4
+ import { BigIntArray, CoreAPI, IWasmAPI, IWasmMemoryAccess, MemorySlice, WasmExports } from "./api.js";
5
5
  export declare const Panic: {
6
6
  new (msg?: string | undefined): {
7
7
  name: string;
@@ -136,34 +136,8 @@ export declare class WasmBridge<T extends WasmExports = WasmExports> implements
136
136
  * @param numPages
137
137
  */
138
138
  growMemory(numPages: number): void;
139
- /**
140
- * Attempts to allocate `numBytes` using the exported WASM core API function
141
- * {@link WasmExports._wasm_allocate} (implementation specific) and returns
142
- * start address of the new memory block. If unsuccessful, throws an
143
- * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
144
- * be zero-filled.
145
- *
146
- * @remarks
147
- * See {@link WasmExports._wasm_allocate} docs for further details.
148
- *
149
- * @param numBytes
150
- * @param clear
151
- */
152
- allocate(numBytes: number, clear?: boolean): number;
153
- /**
154
- * Frees a previous allocated memory region using the exported WASM core API
155
- * function {@link WasmExports._wasm_free} (implementation specific). The
156
- * `numBytes` value must be the same as previously given to
157
- * {@link WasmBridge.allocate}.
158
- *
159
- * @remarks
160
- * This function always succeeds, regardless of presence of an active
161
- * allocator on the WASM side or validity of given arguments.
162
- *
163
- * @param addr
164
- * @param numBytes
165
- */
166
- free(addr: number, numBytes: number): void;
139
+ allocate(numBytes: number, clear?: boolean): MemorySlice;
140
+ free([addr, numBytes]: MemorySlice): void;
167
141
  getI8(addr: number): number;
168
142
  getU8(addr: number): number;
169
143
  getI16(addr: number): number;
package/bridge.js CHANGED
@@ -198,19 +198,6 @@ let WasmBridge = class WasmBridge {
198
198
  this.exports.memory.grow(numPages);
199
199
  this.ensureMemory();
200
200
  }
201
- /**
202
- * Attempts to allocate `numBytes` using the exported WASM core API function
203
- * {@link WasmExports._wasm_allocate} (implementation specific) and returns
204
- * start address of the new memory block. If unsuccessful, throws an
205
- * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
206
- * be zero-filled.
207
- *
208
- * @remarks
209
- * See {@link WasmExports._wasm_allocate} docs for further details.
210
- *
211
- * @param numBytes
212
- * @param clear
213
- */
214
201
  allocate(numBytes, clear = false) {
215
202
  const addr = this.exports._wasm_allocate(numBytes);
216
203
  if (!addr)
@@ -218,22 +205,9 @@ let WasmBridge = class WasmBridge {
218
205
  this.logger.fine(() => `allocated ${numBytes} bytes @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
219
206
  this.ensureMemory();
220
207
  clear && this.u8.fill(0, addr, addr + numBytes);
221
- return addr;
208
+ return [addr, numBytes];
222
209
  }
223
- /**
224
- * Frees a previous allocated memory region using the exported WASM core API
225
- * function {@link WasmExports._wasm_free} (implementation specific). The
226
- * `numBytes` value must be the same as previously given to
227
- * {@link WasmBridge.allocate}.
228
- *
229
- * @remarks
230
- * This function always succeeds, regardless of presence of an active
231
- * allocator on the WASM side or validity of given arguments.
232
- *
233
- * @param addr
234
- * @param numBytes
235
- */
236
- free(addr, numBytes) {
210
+ free([addr, numBytes]) {
237
211
  this.logger.fine(() => `freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
238
212
  this.exports._wasm_free(addr, numBytes);
239
213
  }
package/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { flag, oneOf, oneOfMulti, parse, ParseError, string, strings, usage, } from "@thi.ng/args";
2
- import { isArray, isPlainObject } from "@thi.ng/checks";
2
+ import { isArray, isPlainObject, isString } from "@thi.ng/checks";
3
3
  import { illegalArgs } from "@thi.ng/errors";
4
4
  import { readJSON, readText, writeJSON, writeText } from "@thi.ng/file-io";
5
5
  import { ConsoleLogger } from "@thi.ng/logger";
@@ -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}`);
@@ -80,7 +84,7 @@ const addTypeSpec = (ctx, path, coll, spec) => {
80
84
  invalidSpec(path, `${spec.name}.body must be an object`);
81
85
  for (let lang in spec.body) {
82
86
  const src = spec.body[lang];
83
- if (src[0] === "@") {
87
+ if (isString(src) && src[0] === "@") {
84
88
  spec.body[lang] = readText(src.substring(1), ctx.logger);
85
89
  }
86
90
  }
@@ -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; }`;