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:
parent
d6de0db49a
commit
d1fe0f0eb1
2 changed files with 45 additions and 12 deletions
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue