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

Optimize internally tagged enums -- do not use internal buffer if tag is the first field #1922

Open
wants to merge 35 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4b61892
Explain code in ContentDeserializer::deserialize_newtype_struct and a…
Mingun Aug 17, 2024
e3f13a5
Cover all cases in ContentDeserializer::deserialize_option
Mingun Aug 17, 2024
3c2fd8a
Move test that was missed in 2cbfd37072a5af4774a77c8d3f7661dd3c1af194
Mingun Aug 17, 2024
35015a4
Add test for sequence in newtype_unit
Mingun Aug 7, 2023
6d5d18b
Unify handling of unit and unit structs in ContentDeserializer
Mingun Aug 7, 2023
b6ef86e
Consistently use visit_content_seq and visit_content_map when deseria…
Mingun Aug 11, 2024
a3a5571
Deserializer should not check correctness of data, this is the respon…
Mingun Aug 12, 2023
e09d488
Copy content::EnumDeserializer into FlatEnumDeserializer
Mingun Aug 15, 2024
dca0a20
Remove Option because Some variant is always used
Mingun Aug 15, 2024
112d56d
Store ContentDeserializer in FlatEnumDeserializer and FlatVariantDese…
Mingun Aug 15, 2024
c7bb1de
Add support for internally tagged enums in non self-describing formats
Mingun Aug 12, 2023
f70b26e
Add support for struct variants in untagged and adjacently tagged enu…
Mingun Aug 12, 2023
8fe93e5
Use deserialize_unit instead of deserialize_any for unit variants of …
Mingun Aug 12, 2023
e803c89
Extract first iteration - just copy body of loop
Mingun Feb 20, 2021
4c6a838
Replace `if let` by `match`
Mingun Oct 2, 2022
2d9f686
Introduce `drain_map` helper
Mingun Feb 20, 2021
ee57e43
Add tests for internally, adjacently and untagged enums
Mingun Oct 2, 2022
63d12a5
Move `TaggedContentVisitor` to generated code
Mingun Feb 20, 2021
58e2e7d
Wrap final match into __Visitor::visit method
Mingun Oct 3, 2022
4723444
Move implementation of __Visitor::visit above
Mingun Oct 3, 2022
4242d5c
Return final result from the root visitor
Mingun Oct 2, 2022
032dbfc
Return `ContentDeserializer` from `drain_map` directly
Mingun Feb 20, 2021
19b34f6
Fix #1495: Do not buffer content of the internally tagged enums if ta…
Mingun Feb 23, 2021
3554238
Introduce MapAccess::is_empty and SeqAccess::is_empty
Mingun Aug 4, 2023
fd4a264
Remove unnecessary intermediate deserializer when deserialize from se…
Mingun Oct 3, 2022
7473a0c
Allow to deserialize unit and unit structs from SeqAccess
Mingun Aug 7, 2023
7a1c507
Allow to deserialize newtype structs from MapAccessDeserializer
Mingun Jul 26, 2023
b954fc4
Allow to deserialize newtype structs from SeqAccess
Mingun Nov 3, 2020
741cc22
Consistently use #this_type and #this_value instead of __Field
Mingun Oct 30, 2020
00f2fec
Add ability to specify prefix for Field, FieldVisitor and Visitor str…
Mingun Oct 30, 2020
7b689c2
Run `cargo fmt`
Mingun Oct 2, 2022
40430fc
Split `deserialize_struct` into functions that generates visitor and …
Mingun Oct 30, 2020
03307b2
Move generated visitors for struct variants out of `match` expression
Mingun Jul 8, 2023
89e2b36
Run `cargo fmt`
Mingun Oct 3, 2022
132dc81
Allow to deserialize unit structs from the empty sequences and maps
Mingun Feb 20, 2021
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
32 changes: 32 additions & 0 deletions serde/src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1739,6 +1739,17 @@ pub trait SeqAccess<'de> {
fn size_hint(&self) -> Option<usize> {
None
}

/// Checks if this sequence is empty. If that is unknown, implementation can
/// return `false`, but deserialization of internally tagged enums would
/// work incorrectly in that case.
#[inline]
fn is_empty(&mut self) -> bool {
match self.size_hint() {
Some(0) => true,
_ => false,
}
}
}

impl<'de, A> SeqAccess<'de> for &mut A
Expand Down Expand Up @@ -1767,6 +1778,11 @@ where
fn size_hint(&self) -> Option<usize> {
(**self).size_hint()
}

#[inline]
fn is_empty(&mut self) -> bool {
(**self).is_empty()
}
}

////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -1892,6 +1908,17 @@ pub trait MapAccess<'de> {
fn size_hint(&self) -> Option<usize> {
None
}

/// Checks if this map is empty. If that is unknown, implementation can
/// return `false`, but deserialization of internally tagged enums would
/// work incorrectly in that case.
#[inline]
fn is_empty(&mut self) -> bool {
match self.size_hint() {
Some(0) => true,
_ => false,
}
}
}

impl<'de, A> MapAccess<'de> for &mut A
Expand Down Expand Up @@ -1958,6 +1985,11 @@ where
fn size_hint(&self) -> Option<usize> {
(**self).size_hint()
}

#[inline]
fn is_empty(&mut self) -> bool {
(**self).is_empty()
}
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
10 changes: 10 additions & 0 deletions serde/src/de/size_hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ where
helper(iter.size_hint())
}

pub fn is_empty<I>(iter: &I) -> bool
where
I: Iterator,
{
match iter.size_hint() {
(0, _) => true,
_ => false,
}
}

#[cfg(any(feature = "std", feature = "alloc"))]
pub fn cautious<Element>(hint: Option<usize>) -> usize {
const MAX_PREALLOC_BYTES: usize = 1024 * 1024;
Expand Down
79 changes: 77 additions & 2 deletions serde/src/de/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,9 +1076,40 @@ where
visitor.visit_seq(self.seq)
}

fn deserialize_unit<V>(mut self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
// Covered by tests/test_enum_internally_tagged.rs/newtype_unit
if self.seq.is_empty() {
visitor.visit_unit()
} else {
visitor.visit_seq(self.seq)
}
}

fn deserialize_unit_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
// Covered by tests/test_enum_internally_tagged.rs/newtype_unit_struct
self.deserialize_unit(visitor)
}

fn deserialize_newtype_struct<V>(self, _name: &str, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}

forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
bytes byte_buf option seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
Expand Down Expand Up @@ -1253,6 +1284,10 @@ where
fn size_hint(&self) -> Option<usize> {
size_hint::from_bounds(&self.iter)
}

fn is_empty(&mut self) -> bool {
size_hint::is_empty(&self.iter)
}
}

impl<'de, I, E> de::SeqAccess<'de> for MapDeserializer<'de, I, E>
Expand Down Expand Up @@ -1281,6 +1316,10 @@ where
fn size_hint(&self) -> Option<usize> {
size_hint::from_bounds(&self.iter)
}

fn is_empty(&mut self) -> bool {
size_hint::is_empty(&self.iter)
}
}

// Cannot #[derive(Clone)] because of the bound `Second<I::Item>: Clone`.
Expand Down Expand Up @@ -1479,6 +1518,30 @@ where
visitor.visit_map(self.map)
}

fn deserialize_unit<V>(mut self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
// For newtype_variant_containing_unit
if self.map.is_empty() {
visitor.visit_unit()
} else {
visitor.visit_map(self.map)
}
}

fn deserialize_unit_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
// For newtype_variant_containing_unit_struct
self.deserialize_unit(visitor)
}

fn deserialize_enum<V>(
self,
_name: &str,
Expand All @@ -1491,9 +1554,21 @@ where
visitor.visit_enum(self)
}

fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
// For test_internally_tagged_enum
visitor.visit_newtype_struct(self)
}

forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
bytes byte_buf option seq tuple
tuple_struct map struct identifier ignored_any
}
}
Expand Down
Loading
Loading