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],