From d1fe0f0eb142ef0270e279394f6357a950bc39e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=81nis=20Gailis?= Date: Fri, 19 May 2023 12:27:29 +0300 Subject: [PATCH] feat(napi): Bigint deserialization (#1592) * Handle little endianness; * Make sure get_128 methods don't panic when words.len() < 2; * Make sure BigInt deserialization takes into account word length and sign; Signed-off-by: Janis Gailis Co-authored-by: Janis Gailis <> --- crates/napi/src/js_values/bigint.rs | 43 ++++++++++++++++++++++++----- crates/napi/src/js_values/de.rs | 14 ++++++---- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/crates/napi/src/js_values/bigint.rs b/crates/napi/src/js_values/bigint.rs index 2725c7be..a72b44b0 100644 --- a/crates/napi/src/js_values/bigint.rs +++ b/crates/napi/src/js_values/bigint.rs @@ -245,20 +245,49 @@ impl JsBigInt { pub fn get_i128(&mut self) -> Result<(i128, bool)> { let (signed, words) = self.get_words()?; - let len = words.len(); - let i128_words: [i64; 2] = [words[0] as _, words[1] as _]; - let mut val = unsafe { ptr::read(i128_words.as_ptr() as *const i128) }; + + let high_part = words.first().copied().unwrap_or(0).to_le_bytes(); + let low_part = words.get(1).copied().unwrap_or(0).to_le_bytes(); + + let mut val = [0_u8; std::mem::size_of::()]; + + let (high_val, low_val) = val.split_at_mut(low_part.len()); + + high_val.copy_from_slice(&high_part); + low_val.copy_from_slice(&low_part); + + let mut val = i128::from_le_bytes(val); + + let mut loss = words.len() > 2; + let mut overflow = false; + if signed { - val = -val; + let result = val.overflowing_neg(); + val = result.0; + overflow = result.1; } - Ok((val, len > 2)) + + loss = overflow || loss; + + Ok((val, loss)) } pub fn get_u128(&mut self) -> Result<(bool, u128, bool)> { let (signed, words) = self.get_words()?; + + let high_part = words.first().copied().unwrap_or(0).to_le_bytes(); + let low_part = words.get(1).copied().unwrap_or(0).to_le_bytes(); + + let mut val = [0_u8; std::mem::size_of::()]; + + let (high_val, low_val) = val.split_at_mut(low_part.len()); + + high_val.copy_from_slice(&high_part); + low_val.copy_from_slice(&low_part); + + let val = u128::from_le_bytes(val); let len = words.len(); - let u128_words: [u64; 2] = [words[0], words[1]]; - let val = unsafe { ptr::read(u128_words.as_ptr() as *const u128) }; + Ok((signed, val, len > 2)) } } diff --git a/crates/napi/src/js_values/de.rs b/crates/napi/src/js_values/de.rs index 657f7fa7..749584a4 100644 --- a/crates/napi/src/js_values/de.rs +++ b/crates/napi/src/js_values/de.rs @@ -56,11 +56,15 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> { #[cfg(feature = "napi6")] ValueType::BigInt => { let mut js_bigint = unsafe { JsBigInt::from_raw(self.0.env, self.0.value)? }; - let (signed, v, _loss) = js_bigint.get_u128()?; - if signed { - visitor.visit_i128(-(v as i128)) - } else { - visitor.visit_u128(v) + + let (signed, words) = js_bigint.get_words()?; + let word_sized = words.len() < 2; + + match (signed, word_sized) { + (true, true) => visitor.visit_i64(js_bigint.get_i64()?.0), + (true, false) => visitor.visit_i128(js_bigint.get_i128()?.0), + (false, true) => visitor.visit_u64(js_bigint.get_u64()?.0), + (false, false) => visitor.visit_u128(js_bigint.get_u128()?.1), } } ValueType::External | ValueType::Function | ValueType::Symbol => Err(Error::new(