perf(serde_v8): fast path for large strings
@@ -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)
@@ -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)
+ }
+}
