diff --git a/crates/cli-support/src/js/mod.rs b/crates/cli-support/src/js/mod.rs index 0a7fd60489b1..f04bc216cb4e 100644 --- a/crates/cli-support/src/js/mod.rs +++ b/crates/cli-support/src/js/mod.rs @@ -1158,6 +1158,7 @@ impl<'a> Context<'a> { return Ok(()); } self.require_internal_export("__wbindgen_malloc")?; + self.require_internal_export("__wbindgen_realloc")?; self.expose_text_encoder(); self.expose_uint8_memory(); self.expose_wasm_vector_len(); @@ -1168,10 +1169,37 @@ impl<'a> Context<'a> { } else { "" }; + + // Add a binding for using `TextEncoder#encodeInto` which avoids + // intermediate buffer allocations by encoding directly into wasm's + // memory. This is a very new API, however, so we feature detect it and + // fall back to the widely available `TextEncoder#encode` method. + self.global(" + function passStringToWasmFast(string) { + let size = string.length; + let ptr = wasm.__wbindgen_malloc(size); + let writeOffset = 0; + while (true) { + const view = getUint8Memory().subarray(ptr + writeOffset, ptr + size); + const { read, written } = cachedTextEncoder.encodeInto(string, view); + string = string.substring(read); + writeOffset += written; + if (string.length === 0) + break; + ptr = wasm.__wbindgen_realloc(ptr, size, size * 2); + size *= 2; + } + WASM_VECTOR_LEN = writeOffset; + return ptr; + } + "); + self.global(&format!( " function passStringToWasm(arg) {{ {} + if (typeof cachedTextEncoder.encodeInto === 'function') + return passStringToWasmFast(arg); const buf = cachedTextEncoder.encode(arg); const ptr = wasm.__wbindgen_malloc(buf.length); getUint8Memory().set(buf, ptr); diff --git a/src/lib.rs b/src/lib.rs index 14a22b8d9991..703233b2672a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -892,7 +892,7 @@ pub mod __rt { } if_std! { - use std::alloc::{alloc, dealloc, Layout}; + use std::alloc::{alloc, dealloc, realloc, Layout}; use std::mem; #[no_mangle] @@ -911,6 +911,27 @@ pub mod __rt { } } + malloc_failure(); + } + + #[no_mangle] + pub extern "C" fn __wbindgen_realloc(ptr: *mut u8, old_size: usize, new_size: usize) -> *mut u8 { + let align = mem::align_of::(); + debug_assert!(old_size > 0); + debug_assert!(new_size > 0); + if let Ok(layout) = Layout::from_size_align(old_size, align) { + unsafe { + let ptr = realloc(ptr, layout, new_size); + if !ptr.is_null() { + return ptr + } + } + } + malloc_failure(); + } + + #[cold] + fn malloc_failure() -> ! { if cfg!(debug_assertions) { super::throw_str("invalid malloc request") } else {