@thi.ng/wasm-api 0.18.1 → 1.0.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.
package/README.md CHANGED
@@ -10,14 +10,10 @@ This project is part of the
10
10
  [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo.
11
11
 
12
12
  - [About](#about)
13
- - [Data bindings & code generators](#data-bindings--code-generators)
14
- - [CLI generator](#cli-generator)
15
- - [Data type definitions](#data-type-definitions)
16
- - [Example usage](#example-usage)
17
- - [String handling](#string-handling)
18
- - [Memory allocations](#memory-allocations)
19
13
  - [Custom API modules](#custom-api-modules)
20
14
  - [Building Zig projects with these hybrid API modules](#building-zig-projects-with-these-hybrid-api-modules)
15
+ - [String handling](#string-handling)
16
+ - [Memory allocations](#memory-allocations)
21
17
  - [Object indices & handles](#object-indices--handles)
22
18
  - [Status](#status)
23
19
  - [Support packages](#support-packages)
@@ -27,465 +23,46 @@ This project is part of the
27
23
  - [API](#api)
28
24
  - [Basic usage example](#basic-usage-example)
29
25
  - [Zig version](#zig-version)
30
- - [C11 version](#c11-version)
26
+ - [C version](#c-version)
31
27
  - [Authors](#authors)
32
28
  - [License](#license)
33
29
 
34
30
  ## About
35
31
 
36
- Generic, modular, extensible API bridge, polyglot glue code and bindings code generators for hybrid JS & WebAssembly projects.
32
+ Generic, modular, extensible API bridge and infrastructure for hybrid JS & WebAssembly projects.
37
33
 
38
- This package provides a the following:
34
+ This package provides the following:
39
35
 
40
- 1. A small, generic and modular
36
+ 1. A small
41
37
  [`WasmBridge`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html)
42
- class as interop basis and much reduced boilerplate for hybrid JS/WebAssembly
38
+ class as generic interop basis and much reduced boilerplate for hybrid JS/WebAssembly
43
39
  applications.
44
40
  2. A minimal core API for debug output, string/pointer/typedarray accessors for
45
- 8/16/32/64 bit (u)ints and 32/64 bit floats, memory allocation (optional).
46
- Additionally, a number of support modules for [DOM
41
+ 8/16/32/64 bit (u)ints and 32/64 bit floats. Additionally, a number of support
42
+ modules for [DOM
47
43
  manipulation](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-dom/),
44
+ [scheduled function
45
+ execution](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-schedule/),
48
46
  WebGL, WebGPU, WebAudio etc. is being actively worked on.
49
- 3. Include files for
50
- [C11/C++](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api/include)
51
- and
47
+ 3. Different types of memory-mapped (UTF-8) string abstractions (slice or pointer based)
48
+ 5. Shared (opt-in) memory allocation mechanism, also accessible from JS/TS side
49
+ 4. Simple registration & dependency-order initialization for child WASM API modules
50
+ 6. Include files for
52
51
  [Zig](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api/zig),
53
- defining glue code for the JS [core
52
+ and
53
+ [C/C++](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api/include)
54
+ defining glue code for the TypeScript [core
54
55
  API](https://docs.thi.ng/umbrella/wasm-api/interfaces/CoreAPI.html) defined
55
56
  by this package
56
- 4. Extensible shared datatype code generators for (currently) C11, Zig &
57
- TypeScript. The latter also generates fully type checked memory-mapped
58
- (zero-copy) accessors of WASM-side data. In general, all languages with a
59
- WebAssembly target are supported, however currently only bindings for these
60
- mentioned langs are included. Other languages require custom bindings, e.g.
61
- based on the flexible primitives provided here.
62
- 5. [CLI frontend/utility](#cli-generator) to invoke the code generator(s)
63
-
64
- ### Data bindings & code generators
65
-
66
- The package provides an extensible codegeneration framework to simplify the
67
- bilateral design & exchange of data structures shared between the WASM & JS host
68
- env. Currently, code generators for TypeScript, Zig and C11 are supplied. A CLI
69
- wrapper is available too. See the
70
- [@thi.ng/wasm-api-dom](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-dom/)
71
- support package for a more thorough realworld example...
72
-
73
- #### CLI generator
74
-
75
- The package includes a [small CLI
76
- wrapper](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/src/cli.ts)
77
- to invoke the code generator(s) from JSON type definitions and to write the
78
- generated source code(s) to different files:
79
-
80
- ```text
81
- $ npx @thi.ng/wasm-api
82
-
83
- █ █ █ │
84
- ██ █ │
85
- █ █ █ █ █ █ █ █ │ @thi.ng/wasm-api 0.15.0
86
- █ █ █ █ █ █ █ █ █ │ Multi-language data bindings code generator
87
- █ │
88
- █ █ │
89
-
90
- usage: wasm-api [OPTS] JSON-INPUT-FILE(S) ...
91
- wasm-api --help
92
-
93
- Flags:
94
-
95
- -d, --debug enable debug output & functions
96
- --dry-run enable dry run (don't overwrite files)
97
-
98
- Main:
99
-
100
- -a FILE, --analytics FILE output file path for raw codegen analytics
101
- -c FILE, --config FILE JSON config file with codegen options
102
- -l ID[,..], --lang ID[,..] [multiple] target language: "c11", "ts", "zig" (default: ["ts","zig"])
103
- -o FILE, --out FILE [multiple] output file path
104
- -s TYPE, --string TYPE Force string type implementation: "slice", "ptr"
105
- ```
106
-
107
- By default, the CLI generates sources for TypeScript and Zig (in this order!).
108
- Order is important, since the output file paths must be given in the same order
109
- as the target languages. It's recommended to be explicit with this. An example
110
- invocation looks like:
111
-
112
- ```bash
113
- wasm-api \
114
- --config codegen-opts.json \
115
- --lang ts -o src/generated.ts \
116
- --lang zig -o src.zig/generated.zig \
117
- typedefs.json
118
- ```
119
-
120
- The structure of the config file is as follows (all optional):
121
-
122
- ```json
123
- {
124
- "global": { ... },
125
- "c11": { ... },
126
- "ts": { ... },
127
- "zig": { ... },
128
- }
129
- ```
130
-
131
- More details about possible
132
- [`global`](https://docs.thi.ng/umbrella/wasm-api/interfaces/CodeGenOpts.html),
133
- [`c`](https://docs.thi.ng/umbrella/wasm-api/interfaces/C11Opts.html) and
134
- [`ts`](https://docs.thi.ng/umbrella/wasm-api/interfaces/TSOpts.html) and
135
- [`zig`](https://docs.thi.ng/umbrella/wasm-api/interfaces/ZigOpts.html) config
136
- options & values.
137
-
138
- All code generators have support for custom code prologues & epilogues which can
139
- be specified via the above options. These config options exist for both non-CLI
140
- & CLI usage. For the latter, these custom code sections can also be loaded from
141
- external files by specifying their file paths using `@` as prefix, e.g.
142
-
143
- ```json
144
- {
145
- "ts": { "pre": "@tpl/prelude.ts" },
146
- "zig": { "pre": "@tpl/prelude.zig", "post": "@tpl/epilogue.zig" },
147
- }
148
- ```
149
-
150
- #### Data type definitions
151
-
152
- Currently, the code generator supports enums, structs and unions. See API docs for
153
- further details:
154
-
155
- - [`Enum`](https://docs.thi.ng/umbrella/wasm-api/interfaces/Enum.html)
156
- - [`EnumValue`](https://docs.thi.ng/umbrella/wasm-api/interfaces/EnumValue.html) (individual enum value spec)
157
- - [`Field`](https://docs.thi.ng/umbrella/wasm-api/interfaces/Field.html) (individual spec for values contained in structs/unions)
158
- - [`Struct`](https://docs.thi.ng/umbrella/wasm-api/interfaces/Struct.html)
159
- - [`Union`](https://docs.thi.ng/umbrella/wasm-api/interfaces/Union.html)
160
- - [`TopLevelType`](https://docs.thi.ng/umbrella/wasm-api/interfaces/TopLevelType.html)
161
-
162
- #### Example usage
163
-
164
- The following example defines 1x enum, 2x structs and 1x union. Shown here are
165
- the JSON type definitions and the resulting source codes:
166
-
167
- **⬇︎ CLICK TO EXPAND EACH CODE BLOCK ⬇︎**
168
-
169
- <details><summary>types.json (Type definitions)</summary>
170
-
171
- ```json tangle:export/readme-types.json
172
- [
173
- {
174
- "name": "EventType",
175
- "type": "enum",
176
- "tag": "u8",
177
- "values": [
178
- "unknown",
179
- { "name": "mouse", "value": 16 },
180
- { "name": "key", "value": 32 }
181
- ]
182
- },
183
- {
184
- "name": "MouseEvent",
185
- "type": "struct",
186
- "doc": "Example struct",
187
- "fields": [
188
- { "name": "type", "type": "EventType" },
189
- { "name": "pos", "type": "u16", "tag": "vec", "len": 2 }
190
- ]
191
- },
192
- {
193
- "name": "KeyEvent",
194
- "type": "struct",
195
- "doc": "Example struct",
196
- "fields": [
197
- { "name": "type", "type": "EventType" },
198
- { "name": "key", "type": "string" },
199
- { "name": "modifiers", "type": "u8", "doc": "Bitmask of modifier keys" }
200
- ]
201
- },
202
- {
203
- "name": "Event",
204
- "type": "union",
205
- "fields": [
206
- { "name": "mouse", "type": "MouseEvent" },
207
- { "name": "key", "type": "KeyEvent" }
208
- ]
209
- }
210
- ]
211
- ```
212
- </details>
213
-
214
- <details><summary>generated.ts (generated TypeScript source)</summary>
215
-
216
- ```ts
217
- /**
218
- * Generated by @thi.ng/wasm-api at 2022-10-26T08:36:16.825Z - DO NOT EDIT!
219
- */
220
-
221
- // @ts-ignore possibly includes unused imports
222
- import { Pointer, WasmStringSlice, WasmTypeBase, WasmTypeConstructor } from "@thi.ng/wasm-api";
223
-
224
- export enum EventType {
225
- UNKNOWN,
226
- MOUSE = 16,
227
- KEY = 32,
228
- }
229
-
230
- /**
231
- * Example struct
232
- */
233
- export interface MouseEvent extends WasmTypeBase {
234
- type: EventType;
235
- pos: Uint16Array;
236
- }
237
-
238
- export const $MouseEvent: WasmTypeConstructor<MouseEvent> = (mem) => ({
239
- get align() {
240
- return 4;
241
- },
242
- get size() {
243
- return 8;
244
- },
245
- instance: (base) => {
246
- return {
247
- get __base() {
248
- return base;
249
- },
250
- get __bytes() {
251
- return mem.u8.subarray(base, base + 8);
252
- },
253
- get type(): EventType {
254
- return mem.u8[base];
255
- },
256
- set type(x: EventType) {
257
- mem.u8[base] = x;
258
- },
259
- get pos(): Uint16Array {
260
- const addr = (base + 4) >>> 1;
261
- return mem.u16.subarray(addr, addr + 2);
262
- },
263
- };
264
- }
265
- });
266
-
267
- /**
268
- * Example struct
269
- */
270
- export interface KeyEvent extends WasmTypeBase {
271
- type: EventType;
272
- key: WasmStringSlice;
273
- /**
274
- * Bitmask of modifier keys
275
- */
276
- modifiers: number;
277
- }
278
-
279
- export const $KeyEvent: WasmTypeConstructor<KeyEvent> = (mem) => ({
280
- get align() {
281
- return 4;
282
- },
283
- get size() {
284
- return 16;
285
- },
286
- instance: (base) => {
287
- let $key: WasmStringSlice | null = null;
288
- return {
289
- get __base() {
290
- return base;
291
- },
292
- get __bytes() {
293
- return mem.u8.subarray(base, base + 16);
294
- },
295
- get type(): EventType {
296
- return mem.u8[base];
297
- },
298
- set type(x: EventType) {
299
- mem.u8[base] = x;
300
- },
301
- get key(): WasmStringSlice {
302
- return $key || ($key = new WasmStringSlice(mem, (base + 4), true));
303
- },
304
- get modifiers(): number {
305
- return mem.u8[(base + 12)];
306
- },
307
- set modifiers(x: number) {
308
- mem.u8[(base + 12)] = x;
309
- },
310
- };
311
- }
312
- });
313
-
314
- export interface Event extends WasmTypeBase {
315
- mouse: MouseEvent;
316
- key: KeyEvent;
317
- }
318
-
319
- export const $Event: WasmTypeConstructor<Event> = (mem) => ({
320
- get align() {
321
- return 4;
322
- },
323
- get size() {
324
- return 16;
325
- },
326
- instance: (base) => {
327
- return {
328
- get __base() {
329
- return base;
330
- },
331
- get __bytes() {
332
- return mem.u8.subarray(base, base + 16);
333
- },
334
- get mouse(): MouseEvent {
335
- return $MouseEvent(mem).instance(base);
336
- },
337
- set mouse(x: MouseEvent) {
338
- mem.u8.set(x.__bytes, base);
339
- },
340
- get key(): KeyEvent {
341
- return $KeyEvent(mem).instance(base);
342
- },
343
- set key(x: KeyEvent) {
344
- mem.u8.set(x.__bytes, base);
345
- },
346
- };
347
- }
348
- });
349
- ```
350
- </details>
351
-
352
- <details><summary>generated.zig (generated Zig source)</summary>
353
-
354
- ```zig
355
- //! Generated by @thi.ng/wasm-api at 2022-10-26T08:36:16.827Z - DO NOT EDIT!
356
-
357
- const std = @import("std");
358
-
359
- pub const EventType = enum(u8) {
360
- UNKNOWN,
361
- MOUSE = 16,
362
- KEY = 32,
363
- };
364
-
365
- /// Example struct
366
- pub const MouseEvent = struct {
367
- type: EventType,
368
- pos: @Vector(2, u16),
369
- };
370
-
371
- /// Example struct
372
- pub const KeyEvent = struct {
373
- type: EventType,
374
- key: []const u8,
375
- /// Bitmask of modifier keys
376
- modifiers: u8,
377
- };
378
-
379
- pub const Event = union {
380
- mouse: MouseEvent,
381
- key: KeyEvent,
382
- };
383
- ```
384
- </details>
385
-
386
- On the TypeScript/JS side, the memory-mapped wrappers (e.g. `$Event`)
387
- can be used in combination with the `WasmBridge` to obtain fully typed views
388
- (according to the generated types) of the underlying WASM memory. Basic usage is
389
- like:
390
-
391
- ```ts
392
- import { WasmBridge } from "@thi.ng/wasm-api";
393
- import { $Event, EventType } from "./generated.ts";
394
-
395
- const bridge = new WasmBridge();
396
- // bridge initialization omitted here (see other examples below)
397
- // ...
398
-
399
- // Create an instance using the bridge's memory views
400
- // and mapping a `Event` union from given address
401
- // (e.g. obtained from an exported WASM function/value)
402
- const event = $Event(bridge).instance(0x10000);
403
-
404
- // then use like normal JS object
405
- event.mouse.pos
406
- // Uint16Array(2) [100, 200]
407
-
408
- // IMPORTANT: any modifications like this are directly
409
- // applied to the underlying WASM memory...
410
- event.mouse.pos[0] = 300;
411
-
412
- event.mouse.type === EventType.MOUSE
413
- // true
414
- ```
415
-
416
- **IMPORTANT:** Field setters are currently only supported for single values,
417
- incl. enums, strings, structs, unions. The latter 2 will always be copied by
418
- value (mem copy). Arrays or slices of strings do not currently provide write
419
- access...
420
-
421
- ### String handling
422
-
423
- Most low-level languages deal with strings very differently and alas there's no
424
- general standard. Some have UTF-8/16 support, others don't. In some languages
425
- (incl. C & Zig), strings are stored as zero terminated, in others they aren't...
426
- It's outside the scope of this package to provide an allround out-of-the-box
427
- solution. The `WasmBridge` provides read & write accessors to obtain JS strings
428
- from UTF-8 encoded WASM memory. See
429
- [`getString()`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html#getString)
430
- and
431
- [`setString()`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html#setString)
432
- for details.
433
-
434
- The code generators too provide a global `stringType` option to
435
- interpret the `string` type of a struct field in different ways:
436
-
437
- - `slice` (default): Considers strings as Zig-style slices (i.e. pointer + length)
438
- - `ptr`: Considers strings as C-style raw `*char` pointer (without any length)
439
-
440
- Note: If setting this global option to `ptr`, it also has to be repeated for the
441
- TypeScript code generator explicitly.
442
-
443
- ### Memory allocations
444
-
445
- If explicitly enabled on the WASM side, the `WasmBridge` includes support for
446
- malloc/free-style allocations (within the linear WASM memory) from the JS side
447
- (Note: This is a breaking change in v0.10.0, now using a more flexible approach
448
- & reverse logic of earlier alpha versions).
449
-
450
- The actual allocator is implementation specific and suitable generic mechanisms
451
- are defined for both the included Zig & C bindings. Please see for further
452
- reference:
453
-
454
- - [`/zig/lib.zig`](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/lib.zig#L64):
455
- comments about WASM-side allocator handling in Zig
456
- - [`/include/wasmapi.h`](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/include/wasmapi.h#L18):
457
- comments about WASM-side allocator handling in C/C++
458
- - [`WasmBridge.allocate()`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html#allocate):
459
- allocating memory from JS side
460
- - [`WasmBridge.free()`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html#free):
461
- freeing previously allocated memory from JS side
462
-
463
- Note: The provided Zig mechanism supports the idiomatic (Zig) pattern of working
464
- with multiple allocators in different parts of the application and supports
465
- dynamic assignments/swapping of the exposed allocator. See comments in source
466
- file and
467
- [tests](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api/test)
468
- for more details...
469
-
470
- ```ts
471
- try {
472
- // allocate 1KB of memory for passing a string to WASM side
473
- const addr = bridge.allocate(256);
474
-
475
- // write string to reserved memory
476
- // max. 256 bytes, zero terminated
477
- const num = bridge.setString("hello WASM world!", addr, 256, true);
478
-
479
- // call WASM function doing something w/ the string
480
- bridge.exports.doSomethingWithString(addr, num);
481
-
482
- // cleanup
483
- bridge.free(addr, 256);
484
- } catch(e) {
485
- // deal with allocation error
486
- // ...
487
- }
488
- ```
57
+ 7. Extensible shared [datatype code generator
58
+ infrastructure](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-bindgen/)
59
+ for (currently) Zig & TypeScript and C11. For TS fully type checked and
60
+ memory-mapped (zero-copy) accessors of WASM-side data are generated. In
61
+ principle, all languages with a WASM target are supported, however currently
62
+ only bindings for these mentioned langs are included.
63
+ 8. [CLI
64
+ frontend/utility](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-bindgen/README.md#cli-generator)
65
+ for the code generator(s)
489
66
 
490
67
  ### Custom API modules
491
68
 
@@ -505,40 +82,40 @@ following example provides a brief overview:
505
82
  import { IWasmAPI, WasmBridge } from "@thi.ng/wasm-api";
506
83
 
507
84
  export class CustomAPI implements IWasmAPI {
508
- // Unique API module identifier to group WASM imports,
509
- // must match ID used by native code (see further below).
510
- readonly id = "custom";
511
- // optionally list IDs of other API modules this module depends on
512
- // these are used to infer the correct initialization order
513
- readonly dependencies = [];
514
-
515
- parent!: WasmBridge;
516
-
517
- async init(parent: WasmBridge) {
518
- this.parent = parent;
519
- this.parent.logger.debug("initializing custom API");
520
-
521
- // any other tasks you might need to do...
522
-
523
- return true;
524
- }
525
-
526
- /**
527
- * Returns object of functions to import as externals into the
528
- * WASM module during instantiation. These imports are merged
529
- * into a larger imports object alongside the bridge's core API...
530
- */
531
- getImports(): WebAssembly.Imports {
532
- return {
533
- /**
534
- * Writes `num` random float32 numbers from given address
535
- */
536
- fillRandom: (addr: number, num: number) => {
537
- addr >>>= 2;
538
- while(num-- > 0) this.parent.f32[addr++] = Math.random();
539
- }
540
- };
541
- }
85
+ // Unique API module identifier to group WASM imports,
86
+ // must match ID used by native code (see further below).
87
+ readonly id = "custom";
88
+ // optionally list IDs of other API modules this module depends on
89
+ // these are used to infer the correct initialization order
90
+ readonly dependencies = [];
91
+
92
+ parent!: WasmBridge;
93
+
94
+ async init(parent: WasmBridge) {
95
+ this.parent = parent;
96
+ this.parent.logger.debug("initializing custom API");
97
+
98
+ // any other tasks you might need to do...
99
+
100
+ return true;
101
+ }
102
+
103
+ /**
104
+ * Returns object of functions to import as externals into the
105
+ * WASM module during instantiation. These imports are merged
106
+ * into a larger imports object alongside the bridge's core API...
107
+ */
108
+ getImports(): WebAssembly.Imports {
109
+ return {
110
+ /**
111
+ * Writes `num` random float32 numbers from given address
112
+ */
113
+ fillRandom: (addr: number, num: number) => {
114
+ addr >>>= 2;
115
+ while(num-- > 0) this.parent.f32[addr++] = Math.random();
116
+ }
117
+ };
118
+ }
542
119
  }
543
120
  ```
544
121
 
@@ -573,16 +150,16 @@ const js = @import("wasmapi");
573
150
  const custom = @import("custom.zig");
574
151
 
575
152
  export fn test_randomVec4() void {
576
- var foo = [4]f32{ 1, 2, 3, 4 };
153
+ var foo = [4]f32{ 1, 2, 3, 4 };
577
154
 
578
- // print original
579
- js.printF32Array(foo[0..]);
155
+ // print original
156
+ js.printF32Array(foo[0..]);
580
157
 
581
- // populate foo with random numbers
582
- custom.fillRandom(&foo, foo.len);
158
+ // populate foo with random numbers
159
+ custom.fillRandom(&foo, foo.len);
583
160
 
584
- // print result
585
- js.printF32Array(foo[0..]);
161
+ // print result
162
+ js.printF32Array(foo[0..]);
586
163
  }
587
164
  ```
588
165
 
@@ -599,6 +176,74 @@ To avoid guesswork about the internals of these API modules, all of them are
599
176
  using an overall uniform structure, with the main Zig entry point in
600
177
  `/zig/lib.zig`...
601
178
 
179
+ ### String handling
180
+
181
+ Most low-level languages deal with strings very differently and alas there's no
182
+ general standard. Some have UTF-8/16 support, others don't. In some languages
183
+ (incl. C & Zig), strings are stored as zero terminated, in others they aren't...
184
+ It's outside the scope of this package to provide an allround out-of-the-box
185
+ solution. The `WasmBridge` provides read & write accessors to obtain JS strings
186
+ from UTF-8 encoded WASM memory. See
187
+ [`getString()`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html#getString)
188
+ and
189
+ [`setString()`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html#setString)
190
+ for details.
191
+
192
+ Furthermore, the package provides these string wrapper types:
193
+
194
+ - [`WasmStringPtr`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmStringPtr.html)
195
+ - [`WasmStringSlice`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmStringSlice.html)
196
+
197
+ Finally, see more information in the
198
+ [@thi.ng/wasm-api-bindgen](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-bindgen/README.md#string-handling)
199
+ package readme.
200
+
201
+ ### Memory allocations
202
+
203
+ If explicitly enabled on the WASM side, the `WasmBridge` includes support for
204
+ malloc/free-style allocations (within the linear WASM memory) from the JS side.
205
+
206
+ The actual allocator is implementation specific and suitable generic mechanisms
207
+ are defined for both the included Zig & C bindings. Please see for further
208
+ reference:
209
+
210
+ - [`/zig/lib.zig`](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/lib.zig#L34-L68):
211
+ comments about WASM-side allocator handling in Zig
212
+ - [`/include/wasmapi.h`](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/include/wasmapi.h#L18-L28):
213
+ comments about WASM-side allocator handling in C/C++
214
+ - [`WasmBridge.allocate()`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html#allocate):
215
+ allocating memory from JS side
216
+ - [`WasmBridge.free()`](https://docs.thi.ng/umbrella/wasm-api/classes/WasmBridge.html#free):
217
+ freeing previously allocated memory from JS side
218
+
219
+ Note: The provided Zig library supports the idiomatic (Zig) pattern of working
220
+ with multiple allocators in different parts of the application and supports
221
+ dynamic assignments/swapping of the exposed allocator. See comments in source
222
+ file and
223
+ [tests](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api/test)
224
+ for more details...
225
+
226
+ ```ts
227
+ try {
228
+ // allocate 256 bytes of memory for passing a string to WASM side
229
+ // the function returns a tuple of `[address, len]`
230
+ const [addr, len] = bridge.allocate(256);
231
+
232
+ // write zero terminated string to reserved memory (max. `len` bytes)
233
+ // function returns number of bytes written (excl. sentinel)
234
+ const num = bridge.setString("hello WASM world!", addr, len, true);
235
+
236
+ // call WASM function doing something w/ the string
237
+ bridge.exports.doSomethingWithString(addr, num);
238
+
239
+ // cleanup
240
+ bridge.free([addr, len]);
241
+ } catch(e) {
242
+ // deal with allocation error
243
+ // ...
244
+ }
245
+ ```
246
+
602
247
  ### Object indices & handles
603
248
 
604
249
  Since only numeric values can be exchanged between the WASM module and the JS
@@ -651,8 +296,8 @@ Since v0.15.0, the supplied Zig core bindings lib also includes a
651
296
  for similar dealings on the Zig side of the application. For example, in the
652
297
  [@thi.ng/wasm-api-dom](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-dom/)
653
298
  &
654
- [@thi.ng/wasm-api-timer](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-timer/)
655
- modules this is used to manage Zig event listeners.
299
+ [@thi.ng/wasm-api-schedule](https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api-schedule/)
300
+ packages this is used to manage Zig event listeners.
656
301
 
657
302
  ## Status
658
303
 
@@ -662,8 +307,9 @@ modules this is used to manage Zig event listeners.
662
307
 
663
308
  ## Support packages
664
309
 
310
+ - [@thi.ng/wasm-api-bindgen](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-bindgen) - Polyglot bindings code generators for hybrid JS & WebAssembly projects
665
311
  - [@thi.ng/wasm-api-dom](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-dom) - Browser DOM bridge API for hybrid TypeScript & WASM (Zig) applications
666
- - [@thi.ng/wasm-api-timer](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-timer) - Delayed & scheduled function execution (via setTimeout() etc.) for hybrid WASM apps
312
+ - [@thi.ng/wasm-api-schedule](https://github.com/thi-ng/umbrella/tree/develop/packages/wasm-api-schedule) - Delayed & scheduled function execution (via setTimeout() etc.) for hybrid WASM apps
667
313
 
668
314
  ## Installation
669
315
 
@@ -688,28 +334,17 @@ node --experimental-repl-await
688
334
  > const wasmApi = await import("@thi.ng/wasm-api");
689
335
  ```
690
336
 
691
- Package sizes (gzipped, pre-treeshake): ESM: 7.14 KB
692
-
693
- **IMPORTANT:** The package includes multiple language code generators which are
694
- **not** required for normal use of the API bridge. Hence, the actual package
695
- size in production will be MUCH smaller than what's stated here!
337
+ Package sizes (gzipped, pre-treeshake): ESM: 2.70 KB
696
338
 
697
339
  ## Dependencies
698
340
 
699
341
  - [@thi.ng/api](https://github.com/thi-ng/umbrella/tree/develop/packages/api)
700
- - [@thi.ng/args](https://github.com/thi-ng/umbrella/tree/develop/packages/args)
701
342
  - [@thi.ng/arrays](https://github.com/thi-ng/umbrella/tree/develop/packages/arrays)
702
- - [@thi.ng/binary](https://github.com/thi-ng/umbrella/tree/develop/packages/binary)
703
343
  - [@thi.ng/checks](https://github.com/thi-ng/umbrella/tree/develop/packages/checks)
704
- - [@thi.ng/compare](https://github.com/thi-ng/umbrella/tree/develop/packages/compare)
705
- - [@thi.ng/defmulti](https://github.com/thi-ng/umbrella/tree/develop/packages/defmulti)
706
344
  - [@thi.ng/errors](https://github.com/thi-ng/umbrella/tree/develop/packages/errors)
707
- - [@thi.ng/file-io](https://github.com/thi-ng/umbrella/tree/develop/packages/file-io)
708
345
  - [@thi.ng/hex](https://github.com/thi-ng/umbrella/tree/develop/packages/hex)
709
346
  - [@thi.ng/idgen](https://github.com/thi-ng/umbrella/tree/develop/packages/idgen)
710
347
  - [@thi.ng/logger](https://github.com/thi-ng/umbrella/tree/develop/packages/logger)
711
- - [@thi.ng/paths](https://github.com/thi-ng/umbrella/tree/develop/packages/paths)
712
- - [@thi.ng/strings](https://github.com/thi-ng/umbrella/tree/develop/packages/strings)
713
348
 
714
349
  ## Usage examples
715
350
 
@@ -719,10 +354,11 @@ directory are using this package.
719
354
 
720
355
  A selection:
721
356
 
722
- | Screenshot | Description | Live demo | Source |
723
- |:-------------------------------------------------------------------------------------------------------------------|:--------------------------------------------|:--------------------------------------------------|:-------------------------------------------------------------------------------|
724
- | <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-canvas.png" width="240"/> | Zig-based DOM creation & canvas drawing app | [Demo](https://demo.thi.ng/umbrella/zig-canvas/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-canvas) |
725
- | <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-counter.png" width="240"/> | Simple Zig/WASM click counter DOM component | [Demo](https://demo.thi.ng/umbrella/zig-counter/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-counter) |
357
+ | Screenshot | Description | Live demo | Source |
358
+ |:---------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------|:----------------------------------------------------|:---------------------------------------------------------------------------------|
359
+ | <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-canvas.png" width="240"/> | Zig-based DOM creation & canvas drawing app | [Demo](https://demo.thi.ng/umbrella/zig-canvas/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-canvas) |
360
+ | <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-counter.png" width="240"/> | Simple Zig/WASM click counter DOM component | [Demo](https://demo.thi.ng/umbrella/zig-counter/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-counter) |
361
+ | <img src="https://raw.githubusercontent.com/thi-ng/umbrella/develop/assets/examples/zig-todo-list.png" width="240"/> | Zig-based To-Do list, DOM creation, local storage task persistence | [Demo](https://demo.thi.ng/umbrella/zig-todo-list/) | [Source](https://github.com/thi-ng/umbrella/tree/develop/examples/zig-todo-list) |
726
362
 
727
363
  ## API
728
364
 
@@ -736,21 +372,21 @@ import { readFileSync } from "fs";
736
372
 
737
373
  // WASM exports from our dummy module (below)
738
374
  interface App extends WasmExports {
739
- start: () => void;
375
+ start: () => void;
740
376
  }
741
377
 
742
378
  (async () => {
743
- // new API bridge with defaults
744
- // (i.e. no child API modules and using console logger)
745
- const bridge = new WasmBridge<App>();
379
+ // new API bridge with defaults
380
+ // (i.e. no child API modules and using console logger)
381
+ const bridge = new WasmBridge<App>();
746
382
 
747
- // instantiate WASM module using imports provided by the bridge
748
- // this also initializes any bindings & bridge child APIs (if any)
749
- // (also accepts a fetch() `Response` as input)
750
- await bridge.instantiate(readFileSync("hello.wasm"));
383
+ // instantiate WASM module using imports provided by the bridge
384
+ // this also initializes any bindings & bridge child APIs (if any)
385
+ // (also accepts a fetch() `Response` as input)
386
+ await bridge.instantiate(readFileSync("hello.wasm"));
751
387
 
752
- // call an exported WASM function
753
- bridge.exports.start();
388
+ // call an exported WASM function
389
+ bridge.exports.start();
754
390
  })();
755
391
  ```
756
392
 
@@ -758,19 +394,15 @@ interface App extends WasmExports {
758
394
 
759
395
  Requires [Zig](https://ziglang.org) to be installed:
760
396
 
761
- ```zig
397
+ ```zig tangle:export/hello.zig
762
398
  //! Example Zig application (hello.zig)
763
399
 
764
400
  /// import externals
765
401
  /// see build command for configuration
766
402
  const js = @import("wasmapi");
767
- const std = @import("std");
768
-
769
- // set custom memory allocator (here to disable)
770
- pub const WASM_ALLOCATOR: ?std.mem.Allocator = null;
771
403
 
772
404
  export fn start() void {
773
- js.printStr("hello world!");
405
+ js.printStr("hello world!");
774
406
  }
775
407
  ```
776
408
 
@@ -781,10 +413,10 @@ folder):
781
413
  ```bash
782
414
  # compile WASM binary
783
415
  zig build-lib \
784
- --pkg-begin wasmapi node_modules/@thi.ng/wasm-api/zig/lib.zig --pkg-end \
785
- -target wasm32-freestanding \
786
- -O ReleaseSmall -dynamic --strip \
787
- hello.zig
416
+ --pkg-begin wasmapi node_modules/@thi.ng/wasm-api/zig/lib.zig --pkg-end \
417
+ -target wasm32-freestanding \
418
+ -O ReleaseSmall -dynamic \
419
+ hello.zig
788
420
 
789
421
  # disassemble WASM
790
422
  wasm-dis -o hello.wast hello.wasm
@@ -819,15 +451,15 @@ The resulting WASM:
819
451
  )
820
452
  ```
821
453
 
822
- ### C11 version
454
+ ### C version
823
455
 
824
456
  Requires [Emscripten](https://emscripten.org/) to be installed:
825
457
 
826
- ```c
458
+ ```c tangle:export/hello.c
827
459
  #include <wasmapi.h>
828
460
 
829
- void WASM_KEEP start() {
830
- wasm_printStr0("hello world!");
461
+ void WASMAPI_KEEP start() {
462
+ wasm_printStrZ("hello world!");
831
463
  }
832
464
  ```
833
465
 
@@ -839,73 +471,6 @@ emcc -Os -Inode_modules/@thi.ng/wasm-api/include \
839
471
  -o hello.wasm hello.c
840
472
  ```
841
473
 
842
- Resulting WASM:
843
-
844
- ```wasm
845
- (module
846
- (type $i32_=>_none (func (param i32)))
847
- (type $none_=>_none (func))
848
- (type $i32_=>_i32 (func (param i32) (result i32)))
849
- (type $none_=>_i32 (func (result i32)))
850
- (type $i32_i32_=>_none (func (param i32 i32)))
851
- (import "wasmapi" "_printStr0" (func $fimport$0 (param i32)))
852
- (global $global$0 (mut i32) (i32.const 5243936))
853
- (memory $0 256 256)
854
- (data (i32.const 1024) "hello world!")
855
- (table $0 2 2 funcref)
856
- (elem (i32.const 1) $0)
857
- (export "memory" (memory $0))
858
- (export "_wasm_allocate" (func $1))
859
- (export "_wasm_free" (func $2))
860
- (export "start" (func $3))
861
- (export "__indirect_function_table" (table $0))
862
- (export "_initialize" (func $0))
863
- (export "__errno_location" (func $7))
864
- (export "stackSave" (func $4))
865
- (export "stackRestore" (func $5))
866
- (export "stackAlloc" (func $6))
867
- (func $0
868
- (nop)
869
- )
870
- (func $1 (param $0 i32) (result i32)
871
- (i32.const 0)
872
- )
873
- (func $2 (param $0 i32) (param $1 i32)
874
- (nop)
875
- )
876
- (func $3
877
- (call $fimport$0
878
- (i32.const 1024)
879
- )
880
- )
881
- (func $4 (result i32)
882
- (global.get $global$0)
883
- )
884
- (func $5 (param $0 i32)
885
- (global.set $global$0
886
- (local.get $0)
887
- )
888
- )
889
- (func $6 (param $0 i32) (result i32)
890
- (global.set $global$0
891
- (local.tee $0
892
- (i32.and
893
- (i32.sub
894
- (global.get $global$0)
895
- (local.get $0)
896
- )
897
- (i32.const -16)
898
- )
899
- )
900
- )
901
- (local.get $0)
902
- )
903
- (func $7 (result i32)
904
- (i32.const 1040)
905
- )
906
- )
907
- ```
908
-
909
474
  ## Authors
910
475
 
911
476
  Karsten Schmidt