mirror of
https://github.com/denoland/deno.git
synced 2025-01-22 15:10:44 -05:00
perf(serde_v8): fast path for large strings (#14450)
This commit is contained in:
parent
38e0a2ec1b
commit
05f6e773fa
1 changed files with 64 additions and 1 deletions
|
@ -211,7 +211,7 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de>
|
||||||
{
|
{
|
||||||
if self.input.is_string() {
|
if self.input.is_string() {
|
||||||
let v8_string = v8::Local::<v8::String>::try_from(self.input).unwrap();
|
let v8_string = v8::Local::<v8::String>::try_from(self.input).unwrap();
|
||||||
let string = v8_string.to_rust_string_lossy(self.scope);
|
let string = to_utf8(v8_string, self.scope);
|
||||||
visitor.visit_string(string)
|
visitor.visit_string(string)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::ExpectedString)
|
Err(Error::ExpectedString)
|
||||||
|
@ -661,3 +661,66 @@ fn bigint_to_f64(b: v8::Local<v8::BigInt>) -> f64 {
|
||||||
.sum();
|
.sum();
|
||||||
sign * x
|
sign * x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_utf8(
|
||||||
|
s: v8::Local<v8::String>,
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
) -> String {
|
||||||
|
to_utf8_fast(s, scope).unwrap_or_else(|| to_utf8_slow(s, scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_utf8_fast(
|
||||||
|
s: v8::Local<v8::String>,
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
) -> Option<String> {
|
||||||
|
// Over-allocate by 20% to avoid checking string twice
|
||||||
|
let len = s.length();
|
||||||
|
let capacity = (len as f64 * 1.2) as usize;
|
||||||
|
let mut buf = Vec::with_capacity(capacity);
|
||||||
|
let mut nchars = 0;
|
||||||
|
let data = buf.as_mut_ptr();
|
||||||
|
let length = s.write_utf8(
|
||||||
|
scope,
|
||||||
|
// SAFETY: we're essentially providing the raw internal slice/buffer owned by the Vec
|
||||||
|
// which fulfills all of from_raw_parts_mut's safety requirements besides "initialization"
|
||||||
|
// and since we're operating on a [u8] not [T] we can safely assume the slice's values
|
||||||
|
// are sufficiently "initialized" for writes
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(data, capacity) },
|
||||||
|
Some(&mut nchars),
|
||||||
|
v8::WriteOptions::NO_NULL_TERMINATION
|
||||||
|
| v8::WriteOptions::REPLACE_INVALID_UTF8,
|
||||||
|
);
|
||||||
|
if nchars < len {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// SAFETY: write_utf8 guarantees `length` bytes are initialized & valid utf8
|
||||||
|
unsafe {
|
||||||
|
buf.set_len(length);
|
||||||
|
Some(String::from_utf8_unchecked(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_utf8_slow(
|
||||||
|
s: v8::Local<v8::String>,
|
||||||
|
scope: &mut v8::HandleScope,
|
||||||
|
) -> String {
|
||||||
|
let capacity = s.utf8_length(scope);
|
||||||
|
let mut buf = Vec::with_capacity(capacity);
|
||||||
|
let data = buf.as_mut_ptr();
|
||||||
|
let length = s.write_utf8(
|
||||||
|
scope,
|
||||||
|
// SAFETY: we're essentially providing the raw internal slice/buffer owned by the Vec
|
||||||
|
// which fulfills all of from_raw_parts_mut's safety requirements besides "initialization"
|
||||||
|
// and since we're operating on a [u8] not [T] we can safely assume the slice's values
|
||||||
|
// are sufficiently "initialized" for writes
|
||||||
|
unsafe { std::slice::from_raw_parts_mut(data, capacity) },
|
||||||
|
None,
|
||||||
|
v8::WriteOptions::NO_NULL_TERMINATION
|
||||||
|
| v8::WriteOptions::REPLACE_INVALID_UTF8,
|
||||||
|
);
|
||||||
|
// SAFETY: write_utf8 guarantees `length` bytes are initialized & valid utf8
|
||||||
|
unsafe {
|
||||||
|
buf.set_len(length);
|
||||||
|
String::from_utf8_unchecked(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue