Cast, coercion, or copy, when creating c-strings for wasm/javascript

I need to pass some strings over to Javascript from my Zig/Wasm code. eg:

pub extern fn logString(message: [*:0]const u8) void;

No problem when passing a const “mystring” type value, but If I try to use an array/slice I start getting into difficulties.

I am struggling to understanding sentinel terminated arrays and type conversions. Currently I am creating a color string to pass over, but struggling to get the types correct. Some examples of where I am going wrong:

//var c_string: [*:0]u8 = "#000000";
//var c_string: [*:0]u8 = &"#000000";
//var c_string: [*:0]u8 = *"#000000";
//var c_string: [*:0]u8 = "#000000"[0..:0];
//var c_string: [*:0]u8 = @ptrCast([*:0]u8, "#000000");
//var c_string: [*:0]u8 = "#000000"[0..:0];
//var c_string: [*:0]u8 = "#000000"[0..];
//var c_string: [*:0]u8 = "#000000".ptr;
//var c_string = [_:0]u8{ '#', '0', '0', '0', '0', '0', '0', 0 };

js.logString(c_string);

I think I tried most things I could think of, and eventually ended up with some code that creates a buffer and copies the bytes into it, that actually compiles, but I dont see a good value arriving on the javascript side.

 const d: f32 = 59;
    var b: [8]u8 = undefined;
    const ratio: u8 = @floatToInt(u8, (255.0 - ((d / maxStarDistance) * 255.0)));
    const color = bufPrint(b[0..], "#{X}{X}{X}", .{ ratio, ratio, ratio }) catch unreachable;
    var x = [_:0]u8{0} ** 7;
    for (color) |c, i| {
        x[i] = c;
    }
    js.logString(x[0..:0]);

On the javascript side I am using the ptr into memory and scanning for the terminating 0. Are there any good examples around for using Zig with WASM, I got that tip from one of only 2 examples I found so far.

You should be able to simply do

const c_string = "#000000";
// ...
js.logString(&c_string);

If you have an error it most probably has to do with something else, not the pointer type.

and the type of errors I am seeing:

error: expected type '[*:0]const u8', found '[3:0]u8'

Which version of Zig are you using? This should work with a nightly build.
Also I noticed I made a mistake with my answer above, sorry.
String literals are pointers already so you don’t need to take their pointer when passing them to logString.

Here you can see the fixed code compiling successfully on godbolt

With your example I get this:

./src/main.zig:127:31: error: expected type '[*:0]const u8', found '*const *const [7:0]u8'
                js.logString(&c_string);
                              ^
./src/main.zig:127:31: note: destination pointer requires a terminating '0' sentinel
                js.logString(&c_string);

Oops I am replying out of sequence here, sorry. I am using 0.7.1 (last supported by my macos Sierra 10.12 I think). I am on the road so this is the only machine I have access to currently. So maybe this is something that has been fixed then. Or maybe something on my javascript side, since now it seems like I always get now CC being passed, or #CCCCCC. I try to create a simple example. Thanks for your patience.

No worries, yeah it seems you hit a compiler bug in 0.7.1. That said though, switching on godbolt from “zig trunk” (aka nightly builds) to “zig 0.7.1” still succeeds at compiling my example. Maybe you have too much expression / anonymous struct literal stuff going on and it’s stressing a sore spot in the stage1 compiler?

In any case, I’m assuming you tried newer builds of Zig and tested that they actually do not work on macos sierra, so I guess the only alternative I can suggest is to use a virtual machine with linux.

I think https://github.com/ziglang/zig/issues/3779 took care of this but it was definitely after 0.7.1.

I took your advice and tried a new version, good tip! 0.8.1 works, and my .wasm binary is less than half the size it was with 071, well done team Zig! :slight_smile:

With 090 I get the following error:

dyld: Library not loaded: /usr/lib/libncurses.5.4.dylib
Referenced from: …/zig090/zig
Reason: Incompatible library version: zig requires version 5.40.0 or later, but libncurses.5.4.dylib provides version 5.4.0

Anyway, now that I am able to correctly pass the strings over to javascript, I added some debug on the javascript side, and found the problem with ‘the same colors’. The color string arrives as a pointer, and as I mentioned, the code I use scans for a null terminator to then decode the memory block into a string.
However at this point it caches the string. I thought that was fine, since the string changes on each frame. But I found that the pointer address of the ‘var’ being passed over to javascript is the same on each frame… and therefore it was using the cached color string :slight_smile: Thanks all for the help!

2 Likes

So I also finally found a way to avoid the copy loop into the temporary x buffer :slight_smile:

const colour = @ptrCast([*:0]const u8, color);
js.logString(colour);