当我从 ziglang 返回空终止切片时,Bun FFi 函数失败

问题描述 投票:0回答:1

我正在实现一个 ziglang 函数来生成 ulid。到目前为止,当通过主函数调用时它可以工作。然而,当与 Bun 的 FFI 一起使用时,它会失败。 (console.log) 是一个空字符串。

之字形

const std = @import("std");

pub fn reverse(source: []const u8, target: []u8) void {
    for (source, 0..) |c, i| {
        target[source.len - 1 - i] = c;
    }
}

fn toBase32(bytes: []const u8, str: []u8) void {
    const BASE_32_CHARACTERS = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";

    var bitBuffer: u16 = 0;
    var bitCount: u4 = 0;
    var strIndex: usize = 0;

    for (bytes) |byte| {
        bitBuffer = (bitBuffer << 8) | @as(u16, byte);
        bitCount += 8;

        while (bitCount >= 5) {
            bitCount -= 5;
            const index = (bitBuffer >> bitCount) & 0x1F;
            str[strIndex] = BASE_32_CHARACTERS[index];
            strIndex += 1;
        }
    }

    // Handle any remaining bits
    if (bitCount > 0) {
        const index = (bitBuffer >> (5 - bitCount)) & 0x1F;
        str[strIndex] = BASE_32_CHARACTERS[index];
    }
}

pub export fn ulid() [*:0]const u8 {
    //  01AN4Z07BY      79KA1307SR9X4MV3
    // |----------|    |----------------|
    //   Timestamp          Randomness
    //     48bits             80bits

    const seed = @as(u64, @intCast(std.time.nanoTimestamp()));
    var rng = std.rand.DefaultPrng.init(seed);

    // 1. 48-bit timestamp
    const current_timestamp = std.time.milliTimestamp();

    // change std.mem.toBytes output order from little-endian to big-endian
    var timestamp: [6]u8 = undefined;
    reverse(std.mem.toBytes(current_timestamp)[0..6], timestamp[0..]);

    // 2. 80 bits of pseudo-random data
    var random_data: [10]u8 = undefined;
    for (0..10) |i| {
        random_data[i] = rng.random().uintAtMost(u8, 255);
    }

    // change std.mem.toBytes output order from little-endian to big-endian
    var randomness: [10]u8 = undefined;
    reverse(&std.mem.toBytes(random_data), randomness[0..]);

    // 3. Concatenate the above to form a 128-bit ULID
    var uint8_array: [27:0]u8 = undefined;
    toBase32(&timestamp, uint8_array[0..]);
    toBase32(&randomness, uint8_array[10..]);

    // null terminator
    uint8_array[26] = 0;
    return &uint8_array;
}

JavaScript 代码

import { join } from "path";
import { dlopen, FFIType, suffix } from "bun:ffi";

export const ulid = () => {
    const lib = dlopen(join(process.cwd(), "bin", `libulid.${suffix}`), {
        ulid: {
            args: [],
            returns: FFIType.cstring,
        },
    });

    const str = lib.symbols.ulid();

    console.log(str);
};

但是编写一个返回静态字符串的简单函数,它可以按预期工作。

fn hey() [*:0]const u8 {
    return "Hello, World!";
}

pub export fn hello() [*:0]const u8 {
    return hey();
}
export const hello = () => {
    const lib = dlopen(join(process.cwd(), "bin", `libhello.${suffix}`), {
        hello: {
            args: [],
            returns: FFIType.cstring,
        },
    });

    const str = lib.symbols.hello();

    console.log(str);
};

PS:我是 zig-lang(系统编程)新手,正在我的 JS 应用程序中尝试低级编程。

javascript c-strings zig null-terminated bun
1个回答
0
投票

正如一位评论者指出的那样:您正在返回一个指向在堆栈上分配的局部变量的指针:

uint8_array
。这是未定义的行为。

对 JavaScript 不太熟悉,但我猜你不能从 Zig 返回堆分配的字符串,因为谁释放了它(以及何时)?如果您的 JavaScript 代码是单线程的,您可以在 Zig 中使用全局

uint8_array
变量并将 its 地址返回给 JavaScript。这样你就不会出现内存泄漏。但你可能必须在 JavaScript 中复制该字符串:这是我要尝试的东西。

© www.soinside.com 2019 - 2024. All rights reserved.