feat(napi): Bigint deserialization (#1592)

* Handle little endianness;
* Make sure get_<u/i>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 <JanisGailis@users.noreply.github.com>
Co-authored-by: Janis Gailis <>
This commit is contained in:
Jānis Gailis 2023-05-19 12:27:29 +03:00 committed by GitHub
parent d6de0db49a
commit d1fe0f0eb1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 45 additions and 12 deletions

View file

@ -245,20 +245,49 @@ impl JsBigInt {
pub fn get_i128(&mut self) -> Result<(i128, bool)> { pub fn get_i128(&mut self) -> Result<(i128, bool)> {
let (signed, words) = self.get_words()?; let (signed, words) = self.get_words()?;
let len = words.len();
let i128_words: [i64; 2] = [words[0] as _, words[1] as _]; let high_part = words.first().copied().unwrap_or(0).to_le_bytes();
let mut val = unsafe { ptr::read(i128_words.as_ptr() as *const i128) }; let low_part = words.get(1).copied().unwrap_or(0).to_le_bytes();
let mut val = [0_u8; std::mem::size_of::<i128>()];
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 { 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)> { pub fn get_u128(&mut self) -> Result<(bool, u128, bool)> {
let (signed, words) = self.get_words()?; 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::<u128>()];
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 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)) Ok((signed, val, len > 2))
} }
} }

View file

@ -56,11 +56,15 @@ impl<'x, 'de, 'env> serde::de::Deserializer<'x> for &'de mut De<'env> {
#[cfg(feature = "napi6")] #[cfg(feature = "napi6")]
ValueType::BigInt => { ValueType::BigInt => {
let mut js_bigint = unsafe { JsBigInt::from_raw(self.0.env, self.0.value)? }; let mut js_bigint = unsafe { JsBigInt::from_raw(self.0.env, self.0.value)? };
let (signed, v, _loss) = js_bigint.get_u128()?;
if signed { let (signed, words) = js_bigint.get_words()?;
visitor.visit_i128(-(v as i128)) let word_sized = words.len() < 2;
} else {
visitor.visit_u128(v) 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( ValueType::External | ValueType::Function | ValueType::Symbol => Err(Error::new(