Skip to content

Commit

Permalink
perf(serde_v8): fast path for large strings (denoland#14450)
Browse files Browse the repository at this point in the history
  • Loading branch information
AaronO committed May 15, 2022
1 parent 38e0a2e commit 05f6e77
Showing 1 changed file with 64 additions and 1 deletion.
65 changes: 64 additions & 1 deletion serde_v8/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de>
{
if self.input.is_string() {
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)
} else {
Err(Error::ExpectedString)
Expand Down Expand Up @@ -661,3 +661,66 @@ fn bigint_to_f64(b: v8::Local<v8::BigInt>) -> f64 {
.sum();
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)
}
}

0 comments on commit 05f6e77

Please sign in to comment.