Hiro
Hiro•2y ago

Pass string to struct FFI

Quick question about Struct-Value feature, what would be the best way to pass *const u8 (or strings) to structs? I am kinda lost after reading the tests to get some examples running, here's what I got working so far :
const lib = Deno.dlopen("./target/debug/libtest_struct.so", {
add: {
parameters: [{struct: ["buffer"] }],
result: "void"
}
})

const buff1 = new TextEncoder().encode("Hello\0").buffer

const ptr = Deno.UnsafePointer.of(buff1);
const struct = new BigUint64Array([BigInt(ptr)]);

lib.symbols.add(struct)
const lib = Deno.dlopen("./target/debug/libtest_struct.so", {
add: {
parameters: [{struct: ["buffer"] }],
result: "void"
}
})

const buff1 = new TextEncoder().encode("Hello\0").buffer

const ptr = Deno.UnsafePointer.of(buff1);
const struct = new BigUint64Array([BigInt(ptr)]);

lib.symbols.add(struct)
use std::ffi;

#[derive(Debug)]
#[repr(C)]
pub struct Point {
pub test: *const u8,
}


#[no_mangle]
pub extern "C" fn add(points: Point) {
let ptr = points.test;
let strtest = unsafe { ffi::CStr::from_ptr(ptr as *const i8) };
println!("strtest: {:?}", strtest);
}
use std::ffi;

#[derive(Debug)]
#[repr(C)]
pub struct Point {
pub test: *const u8,
}


#[no_mangle]
pub extern "C" fn add(points: Point) {
let ptr = points.test;
let strtest = unsafe { ffi::CStr::from_ptr(ptr as *const i8) };
println!("strtest: {:?}", strtest);
}
There must be a beter way of doing this right?
5 Replies
AapoAlas
AapoAlas•2y ago
That's exactly how you'd create a struct that contains a single pointer. There are some libs on /x/ that automate some of this stuff for you, but that's basically how you do byte stuff, it's quite manual.
Hiro
Hiro•2y ago
Yeah I just wanted to be sure I was understanding this properly before going into coding "real stuff" Thanks :D
AapoAlas
AapoAlas•2y ago
One thing I'll warn is that if you never use buff1 after the call but keep using ptr, eventually V8 will GC the buffer after which you'll have use-after-free. So, make sure you force the lifetime of the pointer and the buffer to be the same, eg. by keeping them in the same object that you pass around instead of just the plain pointer. Or, if you know your struct string well enough, you can overallocate enough to write the string into the buffer itself.
Hiro
Hiro•2y ago
Or, if you know your struct string well enough, you can overallocate enough to write the string into the buffer itself. That I do not understand really well 😅 but thanks for the tips
AapoAlas
AapoAlas•2y ago
ie. Let's say you know the string is always one-byte characters. Then you can allocate your buffer as struct size + string length + 1 (null byte). Then you write the string into the buffer after the struct using encodeInto and get that place's pointer using subarray + Deno.UnsafePointer.of. Then your buffer will contain both the struct and the string that it is pointing to, ensuring that their lifetime is exactly equal.