mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-13 13:01:22 +00:00
Add support for TextEncoder#encodeInto
This commit adds support for the recently implemented standard of [`TextEncoder#encodeInto`][standard]. This new function is a "bring your own buffer" style function where we can avoid an intermediate allocation and copy by encoding strings directly into wasm's memory. Currently we feature-detect whether `encodeInto` exists as it is only implemented in recent browsers and not in all browsers. Additionally this commit emits the binding using `encodeInto` by default, but this requires `realloc` functionality to be exposed by the wasm module. Measured locally an empty binary which takes `&str` previously took 7.6k, but after this commit takes 8.7k due to the extra code needed for `realloc`. [standard]: https://encoding.spec.whatwg.org/#dom-textencoder-encodeinto Closes #1172
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
use crate::decode;
|
||||
use crate::descriptor::{Descriptor, VectorKind};
|
||||
use crate::Bindgen;
|
||||
use crate::{Bindgen, EncodeInto};
|
||||
use failure::{bail, Error, ResultExt};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use walrus::{MemoryId, Module};
|
||||
@ -1168,19 +1168,77 @@ impl<'a> Context<'a> {
|
||||
} else {
|
||||
""
|
||||
};
|
||||
self.global(&format!(
|
||||
|
||||
// The first implementation we have for this is to use
|
||||
// `TextEncoder#encode` which has been around for quite some time.
|
||||
let use_encode = format!(
|
||||
"
|
||||
function passStringToWasm(arg) {{
|
||||
{}
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = wasm.__wbindgen_malloc(buf.length);
|
||||
getUint8Memory().set(buf, ptr);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}}
|
||||
",
|
||||
debug
|
||||
));
|
||||
);
|
||||
|
||||
// Another possibility is to use `TextEncoder#encodeInto` which is much
|
||||
// newer and isn't implemented everywhere yet. It's more efficient,
|
||||
// however, becaues it allows us to elide an intermediate allocation.
|
||||
let use_encode_into = format!(
|
||||
"
|
||||
{}
|
||||
let size = arg.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(arg, view);
|
||||
arg = arg.substring(read);
|
||||
writeOffset += written;
|
||||
if (arg.length === 0) {{
|
||||
break;
|
||||
}}
|
||||
ptr = wasm.__wbindgen_realloc(ptr, size, size * 2);
|
||||
size *= 2;
|
||||
}}
|
||||
WASM_VECTOR_LEN = writeOffset;
|
||||
return ptr;
|
||||
",
|
||||
debug
|
||||
);
|
||||
|
||||
match self.config.encode_into {
|
||||
EncodeInto::Never => {
|
||||
self.global(&format!(
|
||||
"function passStringToWasm(arg) {{ {} }}",
|
||||
use_encode,
|
||||
));
|
||||
}
|
||||
EncodeInto::Always => {
|
||||
self.require_internal_export("__wbindgen_realloc")?;
|
||||
self.global(&format!(
|
||||
"function passStringToWasm(arg) {{ {} }}",
|
||||
use_encode_into,
|
||||
));
|
||||
}
|
||||
EncodeInto::Test => {
|
||||
self.require_internal_export("__wbindgen_realloc")?;
|
||||
self.global(&format!(
|
||||
"
|
||||
let passStringToWasm;
|
||||
if (typeof cachedTextEncoder.encodeInto === 'function') {{
|
||||
passStringToWasm = function(arg) {{ {} }};
|
||||
}} else {{
|
||||
passStringToWasm = function(arg) {{ {} }};
|
||||
}}
|
||||
",
|
||||
use_encode_into,
|
||||
use_encode,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ pub struct Bindgen {
|
||||
// module to be "ready to be instantiated on any thread"
|
||||
threads: Option<wasm_bindgen_threads_xform::Config>,
|
||||
anyref: bool,
|
||||
encode_into: EncodeInto,
|
||||
}
|
||||
|
||||
enum Input {
|
||||
@ -44,6 +45,12 @@ enum Input {
|
||||
None,
|
||||
}
|
||||
|
||||
pub enum EncodeInto {
|
||||
Test,
|
||||
Always,
|
||||
Never,
|
||||
}
|
||||
|
||||
impl Bindgen {
|
||||
pub fn new() -> Bindgen {
|
||||
Bindgen {
|
||||
@ -64,6 +71,7 @@ impl Bindgen {
|
||||
weak_refs: env::var("WASM_BINDGEN_WEAKREF").is_ok(),
|
||||
threads: threads_config(),
|
||||
anyref: env::var("WASM_BINDGEN_ANYREF").is_ok(),
|
||||
encode_into: EncodeInto::Test,
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,6 +152,11 @@ impl Bindgen {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn encode_into(&mut self, mode: EncodeInto) -> &mut Bindgen {
|
||||
self.encode_into = mode;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn generate<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
|
||||
self._generate(path.as_ref())
|
||||
}
|
||||
|
Reference in New Issue
Block a user