Skip to content

Commit

Permalink
Add "const-generics" feature
Browse files Browse the repository at this point in the history
  • Loading branch information
c410-f3r committed Oct 4, 2020
1 parent be7d0e7 commit c8f0b33
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 2 deletions.
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 @@ -943,6 +946,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 @@ -960,6 +964,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 @@ -969,6 +974,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 @@ -1047,6 +1053,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))
}
}
5 changes: 3 additions & 2 deletions serde/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,10 @@
must_use_candidate,
)
)]
// Constant generics
#![cfg_attr(feature = "const-generics", feature(min_const_generics))]
// Rustc lints.
#![forbid(unsafe_code)]
#![deny(missing_docs, unused_imports)]
#![deny(missing_docs, unused_imports, unsafe_code)]

////////////////////////////////////////////////////////////////////////////////

Expand Down
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 @@ -347,6 +347,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

0 comments on commit c8f0b33

Please sign in to comment.