diff options
Diffstat (limited to 'intl/icu_capi/js/package/lib/diplomat-runtime.js')
-rw-r--r-- | intl/icu_capi/js/package/lib/diplomat-runtime.js | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/intl/icu_capi/js/package/lib/diplomat-runtime.js b/intl/icu_capi/js/package/lib/diplomat-runtime.js new file mode 100644 index 0000000000..75317d4a74 --- /dev/null +++ b/intl/icu_capi/js/package/lib/diplomat-runtime.js @@ -0,0 +1,121 @@ +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); +}); |