@thi.ng/wasm-api
Version:
Generic, modular, extensible API bridge and infrastructure for hybrid JS & WebAssembly projects
289 lines • 10.9 kB
TypeScript
import type { BigTypedArray, EVENT_ALL, Fn, IDeref, ILength, Keys, TypedArray, Values } from "@thi.ng/api";
import type { WasmBridge } from "./bridge.js";
export declare const EVENT_MEMORY_CHANGED = "memory-changed";
export declare const EVENT_PANIC = "panic";
export type BridgeEventType = typeof EVENT_MEMORY_CHANGED | typeof EVENT_PANIC | typeof EVENT_ALL;
export type BigIntArray = bigint[] | BigInt64Array | BigUint64Array;
export type ReadonlyWasmString = IDeref<string> & ILength & {
readonly addr: number;
};
export interface WasmTypeBase {
/**
* Base address in linear WASM memory.
*/
readonly __base: number;
/**
* Obtain as byte buffer
*/
readonly __bytes: Uint8Array;
}
/**
* Helper type to extract field names of structs/unions based on
* {@link WasmTypeBase} (e.g. those generated by thi.ng/wasm-api-bindgen).
*/
export type WasmTypeKeys<T extends WasmTypeBase> = Keys<Omit<T, Keys<WasmTypeBase>>>;
export interface WasmType<T> {
/**
* Type alignment (in bytes)
*/
readonly align: number;
/**
* Type size (in bytes)
*/
readonly size: number;
/**
* Takes a start address and number of items. Returns an array of memory
* mapped type instances (see {@link WasmType.instance}).
*
* @param base
* @param num
* @returns
*/
instanceArray: (base: number, num: number) => T[];
/**
* Returns a memory-mapped type instance for given start address.
*
* @param base
*/
instance: (base: number) => T;
}
export type WasmTypeConstructor<T> = Fn<IWasmMemoryAccess, WasmType<T>>;
/**
* Common interface for WASM/JS child APIs which will be used in combination
* with a parent {@link WasmBridge}.
*
* @remarks
* The generic type param is optional and only used if the API is requiring
* certain exports declared by WASM module.
*/
export interface IWasmAPI<T extends WasmExports = WasmExports> {
/**
* Called by {@link WasmBridge.init} to initialize all child APIs (async)
* after the WASM module has been instantiated. If the method returns false
* the overall initialization process will be stopped/terminated.
*
* @param parent
*/
init(parent: WasmBridge<T>): Promise<boolean>;
/**
* Returns an object of this child API's declared WASM imports. Be aware
* imports from all child APIs will be merged into a single flat namespace,
* it's recommended to use naming prefixes to avoid clashes.
*/
getImports(): WebAssembly.ModuleImports;
}
export interface WasmModuleSpec<T extends WasmExports = WasmExports> {
/**
* The unique ID for grouping the WASM imports of this module. MUST be the
* same as used by the native side of the module.
*/
id: string;
/**
* Optional array of other {@link WasmModuleSpec}s which this module depends
* on (element order is irrelevant). Used to construct a module dependency
* graph and the correct initialization order of modules. The core module
* (defined via {@link WasmBridge} w/ unique ID: `wasmapi`) is always
* considered an implicit dependency, will be initialized first and MUST NOT
* be stated here.
*/
deps?: WasmModuleSpec<T>[];
/**
* Factory function to pre-instantiate the API module.
*
* @remarks
* Note: All modules will only be fully initialized at a later point via
* {@link WasmBridge.instantiate} or {@link WasmBridge.init} and each
* modules own {@link IWasmAPI.init} method.
*/
factory: Fn<WasmBridge<T>, IWasmAPI<T>>;
}
/**
* Base interface of exports declared by the WASM module. At the very least, a
* compatible module needs to export its memory and the functions defined in
* this interface.
*
* @remarks
* This interface is supposed to be extended with the concrete exports defined
* by your WASM module and is used as generic type param for {@link WasmBridge}
* and any {@link IWasmAPI} bridge modules. These exports can obtained via
* {@link WasmBridge.exports} where they will be stored during the execution of
* {@link WasmBridge.init}.
*/
export interface WasmExports {
/**
* The WASM module's linear memory buffer. The `WasmBridge` automatically
* creates various typed views of that memory (i.e. u8, u16, u32, f32 etc.)
*/
memory: WebAssembly.Memory;
/**
* Implementation specific WASM memory allocation function. If successful
* returns address of new memory block, or zero if unsuccessful.
*
* @remarks
* #### Zig
*
* Using the supplied Zig bindings (see `/zig/lib.zig`), it's the
* user's responsibility to define a public `WASM_ALLOCATOR` in the root
* source file to enable allocations, e.g. using the
* [`std.heap.GeneralPurposeAllocator`](https://ziglang.org/documentation/master/#Choosing-an-Allocator)
* (which also automatically handles growing the WASM memory). However, as
* mentioned, the underlying mechanism is purposefully left to the actual
* WASM-side implementation. If no allocator is defined this function
* returns zero, which in turn will cause {@link WasmBridge.allocate} to
* throw an error.
*
* #### C/C++
*
* Using the supplied C bindings (see `/include/wasmapi.h`), it's the user's
* responsibility to enable allocation support by defining the
* `WASMAPI_MALLOC` symbol (and compiling the WASM module with a malloc
* implementation).
*/
_wasm_allocate(numBytes: number): number;
/**
* Implementation specific function to free a previously allocated chunk of
* of WASM memory (allocated via {@link WasmExports._wasm_allocate}, also
* see remarks for that function).
*
* @param addr
* @param numBytes
*/
_wasm_free(addr: number, numBytes: number): void;
}
export type MemorySlice = [addr: number, len: number];
export type MemoryViewType = "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" | "f32" | "f64";
export interface MemoryViewTypeMap extends Record<MemoryViewType, TypedArray | BigTypedArray> {
u8: Uint8Array;
u8c: Uint8ClampedArray;
i8: Int8Array;
u16: Uint16Array;
i16: Int16Array;
u32: Uint32Array;
i32: Int32Array;
i64: BigInt64Array;
u64: BigUint64Array;
f32: Float32Array;
f64: Float64Array;
}
export type MemoryView = Values<MemoryViewTypeMap>;
export interface IWasmMemoryAccess {
i8: Int8Array;
u8: Uint8Array;
i16: Int16Array;
u16: Uint16Array;
i32: Int32Array;
u32: Uint32Array;
i64: BigInt64Array;
u64: BigUint64Array;
f32: Float32Array;
f64: Float64Array;
/**
* Initializes and/or updates the various typed WASM memory views (e.g.
* after growing the WASM memory and the previous buffer becoming detached).
*/
ensureMemory(): void;
/**
* Attempts to grow the WASM memory by an additional `numPages` (64KB/page)
* and if successful updates all typed memory views to use the new
* underlying buffer.
*
* @param numPages
*/
growMemory(numPages: number): void;
/**
* Attempts to allocate `numBytes` using the exported WASM core API function
* {@link WasmExports._wasm_allocate} (implementation specific) and returns
* start address of the new memory block. If unsuccessful, throws an
* {@link OutOfMemoryError}. If `clear` is true, the allocated region will
* be zero-filled.
*
* @remarks
* See {@link WasmExports._wasm_allocate} docs for further details.
*
* @param numBytes
* @param clear
*/
allocate(numBytes: number, clear?: boolean): MemorySlice;
/**
* Frees a previous allocated memory region using the exported WASM core API
* function {@link WasmExports._wasm_free} (implementation specific). The
* `numBytes` value must be the same as previously given to
* {@link IWasmMemoryAccess.allocate}.
*
* @remarks
* This function always succeeds, regardless of presence of an active
* allocator on the WASM side or validity of given arguments.
*
* @param slice
*/
free(slice: MemorySlice): void;
/**
* Reads UTF-8 encoded string from given address and optional byte length.
* The default length is 0, which will be interpreted as a zero-terminated
* string. Returns string.
*
* @param addr
* @param len
*/
getString(addr: number, len?: number): string;
/**
* Encodes given string as UTF-8 and writes it to WASM memory starting at
* `addr`. By default the string will be zero-terminated and only `maxBytes`
* will be written. Returns the number of bytes written (excluding final
* sentinel, if any).
*
* @remarks
* An error will be thrown if the encoded string doesn't fully fit into the
* designated memory region (also note that there might need to be space for
* the additional sentinel/termination byte).
*
* @param str
* @param addr
* @param maxBytes
* @param terminate
*/
setString(str: string, addr: number, maxBytes: number, terminate?: boolean): number;
}
/**
* Core API of WASM imports defined by the {@link WasmBridge}. The same
* functions are declared as bindings in `/zig/lib.zig`. **Also see this
* file for documentation of each function...**
*
* @remarks
* Zig API:
* https://github.com/thi-ng/umbrella/blob/develop/packages/wasm-api/zig/lib.zig
*/
export interface CoreAPI extends WebAssembly.ModuleImports {
printI8: Fn<number, void>;
printU8: Fn<number, void>;
printU8Hex: Fn<number, void>;
printI16: Fn<number, void>;
printU16: Fn<number, void>;
printU16Hex: Fn<number, void>;
printI32: Fn<number, void>;
printU32: Fn<number, void>;
printU32Hex: Fn<number, void>;
printI64: Fn<bigint, void>;
printU64: Fn<bigint, void>;
printU64Hex: Fn<bigint, void>;
printF32: Fn<number, void>;
printF64: Fn<number, void>;
_printI8Array: (addr: number, len: number) => void;
_printU8Array: (addr: number, len: number) => void;
_printI16Array: (addr: number, len: number) => void;
_printU16Array: (addr: number, len: number) => void;
_printI32Array: (addr: number, len: number) => void;
_printU32Array: (addr: number, len: number) => void;
_printI64Array: (addr: number, len: number) => void;
_printU64Array: (addr: number, len: number) => void;
_printF32Array: (addr: number, len: number) => void;
_printF64Array: (addr: number, len: number) => void;
printStrZ: (addr: number) => void;
_printStr: (addr: number, len: number) => void;
printHexdump: (addr: number, len: number) => void;
debug: () => void;
_panic: (addr: number, len: number) => void;
timer: () => number;
epoch: () => bigint;
}
//# sourceMappingURL=api.d.ts.map