summaryrefslogtreecommitdiffstats
path: root/intl/icu_capi/js/package/lib/diplomat-runtime.js
diff options
context:
space:
mode:
Diffstat (limited to 'intl/icu_capi/js/package/lib/diplomat-runtime.js')
-rw-r--r--intl/icu_capi/js/package/lib/diplomat-runtime.js121
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);
+});