Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "const-generics" feature #1860

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions serde/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
7 changes: 7 additions & 0 deletions serde/src/de/impls.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "const-generics")]
mod const_generics_impls;

use lib::*;

use de::{
Expand Down Expand Up @@ -1002,6 +1005,7 @@ impl<A> ArrayVisitor<A> {
}
}

#[cfg(not(feature = "const-generics"))]
impl<'de, T> Visitor<'de> for ArrayVisitor<[T; 0]> {
type Value = [T; 0];

Expand All @@ -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<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand All @@ -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)+))+) => {
$(
Expand Down Expand Up @@ -1106,6 +1112,7 @@ macro_rules! array_impls {
}
}

#[cfg(not(feature = "const-generics"))]
array_impls! {
1 => (0)
2 => (0 1)
Expand Down
106 changes: 106 additions & 0 deletions serde/src/de/impls/const_generics_impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::de::impls::{ArrayInPlaceVisitor, ArrayVisitor, InPlaceSeed};
use de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};
use lib::fmt;

struct ArrayGuard<T, const N: usize> {
dst: *mut T,
initialized: usize,
}

impl<T, const N: usize> Drop for ArrayGuard<T, N> {
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<E, F, T, const N: usize>(mut cb: F) -> Result<[T; N], E>
where
F: FnMut(usize) -> Result<T, E>,
{
let mut array: core::mem::MaybeUninit<[T; N]> = core::mem::MaybeUninit::uninit();
let mut guard: ArrayGuard<T, N> = 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<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_tuple(N, ArrayVisitor::<[T; N]>::new())
}

fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_tuple(N, ArrayInPlaceVisitor(place))
}
}
6 changes: 6 additions & 0 deletions serde/src/ser/impls.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "const-generics")]
mod const_generics_impls;

use lib::*;

use ser::{Error, Serialize, SerializeTuple, Serializer};
Expand Down Expand Up @@ -127,6 +130,7 @@ impl<T: ?Sized> Serialize for PhantomData<T> {
////////////////////////////////////////////////////////////////////////////////

// Does not require T: Serialize.
#[cfg(not(feature = "const-generics"))]
impl<T> Serialize for [T; 0] {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
Expand All @@ -137,6 +141,7 @@ impl<T> Serialize for [T; 0] {
}
}

#[cfg(not(feature = "const-generics"))]
macro_rules! array_impls {
($($len:tt)+) => {
$(
Expand All @@ -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
Expand Down
19 changes: 19 additions & 0 deletions serde/src/ser/impls/const_generics_impls.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use ser::{Serialize, SerializeTuple, Serializer};

#[cfg(feature = "const-generics")]
impl<T, const N: usize> Serialize for [T; N]
where
T: Serialize,
{
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_tuple(N)?;
for e in self.iter() {
seq.serialize_element(e)?;
}
seq.end()
}
}
1 change: 1 addition & 0 deletions test_suite/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ publish = false
build = "build.rs"

[features]
const-generics = ["serde/const-generics"]
expandtest = []
unstable = ["serde/unstable"]

Expand Down
7 changes: 7 additions & 0 deletions test_suite/tests/test_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down