我正在实现一个 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(×tamp, 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 应用程序中尝试低级编程。
正如一位评论者指出的那样:您正在返回一个指向在堆栈上分配的局部变量的指针:
uint8_array
。这是未定义的行为。
对 JavaScript 不太熟悉,但我猜你不能从 Zig 返回堆分配的字符串,因为谁释放了它(以及何时)?如果您的 JavaScript 代码是单线程的,您可以在 Zig 中使用全局
uint8_array
变量并将 its 地址返回给 JavaScript。这样你就不会出现内存泄漏。但你可能必须在 JavaScript 中复制该字符串:这是我要尝试的东西。