diff --git a/serde/Cargo.toml b/serde/Cargo.toml index 6488d903c..e99edb5bb 100644 --- a/serde/Cargo.toml +++ b/serde/Cargo.toml @@ -31,6 +31,9 @@ targets = ["x86_64-unknown-linux-gnu"] [features] default = ["std"] +# Supports arrays of arbitrary length +const-generics = [] + # Provide derive(Serialize, Deserialize) macros. derive = ["serde_derive"] diff --git a/serde/src/de/impls.rs b/serde/src/de/impls.rs index 59d90d2cc..1e1df0b5a 100644 --- a/serde/src/de/impls.rs +++ b/serde/src/de/impls.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "const-generics")] +mod const_generics_impls; + use lib::*; use de::{ @@ -1002,6 +1005,7 @@ impl ArrayVisitor { } } +#[cfg(not(feature = "const-generics"))] impl<'de, T> Visitor<'de> for ArrayVisitor<[T; 0]> { type Value = [T; 0]; @@ -1019,6 +1023,7 @@ impl<'de, T> Visitor<'de> for ArrayVisitor<[T; 0]> { } // Does not require T: Deserialize<'de>. +#[cfg(not(feature = "const-generics"))] impl<'de, T> Deserialize<'de> for [T; 0] { fn deserialize(deserializer: D) -> Result where @@ -1028,6 +1033,7 @@ impl<'de, T> Deserialize<'de> for [T; 0] { } } +#[cfg(not(feature = "const-generics"))] macro_rules! array_impls { ($($len:expr => ($($n:tt)+))+) => { $( @@ -1106,6 +1112,7 @@ macro_rules! array_impls { } } +#[cfg(not(feature = "const-generics"))] array_impls! { 1 => (0) 2 => (0 1) diff --git a/serde/src/de/impls/const_generics_impls.rs b/serde/src/de/impls/const_generics_impls.rs new file mode 100644 index 000000000..ee0a6c2db --- /dev/null +++ b/serde/src/de/impls/const_generics_impls.rs @@ -0,0 +1,106 @@ +use crate::de::impls::{ArrayInPlaceVisitor, ArrayVisitor, InPlaceSeed}; +use de::{Deserialize, Deserializer, Error, SeqAccess, Visitor}; +use lib::fmt; + +struct ArrayGuard { + dst: *mut T, + initialized: usize, +} + +impl Drop for ArrayGuard { + fn drop(&mut self) { + debug_assert!(self.initialized <= N); + let initialized_part = core::ptr::slice_from_raw_parts_mut(self.dst, self.initialized); + #[allow(unsafe_code)] + unsafe { + core::ptr::drop_in_place(initialized_part); + } + } +} + +fn try_create_array(mut cb: F) -> Result<[T; N], E> +where + F: FnMut(usize) -> Result, +{ + let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit(); + let mut guard: ArrayGuard = ArrayGuard { + dst: array.as_mut_ptr() as _, + initialized: 0, + }; + #[allow(unsafe_code)] + unsafe { + for (idx, value_ptr) in (&mut *array.as_mut_ptr()).iter_mut().enumerate() { + core::ptr::write(value_ptr, cb(idx)?); + guard.initialized += 1; + } + core::mem::forget(guard); + Ok(array.assume_init()) + } +} + +impl<'de, T, const N: usize> Visitor<'de> for ArrayVisitor<[T; N]> +where + T: Deserialize<'de>, +{ + type Value = [T; N]; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("array") + } + + #[inline] + fn visit_seq(self, mut seq: S) -> Result + where + S: SeqAccess<'de>, + { + try_create_array(|idx| seq.next_element()?.ok_or(Error::invalid_length(idx, &self))) + } +} + +impl<'a, 'de, T, const N: usize> Visitor<'de> for ArrayInPlaceVisitor<'a, [T; N]> +where + T: Deserialize<'de>, +{ + type Value = (); + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("array") + } + + #[inline] + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut fail_idx = None; + for (idx, dest) in self.0[..].iter_mut().enumerate() { + if seq.next_element_seed(InPlaceSeed(dest))?.is_none() { + fail_idx = Some(idx); + break; + } + } + if let Some(idx) = fail_idx { + return Err(Error::invalid_length(idx, &self)); + } + Ok(()) + } +} + +impl<'de, T, const N: usize> Deserialize<'de> for [T; N] +where + T: Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_tuple(N, ArrayVisitor::<[T; N]>::new()) + } + + fn deserialize_in_place(deserializer: D, place: &mut Self) -> Result<(), D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_tuple(N, ArrayInPlaceVisitor(place)) + } +} diff --git a/serde/src/ser/impls.rs b/serde/src/ser/impls.rs index c254ac654..dd61e9ceb 100644 --- a/serde/src/ser/impls.rs +++ b/serde/src/ser/impls.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "const-generics")] +mod const_generics_impls; + use lib::*; use ser::{Error, Serialize, SerializeTuple, Serializer}; @@ -127,6 +130,7 @@ impl Serialize for PhantomData { //////////////////////////////////////////////////////////////////////////////// // Does not require T: Serialize. +#[cfg(not(feature = "const-generics"))] impl Serialize for [T; 0] { #[inline] fn serialize(&self, serializer: S) -> Result @@ -137,6 +141,7 @@ impl Serialize for [T; 0] { } } +#[cfg(not(feature = "const-generics"))] macro_rules! array_impls { ($($len:tt)+) => { $( @@ -160,6 +165,7 @@ macro_rules! array_impls { } } +#[cfg(not(feature = "const-generics"))] array_impls! { 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 diff --git a/serde/src/ser/impls/const_generics_impls.rs b/serde/src/ser/impls/const_generics_impls.rs new file mode 100644 index 000000000..35f071287 --- /dev/null +++ b/serde/src/ser/impls/const_generics_impls.rs @@ -0,0 +1,19 @@ +use ser::{Serialize, SerializeTuple, Serializer}; + +#[cfg(feature = "const-generics")] +impl Serialize for [T; N] +where + T: Serialize, +{ + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_tuple(N)?; + for e in self.iter() { + seq.serialize_element(e)?; + } + seq.end() + } +} diff --git a/test_suite/Cargo.toml b/test_suite/Cargo.toml index 53effd4eb..3260920ea 100644 --- a/test_suite/Cargo.toml +++ b/test_suite/Cargo.toml @@ -7,6 +7,7 @@ publish = false build = "build.rs" [features] +const-generics = ["serde/const-generics"] expandtest = [] unstable = ["serde/unstable"] diff --git a/test_suite/tests/test_gen.rs b/test_suite/tests/test_gen.rs index 002885ead..610237f8a 100644 --- a/test_suite/tests/test_gen.rs +++ b/test_suite/tests/test_gen.rs @@ -348,6 +348,13 @@ fn test_gen() { #[serde(deny_unknown_fields)] struct UnitDenyUnknown; + #[cfg(feature = "const-generics")] + #[derive(Serialize, Deserialize)] + struct ArbitraryArrayLength { + empty: [u8; 123], + } + + #[cfg(not(feature = "const-generics"))] #[derive(Serialize, Deserialize)] struct EmptyArray { empty: [X; 0],