WebAssembly function parameter and return value types only support 32/64-bit integers/floats. WebAssembly has no concept of a string. Worse, WebAssembly doesn't even have the concept of an array.
What WebAssembly does have is a linear memory model. One can think of WebAssembly's memory as one large
Uint8Array that can only grow in size.
Neat, if we encode/decode strings from WebAssembly's memory, we can pass the index and length via functions.
Zig makes this easy as it already represents strings as an array of UTF-8 bytes, and when Zig is compiled to WebAssembly, Zig's memory is the same as WebAssembly's memory. Furthermore, Zig keeps track of string as slices. Slices are simply a pointer and length to a thing. A pointer to something in Zig's memory is the same as an index to WebAssembly's memory!
[*]const u8means an slice of unknown length of unmodifiable
[*]const u8is compiled down to a pointer. Pointers in WebAssembly are just indices in memory which are
u32. Therefore, in this context
[*]const u8is the same as
[*:0]u8will be explained later.
Aside: Where is the question string allocated? Why don't we need to use an allocator? For compile time strings, Zig will statically allocate the string somewhere in memory for us. This is why
When we want to encode a string to WebAssembly memory, we run into another problem. Where in memory should we encode the string? We can't just write anywhere into memory, as Zig might already have data there.
What we need to do is ask Zig for space. We need to tell Zig to allocate a segment of memory for us.
For step 2, we need a function that allocates into WebAssembly memory. To do this, in Zig, we wrap
alloc and export it:
std.heap.page_allocatormaps directly to WebAssembly memory.
Aside: Why is the function is named
allocUint8? While it can allocate space for a string, it can be generally used to allocate
Why are we null-terminating the string? Two reasons.
First, strings in Zig are both null-terminated, and length tracked. This allows Zig to easily interop with APIs that expect null-terminated strings and, simultaneously, don't require
O(N) to figure out the length of a string.
Second, WebAssembly multi-value return is not implemented yet. This means we can't return
In Zig, we take the pointer and convert it back to a normal string:
Aside: We can now understand
[*:0]u8to mean a null-terminated slice of unknown length of
std.mem.span scans the memory starting at the pointer until it finds a null byte and returns a normal string.
Do you want to learn the latest web tech while building esports? You're in luck, Battlefy is hiring.