121 lines
4.5 KiB
JavaScript
121 lines
4.5 KiB
JavaScript
export function readString(wasm, ptr, len) {
|
|
const buf = new Uint8Array(wasm.memory.buffer, ptr, len);
|
|
return (new TextDecoder("utf-8")).decode(buf)
|
|
}
|
|
|
|
export function withWriteable(wasm, callback) {
|
|
const writeable = wasm.diplomat_buffer_writeable_create(0);
|
|
try {
|
|
callback(writeable);
|
|
const outStringPtr = wasm.diplomat_buffer_writeable_get_bytes(writeable);
|
|
const outStringLen = wasm.diplomat_buffer_writeable_len(writeable);
|
|
return readString(wasm, outStringPtr, outStringLen);
|
|
} finally {
|
|
wasm.diplomat_buffer_writeable_destroy(writeable);
|
|
}
|
|
}
|
|
|
|
export class FFIError extends Error {
|
|
constructor(error_value) {
|
|
super("Error over FFI");
|
|
this.error_value = error_value; // (2)
|
|
}
|
|
}
|
|
|
|
export function extractCodePoint(str, param) {
|
|
const cp = str.codePointAt?.(0);
|
|
if ((!cp && cp !== 0) || [...str]?.length != 1) {
|
|
throw new TypeError(`Expected single-character string for char parameter ${param}, found ${str}`);
|
|
}
|
|
return cp;
|
|
}
|
|
|
|
// Get the pointer returned by an FFI function
|
|
//
|
|
// It's tempting to call `(new Uint32Array(wasm.memory.buffer, FFI_func(), 1))[0]`.
|
|
// However, there's a chance that `wasm.memory.buffer` will be resized between
|
|
// the time it's accessed and the time it's used, invalidating the view.
|
|
// This function ensures that the view into wasm memory is fresh.
|
|
//
|
|
// This is used for methods that return multiple types into a wasm buffer, where
|
|
// one of those types is another ptr. Call this method to get access to the returned
|
|
// ptr, so the return buffer can be freed.
|
|
export function ptrRead(wasm, ptr) {
|
|
return (new Uint32Array(wasm.memory.buffer, ptr, 1))[0];
|
|
}
|
|
|
|
// Get the flag of a result type.
|
|
export function resultFlag(wasm, ptr, offset) {
|
|
return (new Uint8Array(wasm.memory.buffer, ptr + offset, 1))[0];
|
|
}
|
|
|
|
// Get the discriminant of a Rust enum.
|
|
export function enumDiscriminant(wasm, ptr) {
|
|
return (new Int32Array(wasm.memory.buffer, ptr, 1))[0]
|
|
}
|
|
|
|
// A wrapper around a slice of WASM memory that can be freed manually or
|
|
// automatically by the garbage collector.
|
|
//
|
|
// This type is necessary for Rust functions that take a `&str` or `&[T]`, since
|
|
// they can create an edge to this object if they borrow from the str/slice,
|
|
// or we can manually free the WASM memory if they don't.
|
|
export class DiplomatBuf {
|
|
static str = (wasm, string) => {
|
|
var utf8_len = 0;
|
|
for (const codepoint_string of string) {
|
|
let codepoint = codepoint_string.codePointAt(0);
|
|
if (codepoint < 0x80) {
|
|
utf8_len += 1
|
|
} else if (codepoint < 0x800) {
|
|
utf8_len += 2
|
|
} else if (codepoint < 0x10000) {
|
|
utf8_len += 3
|
|
} else {
|
|
utf8_len += 4
|
|
}
|
|
}
|
|
return new DiplomatBuf(wasm, utf8_len, 1, buf => {
|
|
const result = (new TextEncoder()).encodeInto(string, buf);
|
|
console.assert(string.length == result.read && utf8_len == result.written, "UTF-8 write error");
|
|
})
|
|
}
|
|
|
|
static slice = (wasm, slice, align) => {
|
|
// If the slice is not a Uint8Array, we have to convert to one, as that's the only
|
|
// thing we can write into the wasm buffer.
|
|
const bytes = slice.constructor.name == "Uint8Array" ? slice : new Uint8Array(slice);
|
|
return new DiplomatBuf(wasm, bytes.length, align, buf => buf.set(bytes));
|
|
}
|
|
|
|
constructor(wasm, size, align, encodeCallback) {
|
|
const ptr = wasm.diplomat_alloc(size, align);
|
|
encodeCallback(new Uint8Array(wasm.memory.buffer, ptr, size));
|
|
|
|
this.ptr = ptr;
|
|
this.size = size;
|
|
this.free = () => {
|
|
const successfully_unregistered = DiplomatBuf_finalizer.unregister(this);
|
|
if (successfully_unregistered) {
|
|
wasm.diplomat_free(this.ptr, this.size, align);
|
|
} else {
|
|
console.error(`Failed to unregister DiplomatBuf at ${ptr}, this is a bug. Either it was never registered (leak), it was already unregistered (failed attempt to double free), or the unregister token was unrecognized (fallback to GC).`);
|
|
}
|
|
}
|
|
|
|
DiplomatBuf_finalizer.register(this, { wasm, ptr, size, align }, this);
|
|
}
|
|
|
|
leak = () => {
|
|
const successfully_unregistered = DiplomatBuf_finalizer.unregister(this);
|
|
if (successfully_unregistered) {
|
|
// leak
|
|
} else {
|
|
console.error(`Failed to unregister DiplomatBuf at ${this.ptr}, this is a bug. Either it was never registered (leak), it was already unregistered (failed attempt to double free), or the unregister token was unrecognized (fallback to GC).`);
|
|
}
|
|
}
|
|
}
|
|
|
|
const DiplomatBuf_finalizer = new FinalizationRegistry(({ wasm, ptr, size, align }) => {
|
|
wasm.diplomat_free(ptr, size, align);
|
|
});
|