@thi.ng/wasm-api 2.0.4 → 2.1.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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2024-10-08T20:41:04Z
3
+ - **Last updated**: 2024-11-09T15:34:31Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,18 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [2.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@2.1.0) (2024-11-09)
13
+
14
+ #### 🚀 Features
15
+
16
+ - add null handling in WasmStringPtr ([e8a094d](https://github.com/thi-ng/umbrella/commit/e8a094d))
17
+ - update logic to interpret as null pointer if addr=0
18
+ - update doc strings
19
+ - add null pointer checks, update tests ([247cbbf](https://github.com/thi-ng/umbrella/commit/247cbbf))
20
+ - update Pointer/Pointer64 deref logic to return undefined for null pointers
21
+ - add `.isNull` getter for all pointer types (incl. `WasmStringPtr`)
22
+ - update tests
23
+
12
24
  # [2.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/wasm-api@2.0.0) (2024-08-18)
13
25
 
14
26
  #### 🛑 Breaking changes
package/README.md CHANGED
@@ -460,7 +460,7 @@ Browser ESM import:
460
460
 
461
461
  [JSDelivr documentation](https://www.jsdelivr.com/)
462
462
 
463
- Package sizes (brotli'd, pre-treeshake): ESM: 3.00 KB
463
+ Package sizes (brotli'd, pre-treeshake): ESM: 3.05 KB
464
464
 
465
465
  ## Dependencies
466
466
 
@@ -520,7 +520,7 @@ interface App extends WasmExports {
520
520
 
521
521
  ### Zig version
522
522
 
523
- Requires [Zig](https://ziglang.org) to be installed:
523
+ Requires [Zig](https://ziglang.org) (v0.13.x) to be installed:
524
524
 
525
525
  ```zig tangle:export/hello.zig
526
526
  //! Example Zig application (hello.zig)
@@ -539,7 +539,7 @@ scenarios add the supplied .zig file(s) to your `build.zig` and/or source
539
539
  folder):
540
540
 
541
541
  ```bash
542
- # compile WASM binary (zig v0.12)
542
+ # compile WASM binary (zig v0.13)
543
543
  zig build-exe \
544
544
  -fno-entry -fstrip -OReleaseSmall -target wasm32-freestanding \
545
545
  --name hello -rdynamic --import-symbols \
@@ -555,24 +555,29 @@ The resulting WASM:
555
555
 
556
556
  ```wasm
557
557
  (module
558
- (type $t0 (func (param i32 i32)))
559
- (type $t1 (func))
560
- (type $t2 (func (param i32) (result i32)))
561
- (import "wasmapi" "_printStr" (func $wasmapi._printStr (type $t0)))
562
- (func $start (type $t1)
563
- (call $wasmapi._printStr
564
- (i32.const 1048576)
565
- (i32.const 12)))
566
- (func $_wasm_allocate (type $t2) (param $p0 i32) (result i32)
567
- (i32.const 0))
568
- (func $_wasm_free (type $t0) (param $p0 i32) (param $p1 i32))
569
- (memory $memory 17)
570
- (global $g0 (mut i32) (i32.const 1048576))
571
- (export "memory" (memory $memory))
572
- (export "start" (func $start))
573
- (export "_wasm_allocate" (func $_wasm_allocate))
574
- (export "_wasm_free" (func $_wasm_free))
575
- (data $d0 (i32.const 1048576) "hello world!\00"))
558
+ (type $0 (func (param i32 i32)))
559
+ (type $1 (func))
560
+ (type $2 (func (param i32) (result i32)))
561
+ (import "wasmapi" "_printStr" (func $fimport$0 (param i32 i32)))
562
+ (global $global$0 (mut i32) (i32.const 1048576))
563
+ (memory $0 17)
564
+ (data $0 (i32.const 1048576) "hello world!\00")
565
+ (export "memory" (memory $0))
566
+ (export "start" (func $0))
567
+ (export "_wasm_allocate" (func $1))
568
+ (export "_wasm_free" (func $2))
569
+ (func $0
570
+ (call $fimport$0
571
+ (i32.const 1048576)
572
+ (i32.const 12)
573
+ )
574
+ )
575
+ (func $1 (param $0 i32) (result i32)
576
+ (i32.const 0)
577
+ )
578
+ (func $2 (param $0 i32) (param $1 i32)
579
+ )
580
+ )
576
581
  ```
577
582
 
578
583
  ### C version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/wasm-api",
3
- "version": "2.0.4",
3
+ "version": "2.1.0",
4
4
  "description": "Generic, modular, extensible API bridge and infrastructure for hybrid JS & WebAssembly projects",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -33,12 +33,14 @@
33
33
  "doc:readme": "bun ../../tools/src/module-stats.ts && bun ../../tools/src/readme.ts",
34
34
  "pub": "yarn npm publish --access public",
35
35
  "test": "zig test zig/tests.zig && bun test",
36
- "test:build-zig": "zig build-lib -O ReleaseSmall -target wasm32-freestanding -dynamic --strip --pkg-begin wasmapi zig/lib.zig --pkg-end test/custom.zig && wasm-dis -o custom.wast custom.wasm && cp custom.wasm test",
36
+ "test:build-zig-allocators": "zig build-exe -fno-entry -fstrip --stack 4096 -OReleaseSmall -target wasm32-freestanding --dep wasmapi -Mroot=test/allocators.zig -Mwasmapi=zig/lib.zig --name allocators -rdynamic --import-symbols && wasm-dis -o allocators.wast allocators.wasm && mv allocators.wasm test",
37
+ "test:build-zig-custom": "zig build-exe -fno-entry -fstrip --stack 4096 -OReleaseSmall -target wasm32-freestanding --dep wasmapi -Mroot=test/custom.zig -Mwasmapi=zig/lib.zig --name custom -rdynamic --import-symbols && wasm-dis -o custom.wast custom.wasm && mv custom.wasm test",
38
+ "test:build-zig": "yarn test:build-zig-allocators && yarn test:build-zig-custom",
37
39
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
38
40
  },
39
41
  "dependencies": {
40
42
  "@thi.ng/api": "^8.11.11",
41
- "@thi.ng/arrays": "^2.10.3",
43
+ "@thi.ng/arrays": "^2.10.4",
42
44
  "@thi.ng/checks": "^3.6.13",
43
45
  "@thi.ng/errors": "^2.5.17",
44
46
  "@thi.ng/hex": "^2.3.55",
@@ -119,5 +121,5 @@
119
121
  "status": "alpha",
120
122
  "year": 2022
121
123
  },
122
- "gitHead": "9219c4c1d5fff4869d8afce6c06531e3b08a1527\n"
124
+ "gitHead": "c2c371069911fdae138149fdcf937c4ad7a8c71e\n"
123
125
  }
package/pointer.d.ts CHANGED
@@ -10,15 +10,19 @@ import type { IWasmMemoryAccess } from "./api.js";
10
10
  * The pointer always behaves like `volatile`, i.e. memoization of target values
11
11
  * is purposfully avoided and the wrapper function is executed anew _each_ time
12
12
  * the pointer is deref'd.
13
+ *
14
+ * Pointers with addr=0 are interpreted as null/optional pointers and
15
+ * {@link Pointer.deref} will return `undefined` in these cases.
13
16
  */
14
- export declare class Pointer<T> implements IDeref<T> {
17
+ export declare class Pointer<T> implements IDeref<T | undefined> {
15
18
  readonly mem: IWasmMemoryAccess;
16
19
  readonly base: number;
17
20
  readonly fn: Fn<number, T>;
18
21
  constructor(mem: IWasmMemoryAccess, base: number, fn: Fn<number, T>);
19
22
  get addr(): number;
20
23
  set addr(addr: number);
21
- deref(): T;
24
+ get isNull(): boolean;
25
+ deref(): T | undefined;
22
26
  }
23
27
  /**
24
28
  * Generic pointer facility for
@@ -31,14 +35,18 @@ export declare class Pointer<T> implements IDeref<T> {
31
35
  * The pointer always behaves like `volatile`, i.e. memoization of target values
32
36
  * is purposefully avoided and the wrapper function is executed anew _each_ time
33
37
  * the pointer is deref'd.
38
+ *
39
+ * Pointers with addr=0 are interpreted as null/optional pointers and
40
+ * {@link Pointer64.deref} will return `undefined` in these cases.
34
41
  */
35
- export declare class Pointer64<T> implements IDeref<T> {
42
+ export declare class Pointer64<T> implements IDeref<T | undefined> {
36
43
  readonly mem: IWasmMemoryAccess;
37
44
  readonly base: bigint;
38
45
  readonly fn: Fn<bigint, T>;
39
46
  constructor(mem: IWasmMemoryAccess, base: bigint, fn: Fn<bigint, T>);
40
47
  get addr(): bigint;
41
48
  set addr(addr: bigint);
42
- deref(): T;
49
+ get isNull(): boolean;
50
+ deref(): T | undefined;
43
51
  }
44
52
  //# sourceMappingURL=pointer.d.ts.map
package/pointer.js CHANGED
@@ -5,13 +5,19 @@ class Pointer {
5
5
  this.fn = fn;
6
6
  }
7
7
  get addr() {
8
+ this.mem.ensureMemory();
8
9
  return this.mem.u32[this.base >>> 2];
9
10
  }
10
11
  set addr(addr) {
12
+ this.mem.ensureMemory();
11
13
  this.mem.u32[this.base >>> 2] = addr;
12
14
  }
15
+ get isNull() {
16
+ return this.addr === 0;
17
+ }
13
18
  deref() {
14
- return this.fn(this.addr);
19
+ const addr = this.addr;
20
+ return addr ? this.fn(addr) : void 0;
15
21
  }
16
22
  }
17
23
  class Pointer64 {
@@ -21,13 +27,19 @@ class Pointer64 {
21
27
  this.fn = fn;
22
28
  }
23
29
  get addr() {
30
+ this.mem.ensureMemory();
24
31
  return this.mem.u64[Number(this.base >> BigInt(3))];
25
32
  }
26
33
  set addr(addr) {
34
+ this.mem.ensureMemory();
27
35
  this.mem.u64[Number(this.base >> BigInt(3))] = addr;
28
36
  }
37
+ get isNull() {
38
+ return this.addr === 0n;
39
+ }
29
40
  deref() {
30
- return this.fn(this.addr);
41
+ const addr = this.addr;
42
+ return addr ? this.fn(addr) : void 0;
31
43
  }
32
44
  }
33
45
  export {
package/string.d.ts CHANGED
@@ -80,7 +80,11 @@ export declare class WasmStringSlice implements ReadonlyWasmString {
80
80
  /**
81
81
  * Memory mapped string wrapper for C-style UTF-8 encoded and **always**
82
82
  * zero-terminated char pointers. The actual JS string can be obtained via
83
- * {@link WasmStringSlice.deref} and mutated via {@link WasmStringSlice.set}.
83
+ * {@link WasmStringPtr.deref} and mutated via {@link WasmStringPtr.set}.
84
+ *
85
+ * @remarks
86
+ * Pointers with addr=0 are interpreted as null/optional pointers. See
87
+ * {@link WasmStringPtr.length} and {@link WasmStringPtr.deref} for details.
84
88
  */
85
89
  export declare class WasmStringPtr implements ReadonlyWasmString {
86
90
  readonly mem: IWasmMemoryAccess;
@@ -92,13 +96,19 @@ export declare class WasmStringPtr implements ReadonlyWasmString {
92
96
  */
93
97
  get addr(): number;
94
98
  set addr(addr: number);
99
+ get isNull(): boolean;
95
100
  /**
96
101
  * Returns computed string length (scanning memory for zero sentinel)
102
+ *
103
+ * @remarks
104
+ * Always returns 0 if null pointer (i.e. if {@link WasmStringPtr.addr} is
105
+ * zero).
97
106
  */
98
107
  get length(): number;
99
108
  /**
100
- * Returns memory as JS string (aka wrapper for
101
- * {@link WasmBridge.getString}).
109
+ * Returns memory as JS string (via {@link WasmBridge.getString}). Returns
110
+ * empty string if null pointer (i.e. if {@link WasmStringPtr.addr} is
111
+ * zero).
102
112
  */
103
113
  deref(): string;
104
114
  /**
package/string.js CHANGED
@@ -119,20 +119,30 @@ class WasmStringPtr {
119
119
  this.mem.ensureMemory();
120
120
  this.mem.u32[this.base >>> 2] = addr;
121
121
  }
122
+ get isNull() {
123
+ return this.addr === 0;
124
+ }
122
125
  /**
123
126
  * Returns computed string length (scanning memory for zero sentinel)
127
+ *
128
+ * @remarks
129
+ * Always returns 0 if null pointer (i.e. if {@link WasmStringPtr.addr} is
130
+ * zero).
124
131
  */
125
132
  get length() {
126
- this.mem.ensureMemory();
127
- const idx = this.mem.u8.indexOf(0, this.addr);
128
- return idx >= 0 ? idx - this.addr : 0;
133
+ const addr = this.addr;
134
+ if (!addr) return 0;
135
+ const idx = this.mem.u8.indexOf(0, addr);
136
+ return idx >= 0 ? idx - addr : 0;
129
137
  }
130
138
  /**
131
- * Returns memory as JS string (aka wrapper for
132
- * {@link WasmBridge.getString}).
139
+ * Returns memory as JS string (via {@link WasmBridge.getString}). Returns
140
+ * empty string if null pointer (i.e. if {@link WasmStringPtr.addr} is
141
+ * zero).
133
142
  */
134
143
  deref() {
135
- return this.mem.getString(this.addr, this.length);
144
+ const addr = this.addr;
145
+ return addr ? this.mem.getString(addr, this.length) : "";
136
146
  }
137
147
  /**
138
148
  * If given a JS string as arg (and if this `WasmStringPtr` instance itself
@@ -156,6 +166,7 @@ class WasmStringPtr {
156
166
  */
157
167
  set(str) {
158
168
  const addr = this.addr;
169
+ if (!addr) unsupported("can't mutate null pointer");
159
170
  if (typeof str === "string") {
160
171
  if (this.isConst) unsupported("can't mutate const string");
161
172
  this.mem.ensureMemory();