diff options
| author | Luca Casonato <hello@lcas.dev> | 2023-03-16 17:59:47 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-16 16:59:47 +0000 |
| commit | b99c431ac78810034ea57cc778bf57d627998aa9 (patch) | |
| tree | 4398efcd8d3757e42a5cd4bdcee4ed9533103f43 /serde_v8 | |
| parent | 1a3c2e2f1dc5add94b5b7ff4ba4c26df55c7a011 (diff) | |
feat(serde_v8): support BigInt serialization (#18225)
This commit enables serializing `v8::BigInt` to `num_bigint::BigInt`
in Rust.
Pre-requisite for sub upcoming feature work.
Diffstat (limited to 'serde_v8')
| -rw-r--r-- | serde_v8/Cargo.toml | 1 | ||||
| -rw-r--r-- | serde_v8/de.rs | 4 | ||||
| -rw-r--r-- | serde_v8/error.rs | 1 | ||||
| -rw-r--r-- | serde_v8/lib.rs | 1 | ||||
| -rw-r--r-- | serde_v8/magic/bigint.rs | 76 | ||||
| -rw-r--r-- | serde_v8/magic/mod.rs | 1 | ||||
| -rw-r--r-- | serde_v8/ser.rs | 8 | ||||
| -rw-r--r-- | serde_v8/serializable.rs | 5 | ||||
| -rw-r--r-- | serde_v8/tests/de.rs | 63 | ||||
| -rw-r--r-- | serde_v8/tests/ser.rs | 56 |
10 files changed, 216 insertions, 0 deletions
diff --git a/serde_v8/Cargo.toml b/serde_v8/Cargo.toml index 4e518e71d..a146d5ada 100644 --- a/serde_v8/Cargo.toml +++ b/serde_v8/Cargo.toml @@ -16,6 +16,7 @@ path = "lib.rs" [dependencies] bytes.workspace = true derive_more = "0.99.17" +num-bigint.workspace = true serde.workspace = true serde_bytes.workspace = true smallvec = { workspace = true, features = ["union"] } diff --git a/serde_v8/de.rs b/serde_v8/de.rs index 15a90a13d..6708daa4d 100644 --- a/serde_v8/de.rs +++ b/serde_v8/de.rs @@ -13,6 +13,7 @@ use crate::magic::transl8::visit_magic; use crate::magic::transl8::FromV8; use crate::magic::transl8::MagicType; use crate::payload::ValueType; +use crate::BigInt; use crate::ByteString; use crate::DetachedBuffer; use crate::StringOrBuffer; @@ -348,6 +349,9 @@ impl<'de, 'a, 'b, 's, 'x> de::Deserializer<'de> StringOrBuffer::MAGIC_NAME => { visit_magic(visitor, StringOrBuffer::from_v8(self.scope, self.input)?) } + BigInt::MAGIC_NAME => { + visit_magic(visitor, BigInt::from_v8(self.scope, self.input)?) + } magic::Value::MAGIC_NAME => { visit_magic(visitor, magic::Value::from_v8(self.scope, self.input)?) } diff --git a/serde_v8/error.rs b/serde_v8/error.rs index 145524abb..72d3cc925 100644 --- a/serde_v8/error.rs +++ b/serde_v8/error.rs @@ -23,6 +23,7 @@ pub enum Error { ExpectedBuffer, ExpectedDetachable, ExpectedExternal, + ExpectedBigInt, ExpectedUtf8, ExpectedLatin1, diff --git a/serde_v8/lib.rs b/serde_v8/lib.rs index b857acbe8..1d17914bb 100644 --- a/serde_v8/lib.rs +++ b/serde_v8/lib.rs @@ -15,6 +15,7 @@ pub use de::Deserializer; pub use error::Error; pub use error::Result; pub use keys::KeyCache; +pub use magic::bigint::BigInt; pub use magic::buffer::ZeroCopyBuf; pub use magic::bytestring::ByteString; pub use magic::detached_buffer::DetachedBuffer; diff --git a/serde_v8/magic/bigint.rs b/serde_v8/magic/bigint.rs new file mode 100644 index 000000000..69828747f --- /dev/null +++ b/serde_v8/magic/bigint.rs @@ -0,0 +1,76 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +use smallvec::smallvec; +use smallvec::SmallVec; + +use super::transl8::FromV8; +use super::transl8::ToV8; +use crate::magic::transl8::impl_magic; +use crate::Error; + +#[derive( + PartialEq, + Eq, + Clone, + Debug, + Default, + derive_more::Deref, + derive_more::DerefMut, + derive_more::AsRef, + derive_more::AsMut, +)] +#[as_mut(forward)] +#[as_ref(forward)] +pub struct BigInt(num_bigint::BigInt); +impl_magic!(BigInt); + +impl ToV8 for BigInt { + fn to_v8<'a>( + &mut self, + scope: &mut v8::HandleScope<'a>, + ) -> Result<v8::Local<'a, v8::Value>, crate::Error> { + let (sign, words) = self.0.to_u64_digits(); + let sign_bit = sign == num_bigint::Sign::Minus; + let v = v8::BigInt::new_from_words(scope, sign_bit, &words).unwrap(); + Ok(v.into()) + } +} + +impl FromV8 for BigInt { + fn from_v8( + _scope: &mut v8::HandleScope, + value: v8::Local<v8::Value>, + ) -> Result<Self, crate::Error> { + let v8bigint = v8::Local::<v8::BigInt>::try_from(value) + .map_err(|_| Error::ExpectedBigInt)?; + let word_count = v8bigint.word_count(); + let mut words: SmallVec<[u64; 1]> = smallvec![0u64; word_count]; + let (sign_bit, _words) = v8bigint.to_words_array(&mut words); + let sign = match sign_bit { + true => num_bigint::Sign::Minus, + false => num_bigint::Sign::Plus, + }; + // SAFETY: Because the alignment of u64 is 8, the alignment of u32 is 4, and + // the size of u64 is 8, the size of u32 is 4, the alignment of u32 is a + // factor of the alignment of u64, and the size of u32 is a factor of the + // size of u64, we can safely transmute the slice of u64 to a slice of u32. + let (prefix, slice, suffix) = unsafe { words.align_to::<u32>() }; + assert!(prefix.is_empty()); + assert!(suffix.is_empty()); + assert_eq!(slice.len(), words.len() * 2); + let big_int = num_bigint::BigInt::from_slice(sign, slice); + Ok(Self(big_int)) + } +} + +impl From<num_bigint::BigInt> for BigInt { + fn from(big_int: num_bigint::BigInt) -> Self { + Self(big_int) + } +} + +impl From<BigInt> for num_bigint::BigInt { + fn from(big_int: BigInt) -> Self { + big_int.0 + } +} diff --git a/serde_v8/magic/mod.rs b/serde_v8/magic/mod.rs index f96e422b1..9e5064867 100644 --- a/serde_v8/magic/mod.rs +++ b/serde_v8/magic/mod.rs @@ -1,4 +1,5 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +pub mod bigint; pub mod buffer; pub mod bytestring; pub mod detached_buffer; diff --git a/serde_v8/ser.rs b/serde_v8/ser.rs index fa4cfecde..6c10f3fb4 100644 --- a/serde_v8/ser.rs +++ b/serde_v8/ser.rs @@ -14,6 +14,7 @@ use crate::magic::transl8::opaque_recv; use crate::magic::transl8::MagicType; use crate::magic::transl8::ToV8; use crate::magic::transl8::MAGIC_FIELD; +use crate::BigInt; use crate::ByteString; use crate::DetachedBuffer; use crate::ExternalPointer; @@ -277,6 +278,7 @@ pub enum StructSerializers<'a, 'b, 'c> { MagicByteString(MagicalSerializer<'a, 'b, 'c, ByteString>), MagicU16String(MagicalSerializer<'a, 'b, 'c, U16String>), MagicStringOrBuffer(MagicalSerializer<'a, 'b, 'c, StringOrBuffer>), + MagicBigInt(MagicalSerializer<'a, 'b, 'c, BigInt>), Regular(ObjectSerializer<'a, 'b, 'c>), } @@ -299,6 +301,7 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> { StructSerializers::MagicStringOrBuffer(s) => { s.serialize_field(key, value) } + StructSerializers::MagicBigInt(s) => s.serialize_field(key, value), StructSerializers::Regular(s) => s.serialize_field(key, value), } } @@ -312,6 +315,7 @@ impl<'a, 'b, 'c> ser::SerializeStruct for StructSerializers<'a, 'b, 'c> { StructSerializers::MagicByteString(s) => s.end(), StructSerializers::MagicU16String(s) => s.end(), StructSerializers::MagicStringOrBuffer(s) => s.end(), + StructSerializers::MagicBigInt(s) => s.end(), StructSerializers::Regular(s) => s.end(), } } @@ -592,6 +596,10 @@ impl<'a, 'b, 'c> ser::Serializer for Serializer<'a, 'b, 'c> { let m = MagicalSerializer::<StringOrBuffer>::new(self.scope); Ok(StructSerializers::MagicStringOrBuffer(m)) } + BigInt::MAGIC_NAME => { + let m = MagicalSerializer::<BigInt>::new(self.scope); + Ok(StructSerializers::MagicBigInt(m)) + } magic::Value::MAGIC_NAME => { let m = MagicalSerializer::<magic::Value<'a>>::new(self.scope); Ok(StructSerializers::Magic(m)) diff --git a/serde_v8/serializable.rs b/serde_v8/serializable.rs index 21c7bb752..b02aa0629 100644 --- a/serde_v8/serializable.rs +++ b/serde_v8/serializable.rs @@ -2,6 +2,7 @@ use std::any::TypeId; use std::mem::transmute_copy; +use crate::BigInt; use crate::ByteString; use crate::U16String; use crate::ZeroCopyBuf; @@ -65,6 +66,7 @@ pub enum Primitive { ZeroCopyBuf(ZeroCopyBuf), ByteString(ByteString), U16String(U16String), + BigInt(BigInt), } impl serde::Serialize for Primitive { @@ -89,6 +91,7 @@ impl serde::Serialize for Primitive { Self::ZeroCopyBuf(x) => x.serialize(s), Self::ByteString(x) => x.serialize(s), Self::U16String(x) => x.serialize(s), + Self::BigInt(x) => x.serialize(s), } } } @@ -137,6 +140,8 @@ impl<T: serde::Serialize + 'static> From<T> for SerializablePkg { Self::Primitive(Primitive::ByteString(tc(x))) } else if tid == TypeId::of::<U16String>() { Self::Primitive(Primitive::U16String(tc(x))) + } else if tid == TypeId::of::<BigInt>() { + Self::Primitive(Primitive::BigInt(tc(x))) } else { Self::Serializable(Box::new(x)) } diff --git a/serde_v8/tests/de.rs b/serde_v8/tests/de.rs index 5f3e262e7..a77089bab 100644 --- a/serde_v8/tests/de.rs +++ b/serde_v8/tests/de.rs @@ -4,6 +4,7 @@ use serde::Deserializer; use serde_v8::utils::js_exec; use serde_v8::utils::v8_do; +use serde_v8::BigInt; use serde_v8::ByteString; use serde_v8::Error; use serde_v8::U16String; @@ -566,3 +567,65 @@ detest!( "BigInt(-1.7976931348623157e+308)", f32::NEG_INFINITY ); + +// BigInt to BigInt +detest!( + de_bigint_var_u8, + BigInt, + "255n", + num_bigint::BigInt::from(255u8).into() +); +detest!( + de_bigint_var_i8, + BigInt, + "-128n", + num_bigint::BigInt::from(-128i8).into() +); +detest!( + de_bigint_var_u16, + BigInt, + "65535n", + num_bigint::BigInt::from(65535u16).into() +); +detest!( + de_bigint_var_i16, + BigInt, + "-32768n", + num_bigint::BigInt::from(-32768i16).into() +); +detest!( + de_bigint_var_u32, + BigInt, + "4294967295n", + num_bigint::BigInt::from(4294967295u32).into() +); +detest!( + de_bigint_var_i32, + BigInt, + "-2147483648n", + num_bigint::BigInt::from(-2147483648i32).into() +); +detest!( + de_bigint_var_u64, + BigInt, + "18446744073709551615n", + num_bigint::BigInt::from(18446744073709551615u64).into() +); +detest!( + de_bigint_var_i64, + BigInt, + "-9223372036854775808n", + num_bigint::BigInt::from(-9223372036854775808i64).into() +); +detest!( + de_bigint_var_u128, + BigInt, + "340282366920938463463374607431768211455n", + num_bigint::BigInt::from(340282366920938463463374607431768211455u128).into() +); +detest!( + de_bigint_var_i128, + BigInt, + "-170141183460469231731687303715884105728n", + num_bigint::BigInt::from(-170141183460469231731687303715884105728i128).into() +); diff --git a/serde_v8/tests/ser.rs b/serde_v8/tests/ser.rs index d6de3a62a..b61a758f9 100644 --- a/serde_v8/tests/ser.rs +++ b/serde_v8/tests/ser.rs @@ -3,6 +3,7 @@ use serde::Serialize; use serde_json::json; use serde_v8::utils::js_exec; use serde_v8::utils::v8_do; +use serde_v8::BigInt; #[derive(Debug, Serialize, PartialEq)] struct MathOp { @@ -138,6 +139,61 @@ sertest!( ); sertest!( + ser_bigint_u8, + BigInt::from(num_bigint::BigInt::from(255_u8)), + "x === 255n" +); +sertest!( + ser_bigint_i8, + BigInt::from(num_bigint::BigInt::from(-128_i8)), + "x === -128n" +); +sertest!( + ser_bigint_u16, + BigInt::from(num_bigint::BigInt::from(65535_u16)), + "x === 65535n" +); +sertest!( + ser_bigint_i16, + BigInt::from(num_bigint::BigInt::from(-32768_i16)), + "x === -32768n" +); +sertest!( + ser_bigint_u32, + BigInt::from(num_bigint::BigInt::from(4294967295_u32)), + "x === 4294967295n" +); +sertest!( + ser_bigint_i32, + BigInt::from(num_bigint::BigInt::from(-2147483648_i32)), + "x === -2147483648n" +); +sertest!( + ser_bigint_u64, + BigInt::from(num_bigint::BigInt::from(9007199254740991_u64)), + "x === 9007199254740991n" +); +sertest!( + ser_bigint_i64, + BigInt::from(num_bigint::BigInt::from(-9007199254740991_i64)), + "x === -9007199254740991n" +); +sertest!( + ser_bigint_u128, + BigInt::from(num_bigint::BigInt::from( + 340282366920938463463374607431768211455_u128 + )), + "x === 340282366920938463463374607431768211455n" +); +sertest!( + ser_bigint_i128, + BigInt::from(num_bigint::BigInt::from( + -170141183460469231731687303715884105728_i128 + )), + "x === -170141183460469231731687303715884105728n" +); + +sertest!( ser_map, { let map: std::collections::BTreeMap<&str, u32> = |
