@thi.ng/wasm-api 0.5.0 → 0.6.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/bridge.js CHANGED
@@ -28,7 +28,7 @@ export class WasmBridge {
28
28
  this.utf8Encoder = new TextEncoder();
29
29
  const logN = (x) => this.logger.debug(x);
30
30
  const logA = (method) => (addr, len) => this.logger.debug(method(addr, len).join(", "));
31
- this.core = {
31
+ this.api = {
32
32
  printI8: logN,
33
33
  printU8: logN,
34
34
  printU8Hex: (x) => this.logger.debug(`0x${U8(x)}`),
@@ -136,7 +136,7 @@ export class WasmBridge {
136
136
  * bridge.getImports();
137
137
  * {
138
138
  * // imports defined by the core API of the bridge itself
139
- * core: { ... },
139
+ * wasmapi: { ... },
140
140
  * // imports defined by the CustomAPI module
141
141
  * custom: { ... }
142
142
  * }
@@ -151,7 +151,7 @@ export class WasmBridge {
151
151
  */
152
152
  getImports() {
153
153
  if (!this.imports) {
154
- this.imports = { core: this.core };
154
+ this.imports = { wasmapi: this.api };
155
155
  for (let id in this.modules) {
156
156
  if (this.imports[id] !== undefined) {
157
157
  illegalArgs(`attempt to redeclare API module ${id}`);
@@ -176,24 +176,36 @@ export class WasmBridge {
176
176
  * Attempts to allocate `numBytes` using the exported WASM core API function
177
177
  * {@link WasmExports._wasm_allocate} (implementation specific) and returns
178
178
  * start address of the new memory block. If unsuccessful, throws an
179
- * {@link OutOfMemoryError}.
179
+ * {@link OutOfMemoryError}. If `clear` is true, the allocated region will
180
+ * be zero-filled.
180
181
  *
181
182
  * @remarks
182
183
  * See {@link WasmExports._wasm_allocate} docs for further details.
183
184
  *
184
185
  * @param numBytes
186
+ * @param clear
185
187
  */
186
- allocate(numBytes) {
188
+ allocate(numBytes, clear = false) {
187
189
  const addr = this.exports._wasm_allocate(numBytes);
188
190
  if (!addr)
189
191
  throw new OutOfMemoryError(`unable to allocate: ${numBytes}`);
190
192
  this.logger.debug(`allocated ${numBytes} bytes @ 0x${U32(addr)}`);
191
193
  this.ensureMemory();
194
+ clear && this.u8.fill(0, addr, addr + numBytes);
192
195
  return addr;
193
196
  }
194
- free(addr) {
195
- this.logger.debug(`freeing memory @ 0x${U32(addr)}`);
196
- this.exports._wasm_free(addr);
197
+ /**
198
+ * Frees a previous allocated memory region using the exported WASM core API
199
+ * function {@link WasmExports._wasm_free} (implementation specific). The
200
+ * `numBytes` value must be the same as previously given to
201
+ * {@link WasmBridge.allocate}.
202
+ *
203
+ * @param addr
204
+ * @param numBytes
205
+ */
206
+ free(addr, numBytes) {
207
+ this.logger.debug(`freeing memory @ 0x${U32(addr)} .. 0x${U32(addr + numBytes - 1)}`);
208
+ this.exports._wasm_free(addr, numBytes);
197
209
  }
198
210
  getI8(addr) {
199
211
  return this.i8[addr];
@@ -343,9 +355,33 @@ export class WasmBridge {
343
355
  this.f64.set(buf, addr >> 3);
344
356
  return this;
345
357
  }
358
+ /**
359
+ * Reads UTF-8 encoded string from given address and optional byte length.
360
+ * The default length is 0, which will be interpreted as a zero-terminated
361
+ * string. Returns string.
362
+ *
363
+ * @param addr
364
+ * @param len
365
+ */
346
366
  getString(addr, len = 0) {
367
+ this.ensureMemory();
347
368
  return this.utf8Decoder.decode(this.u8.subarray(addr, len > 0 ? addr + len : this.u8.indexOf(0, addr)));
348
369
  }
370
+ /**
371
+ * Encodes given string as UTF-8 and writes it to WASM memory starting at
372
+ * `addr`. By default the string will be zero-terminated and only `maxBytes`
373
+ * will be written. Returns the number of bytes written.
374
+ *
375
+ * @remarks
376
+ * An error will be thrown if the encoded string doesn't fully fit into the
377
+ * designated memory region (also note that there might need to be space for
378
+ * the additional sentinel/termination byte).
379
+ *
380
+ * @param str
381
+ * @param addr
382
+ * @param maxBytes
383
+ * @param terminate
384
+ */
349
385
  setString(str, addr, maxBytes, terminate = true) {
350
386
  maxBytes = Math.min(maxBytes, this.u8.length - addr);
351
387
  const len = this.utf8Encoder.encodeInto(str, this.u8.subarray(addr, addr + maxBytes)).written;
@@ -0,0 +1,22 @@
1
+ import { ICodeGen } from "../api.js";
2
+ export interface TSOpts {
3
+ /**
4
+ * Indentation string
5
+ *
6
+ * @defaultValue "\t"
7
+ */
8
+ indent: string;
9
+ }
10
+ /**
11
+ * TypeScript code generator. Call with options and then pass to
12
+ * {@link generateTypes} (see its docs for further usage).
13
+ *
14
+ * @remarks
15
+ * This codegen generates interface and enum definitions for a {@link TypeColl}
16
+ * given to {@link generateTypes}. For structs it will also generate memory
17
+ * mapped wrappers with fully typed accessors.
18
+ *
19
+ * @param opts
20
+ */
21
+ export declare const TYPESCRIPT: (opts?: Partial<TSOpts>) => ICodeGen;
22
+ //# sourceMappingURL=typescript.d.ts.map
@@ -0,0 +1,146 @@
1
+ import { BIGINT_ARRAY_CTORS, BIT_SHIFTS, TYPEDARRAY_CTORS, } from "@thi.ng/api/typedarray";
2
+ import { isString } from "@thi.ng/checks/is-string";
3
+ import { PKG_NAME, USIZE, } from "../api.js";
4
+ import { isBigNumeric, isNumeric, isPrim, prefixLines } from "./utils.js";
5
+ /**
6
+ * TypeScript code generator. Call with options and then pass to
7
+ * {@link generateTypes} (see its docs for further usage).
8
+ *
9
+ * @remarks
10
+ * This codegen generates interface and enum definitions for a {@link TypeColl}
11
+ * given to {@link generateTypes}. For structs it will also generate memory
12
+ * mapped wrappers with fully typed accessors.
13
+ *
14
+ * @param opts
15
+ */
16
+ export const TYPESCRIPT = (opts) => {
17
+ const { indent } = { indent: "\t", ...opts };
18
+ const I = indent;
19
+ const I2 = I + I;
20
+ const I3 = I2 + I;
21
+ const gen = {
22
+ pre: `import type { WasmTypeBase, WasmTypeConstructor } from "${PKG_NAME}";`,
23
+ doc: (doc, indent, acc) => {
24
+ if (doc.indexOf("\n") !== -1) {
25
+ acc.push(indent + "/**", prefixLines(indent + " * ", doc), indent + " */");
26
+ }
27
+ else {
28
+ acc.push(`${indent}/** ${doc} */`);
29
+ }
30
+ },
31
+ enum: (type, _, acc) => {
32
+ const e = type;
33
+ acc.push(`export enum ${e.name} {`);
34
+ for (let v of e.values) {
35
+ var line = indent;
36
+ if (!isString(v)) {
37
+ v.doc && gen.doc(v.doc, indent, acc);
38
+ line += v.name;
39
+ if (v.value != null)
40
+ line += ` = ${v.value}`;
41
+ }
42
+ else {
43
+ line += v;
44
+ }
45
+ acc.push(line + ",");
46
+ }
47
+ acc.push("}\n");
48
+ return acc;
49
+ },
50
+ struct: (type, types, acc) => {
51
+ const struct = type;
52
+ const returnTypes = {};
53
+ // interface definition
54
+ acc.push(`export interface ${struct.name} extends WasmTypeBase {`);
55
+ for (let f of struct.fields) {
56
+ f.doc && gen.doc(f.doc, indent, acc);
57
+ let line = `${indent}${f.name}: `;
58
+ let rtype = "";
59
+ if (f.tag == "array" || f.tag == "slice" || f.tag === "vec") {
60
+ rtype = isNumeric(f.type)
61
+ ? TYPEDARRAY_CTORS[f.type].name
62
+ : isBigNumeric(f.type)
63
+ ? BIGINT_ARRAY_CTORS[f.type].name
64
+ : f.type + "[]";
65
+ }
66
+ else if (!f.tag || f.tag === "scalar" || f.tag === "ptr") {
67
+ rtype = isBigNumeric(f.type)
68
+ ? "bigint"
69
+ : isNumeric(f.type)
70
+ ? "number"
71
+ : f.type;
72
+ }
73
+ returnTypes[f.name] = rtype;
74
+ acc.push(line + rtype + ";");
75
+ }
76
+ acc.push("}\n");
77
+ // type implementation
78
+ acc.push(`export const $${struct.name}: WasmTypeConstructor<${struct.name}> = (mem) => ({`, `${I}get align() { return ${struct.__align}; },`, `${I}get size() { return ${struct.__size}; },`, `${I}instance: (base) => ({`, `${I2}get __base() { return base; },`, `${I2}get __bytes() { return mem.u8.subarray(base, base + ${struct.__size}); },`);
79
+ for (let f of struct.fields) {
80
+ const offset = f.__offset || 0;
81
+ acc.push(`${I2}get ${f.name}(): ${returnTypes[f.name]} {`);
82
+ const prim = isPrim(f.type);
83
+ if (f.tag === "ptr") {
84
+ acc.push(prim
85
+ ? `${I3}return mem.${f.type}[${__ptrShift(offset, f.type)}];`
86
+ : `${I3}return $${f.type}.instance(${__ptr(offset)});`);
87
+ }
88
+ else if (f.tag === "slice") {
89
+ acc.push(`${I3}const len = ${__ptr(offset + 4)};`, prim
90
+ ? `${I3}const addr = ${__ptrShift(offset, f.type)};
91
+ ${I3}return mem.${f.type}.subarray(addr, addr + len);`
92
+ : `${I3}const addr = ${__ptr(offset)};\n${__mapArray(struct, f, I3)}`);
93
+ }
94
+ else if (f.tag === "array" || f.tag === "vec") {
95
+ acc.push(prim
96
+ ? `${I3}const addr = ${__addrShift(offset, f.type)};
97
+ ${I3}return mem.${f.type}.subarray(addr, addr + ${f.len});`
98
+ : `${I3}const addr = ${__addr(offset)};\n${__mapArray(struct, f, I3, f.len)}`);
99
+ }
100
+ else {
101
+ let setter;
102
+ if (prim) {
103
+ const addr = __mem(f.type, f.__offset);
104
+ acc.push(`${I3}return ${addr};`);
105
+ setter = `${addr} = x`;
106
+ }
107
+ else if (types[f.type].type === "enum") {
108
+ const tag = types[f.type].tag;
109
+ const addr = __mem(tag, f.__offset);
110
+ acc.push(`${I3}return ${addr};`);
111
+ setter = `${addr} = x`;
112
+ }
113
+ else {
114
+ acc.push(`${I3}return $${f.type}(mem).instance(${__addr(offset)});`);
115
+ setter = `mem.u8.set(x.__bytes, ${__addr(offset)})`;
116
+ }
117
+ // close getter
118
+ acc.push(`${I2}},`);
119
+ // setter
120
+ acc.push(`${I2}set ${f.name}(x: ${returnTypes[f.name]}) {`, `${I3}${setter};`);
121
+ }
122
+ // close field accessor
123
+ acc.push(`${I2}},`);
124
+ }
125
+ acc.push(`${I}})\n});\n`);
126
+ return acc;
127
+ },
128
+ };
129
+ return gen;
130
+ };
131
+ /** @internal */
132
+ const __shift = (type) => BIT_SHIFTS[type];
133
+ /** @internal */
134
+ const __addr = (offset) => (offset > 0 ? `(base + ${offset})` : "base");
135
+ /** @internal */
136
+ const __addrShift = (offset, shift) => __addr(offset) + " >>> " + __shift(shift);
137
+ /** @internal */
138
+ const __ptr = (offset) => `mem.${USIZE}[${__addrShift(offset, USIZE)}]`;
139
+ /** @internal */
140
+ const __ptrShift = (offset, shift) => __ptr(offset) + " >>> " + __shift(shift);
141
+ const __mem = (type, offset) => `mem.${type}[${__addrShift(offset, type)}]`;
142
+ /** @internal */
143
+ const __mapArray = (struct, f, indent, len = "len") => prefixLines(indent, `const inst = $${f.type}(mem);
144
+ const slice: ${f.type}[] = [];
145
+ for(let i = 0; i < ${len}; i++) slice.push(inst.instance(addr + i * ${struct.__size}));
146
+ return slice;`);
@@ -0,0 +1,5 @@
1
+ export declare const isNumeric: (x: string) => boolean;
2
+ export declare const isBigNumeric: (x: string) => boolean;
3
+ export declare const isPrim: (x: string) => boolean;
4
+ export declare const prefixLines: (prefix: string, str: string) => string;
5
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1,7 @@
1
+ export const isNumeric = (x) => /^([iu](8|16|32))|(f(32|64))$/.test(x);
2
+ export const isBigNumeric = (x) => /^[iu]64$/.test(x);
3
+ export const isPrim = (x) => isNumeric(x) || isBigNumeric(x);
4
+ export const prefixLines = (prefix, str) => str
5
+ .split("\n")
6
+ .map((line) => prefix + line)
7
+ .join("\n");
@@ -0,0 +1,22 @@
1
+ import type { ICodeGen } from "../api.js";
2
+ export interface ZigOpts {
3
+ /**
4
+ * If true, generates various struct & struct field analysis functions
5
+ * (sizes, alignment, offsets etc.).
6
+ *
7
+ * @defaultValue false
8
+ */
9
+ debug: boolean;
10
+ }
11
+ /**
12
+ * Zig code generator. Call with options and then pass to {@link generateTypes}
13
+ * (see its docs for further usage).
14
+ *
15
+ * @remarks
16
+ * This codegen generates struct and enum definitions for a {@link TypeColl}
17
+ * given to {@link generateTypes}.
18
+ *
19
+ * @param opts
20
+ */
21
+ export declare const ZIG: (opts?: Partial<ZigOpts>) => ICodeGen;
22
+ //# sourceMappingURL=zig.d.ts.map
package/codegen/zig.js ADDED
@@ -0,0 +1,75 @@
1
+ import { isString } from "@thi.ng/checks/is-string";
2
+ import { prefixLines } from "./utils.js";
3
+ /**
4
+ * Zig code generator. Call with options and then pass to {@link generateTypes}
5
+ * (see its docs for further usage).
6
+ *
7
+ * @remarks
8
+ * This codegen generates struct and enum definitions for a {@link TypeColl}
9
+ * given to {@link generateTypes}.
10
+ *
11
+ * @param opts
12
+ */
13
+ export const ZIG = (opts) => {
14
+ const { debug } = { debug: false, ...opts };
15
+ const gen = {
16
+ doc: (doc, indent, acc, topLevel = false) => {
17
+ acc.push(prefixLines(topLevel ? "//! " : indent + "/// ", doc));
18
+ },
19
+ enum: (e, _, acc) => {
20
+ acc.push(`pub const ${e.name} = enum(${e.tag}) {`);
21
+ for (let v of e.values) {
22
+ let line = ` `;
23
+ if (!isString(v)) {
24
+ v.doc && gen.doc(v.doc, " ", acc);
25
+ line += v.name;
26
+ if (v.value != null)
27
+ line += ` = ${v.value}`;
28
+ }
29
+ else {
30
+ line += v;
31
+ }
32
+ acc.push(line + ",");
33
+ }
34
+ acc.push("};\n");
35
+ },
36
+ struct: (struct, _, acc) => {
37
+ const name = struct.name;
38
+ acc.push(`pub const ${name} = struct {`);
39
+ const ftypes = {};
40
+ for (let f of struct.fields) {
41
+ f.doc && gen.doc(f.doc, " ", acc);
42
+ var ftype;
43
+ switch (f.tag) {
44
+ case "array":
45
+ ftype = `[${f.len}]${f.type}`;
46
+ break;
47
+ case "slice":
48
+ ftype = `[]${f.type}`;
49
+ break;
50
+ case "vec":
51
+ ftype = `@Vector(${f.len}, ${f.type})`;
52
+ break;
53
+ case "ptr":
54
+ ftype = `*${f.len ? `[${f.len}]` : ""}${f.type}`;
55
+ break;
56
+ case "scalar":
57
+ default:
58
+ ftype = f.type;
59
+ }
60
+ ftypes[f.name] = ftype;
61
+ acc.push(` ${f.name}: ${ftype},`);
62
+ }
63
+ acc.push("};\n");
64
+ if (!debug)
65
+ return;
66
+ const fn = (fname, body) => `export fn ${name}_${fname}() usize { return ${body}; }`;
67
+ acc.push(fn("align", `@alignOf(${name})`), fn("size", `@sizeOf(${name})`));
68
+ for (let f of struct.fields) {
69
+ acc.push(fn(f.name + "_align", `@alignOf(${ftypes[f.name]})`), fn(f.name + "_offset", `@offsetOf(${name}, "${f.name}")`), fn(f.name + "_size", `@sizeOf(${ftypes[f.name]})`));
70
+ }
71
+ acc.push("");
72
+ },
73
+ };
74
+ return gen;
75
+ };
package/codegen.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { ICodeGen, TypeColl } from "./api.js";
2
+ export interface CodeGenOpts {
3
+ /**
4
+ * Optional string to be injected before generated type defs (but after
5
+ * codegen's own prelude, if any)
6
+ */
7
+ pre: string;
8
+ /**
9
+ * Optional string to be injected after generated type defs (but before
10
+ * codegen's own epilogue, if any)
11
+ */
12
+ post: string;
13
+ }
14
+ export declare const prepareTypes: (types: TypeColl) => void;
15
+ export declare const generateTypes: (types: TypeColl, codegen: ICodeGen, opts?: Partial<CodeGenOpts>) => string;
16
+ //# sourceMappingURL=codegen.d.ts.map
package/codegen.js ADDED
@@ -0,0 +1,116 @@
1
+ import { SIZEOF } from "@thi.ng/api/typedarray";
2
+ import { align } from "@thi.ng/binary/align";
3
+ import { ceilPow2 } from "@thi.ng/binary/pow";
4
+ import { compareByKey } from "@thi.ng/compare/keys";
5
+ import { compareNumDesc } from "@thi.ng/compare/numeric";
6
+ import { DEFAULT, defmulti } from "@thi.ng/defmulti/defmulti";
7
+ import { PKG_NAME, USIZE_SIZE, } from "./api.js";
8
+ import { isNumeric } from "./codegen/utils.js";
9
+ const sizeOf = defmulti((x) => x.type, {}, {
10
+ [DEFAULT]: (field, types) => {
11
+ if (field.__size)
12
+ return field.__size;
13
+ let size = 0;
14
+ if (field.tag === "ptr") {
15
+ size = USIZE_SIZE;
16
+ }
17
+ else if (field.tag === "slice") {
18
+ size = USIZE_SIZE * 2;
19
+ }
20
+ else {
21
+ size = isNumeric(field.type)
22
+ ? SIZEOF[field.type]
23
+ : sizeOf(types[field.type], types);
24
+ if (field.tag == "array" || field.tag === "vec") {
25
+ size *= field.len;
26
+ }
27
+ }
28
+ return (field.__size = align(size, field.__align));
29
+ },
30
+ enum: (type) => {
31
+ if (type.__size)
32
+ return type.__size;
33
+ return (type.__size = SIZEOF[type.tag]);
34
+ },
35
+ struct: (type, types) => {
36
+ if (type.__size)
37
+ return type.__size;
38
+ const struct = type;
39
+ let size = 0;
40
+ for (let f of struct.fields) {
41
+ size = align(size, f.__align);
42
+ f.__offset = size;
43
+ size += sizeOf(f, types);
44
+ }
45
+ return (type.__size = align(size, type.__align));
46
+ },
47
+ });
48
+ const alignOf = defmulti((x) => x.type, {}, {
49
+ [DEFAULT]: (field, types) => {
50
+ if (field.__align)
51
+ return field.__align;
52
+ let align = isNumeric(field.type)
53
+ ? SIZEOF[field.type]
54
+ : alignOf(types[field.type], types);
55
+ if (field.tag === "vec") {
56
+ align *= ceilPow2(field.len);
57
+ }
58
+ field.__align = align;
59
+ return align;
60
+ },
61
+ enum: (e) => {
62
+ return (e.__align = SIZEOF[e.tag]);
63
+ },
64
+ struct: (type, types) => {
65
+ const struct = type;
66
+ let maxAlign = 0;
67
+ for (let f of struct.fields) {
68
+ maxAlign = Math.max(maxAlign, alignOf(f, types));
69
+ }
70
+ return (type.__align = maxAlign);
71
+ },
72
+ });
73
+ const prepareType = defmulti((x) => x.type, {}, {
74
+ [DEFAULT]: (x, types) => {
75
+ if (x.__align && x.__size)
76
+ return;
77
+ alignOf(x, types);
78
+ sizeOf(x, types);
79
+ },
80
+ struct: (x, types) => {
81
+ if (x.__align && x.__size)
82
+ return;
83
+ const struct = x;
84
+ alignOf(struct, types);
85
+ if (struct.auto) {
86
+ struct.fields.sort(compareByKey("__align", compareNumDesc));
87
+ }
88
+ for (let f of struct.fields) {
89
+ if (types[f.type]) {
90
+ prepareType(types[f.type], types);
91
+ }
92
+ }
93
+ sizeOf(struct, types);
94
+ },
95
+ });
96
+ export const prepareTypes = (types) => {
97
+ for (let id in types) {
98
+ prepareType(types[id], types);
99
+ }
100
+ };
101
+ export const generateTypes = (types, codegen, opts = {}) => {
102
+ prepareTypes(types);
103
+ const res = [];
104
+ codegen.doc(`Generated by ${PKG_NAME} at ${new Date().toISOString()} - DO NOT EDIT!`, "", res, true);
105
+ res.push("");
106
+ codegen.pre && res.push(codegen.pre, "");
107
+ opts.pre && res.push(opts.pre, "");
108
+ for (let id in types) {
109
+ const type = types[id];
110
+ type.doc && codegen.doc(type.doc, "", res);
111
+ codegen[type.type](type, types, res);
112
+ }
113
+ opts.post && res.push("", opts.post);
114
+ codegen.post && res.push("", codegen.post);
115
+ return res.join("\n");
116
+ };
@@ -0,0 +1,60 @@
1
+ #pragma once
2
+
3
+ #ifdef __cplusplus
4
+ extern "C" {
5
+ #endif
6
+
7
+ #include <stddef.h>
8
+ #include <stdint.h>
9
+
10
+ #define WASM_IMPORT(MODULE, TYPE, NAME, PREFIX) \
11
+ extern __attribute__((import_module(MODULE), import_name(#NAME))) \
12
+ TYPE PREFIX##NAME
13
+ #define WASM_KEEP __attribute__((used))
14
+
15
+ // Generate stubs only if explicitly disabled by defining this symbol
16
+ #ifdef WASMAPI_NO_MALLOC
17
+ size_t WASM_KEEP _wasm_allocate(size_t num_bytes) { return 0; }
18
+ void WASM_KEEP _wasm_free(size_t addr) {}
19
+ #else
20
+ #include <stdlib.h>
21
+ size_t WASM_KEEP _wasm_allocate(size_t numBytes) {
22
+ return (size_t)malloc(numBytes);
23
+ }
24
+ void WASM_KEEP _wasm_free(size_t addr) { free((void*)addr); }
25
+ #endif
26
+
27
+ WASM_IMPORT("wasmapi", void, printI8, wasm_)(int8_t x);
28
+ WASM_IMPORT("wasmapi", void, printU8, wasm_)(uint8_t x);
29
+ WASM_IMPORT("wasmapi", void, printU8Hex, wasm_)(uint8_t x);
30
+ WASM_IMPORT("wasmapi", void, printI16, wasm_)(int16_t x);
31
+ WASM_IMPORT("wasmapi", void, printU16, wasm_)(uint16_t x);
32
+ WASM_IMPORT("wasmapi", void, printU16Hex, wasm_)(uint16_t x);
33
+ WASM_IMPORT("wasmapi", void, printI32, wasm_)(int32_t x);
34
+ WASM_IMPORT("wasmapi", void, printU32, wasm_)(uint32_t x);
35
+ WASM_IMPORT("wasmapi", void, printU32Hex, wasm_)(uint32_t x);
36
+ WASM_IMPORT("wasmapi", void, printI64, wasm_)(int64_t x);
37
+ WASM_IMPORT("wasmapi", void, printU64, wasm_)(uint64_t x);
38
+ WASM_IMPORT("wasmapi", void, printU64Hex, wasm_)(uint64_t x);
39
+ WASM_IMPORT("wasmapi", void, printF32, wasm_)(float x);
40
+ WASM_IMPORT("wasmapi", void, printF64, wasm_)(double x);
41
+
42
+ WASM_IMPORT("wasmapi", void, _printI8Array, wasm)(void* addr, size_t len);
43
+ WASM_IMPORT("wasmapi", void, _printU8Array, wasm)(void* addr, size_t len);
44
+ WASM_IMPORT("wasmapi", void, _printI16Array, wasm)(void* addr, size_t len);
45
+ WASM_IMPORT("wasmapi", void, _printU16Array, wasm)(void* addr, size_t len);
46
+ WASM_IMPORT("wasmapi", void, _printI32Array, wasm)(void* addr, size_t len);
47
+ WASM_IMPORT("wasmapi", void, _printU32Array, wasm)(void* addr, size_t len);
48
+ WASM_IMPORT("wasmapi", void, _printI64Array, wasm)(void* addr, size_t len);
49
+ WASM_IMPORT("wasmapi", void, _printU64Array, wasm)(void* addr, size_t len);
50
+ WASM_IMPORT("wasmapi", void, _printF32Array, wasm)(void* addr, size_t len);
51
+ WASM_IMPORT("wasmapi", void, _printF64Array, wasm)(void* addr, size_t len);
52
+
53
+ WASM_IMPORT("wasmapi", void, _printStr0, wasm)(void* addr);
54
+ WASM_IMPORT("wasmapi", void, _printStr, wasm)(void* addr, size_t len);
55
+
56
+ void wasm_printPtr(void* ptr) { wasm_printU32Hex((size_t)ptr); }
57
+
58
+ #ifdef __cplusplus
59
+ }
60
+ #endif