-
-
Notifications
You must be signed in to change notification settings - Fork 774
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
base: master
Are you sure you want to change the base?
Changes from all commits
108f1a9
70df1d8
20c790f
4ce376d
7de9499
dc01e9b
960e58a
01af1f8
f246a57
001f92d
46a2fd6
60bcfb8
23f953d
92c1d80
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -209,7 +209,9 @@ mod content { | |||||||||||||||||||||
use crate::lib::*; | ||||||||||||||||||||||
|
||||||||||||||||||||||
use crate::actually_private; | ||||||||||||||||||||||
use crate::de::value::{MapDeserializer, SeqDeserializer}; | ||||||||||||||||||||||
use crate::de::value::{ | ||||||||||||||||||||||
ExpectedInSeq, MapAccessDeserializer, MapDeserializer, SeqDeserializer, | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
use crate::de::{ | ||||||||||||||||||||||
self, size_hint, Deserialize, DeserializeSeed, Deserializer, EnumAccess, Expected, | ||||||||||||||||||||||
IgnoredAny, MapAccess, SeqAccess, Unexpected, Visitor, | ||||||||||||||||||||||
|
@@ -536,9 +538,7 @@ mod content { | |||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
/// This is the type of the map keys in an internally tagged enum. | ||||||||||||||||||||||
/// | ||||||||||||||||||||||
/// Not public API. | ||||||||||||||||||||||
pub enum TagOrContent<'de> { | ||||||||||||||||||||||
enum TagOrContent<'de> { | ||||||||||||||||||||||
Tag, | ||||||||||||||||||||||
Content(Content<'de>), | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
@@ -855,9 +855,9 @@ mod content { | |||||||||||||||||||||
|
||||||||||||||||||||||
impl<'de, T> Visitor<'de> for TaggedContentVisitor<T> | ||||||||||||||||||||||
where | ||||||||||||||||||||||
T: Deserialize<'de>, | ||||||||||||||||||||||
T: Deserialize<'de> + DeserializeSeed<'de>, | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
type Value = (T, Content<'de>); | ||||||||||||||||||||||
type Value = T::Value; | ||||||||||||||||||||||
|
||||||||||||||||||||||
fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||||||||||||||||||||||
fmt.write_str(self.expecting) | ||||||||||||||||||||||
|
@@ -867,42 +867,63 @@ mod content { | |||||||||||||||||||||
where | ||||||||||||||||||||||
S: SeqAccess<'de>, | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
let tag = match tri!(seq.next_element()) { | ||||||||||||||||||||||
let tag: T = match tri!(seq.next_element()) { | ||||||||||||||||||||||
Some(tag) => tag, | ||||||||||||||||||||||
None => { | ||||||||||||||||||||||
return Err(de::Error::missing_field(self.tag_name)); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
}; | ||||||||||||||||||||||
let rest = de::value::SeqAccessDeserializer::new(seq); | ||||||||||||||||||||||
Ok((tag, tri!(Content::deserialize(rest)))) | ||||||||||||||||||||||
tag.deserialize(de::value::SeqAccessDeserializer::new(seq)) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error> | ||||||||||||||||||||||
where | ||||||||||||||||||||||
M: MapAccess<'de>, | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
let mut tag = None; | ||||||||||||||||||||||
let mut vec = Vec::<(Content, Content)>::with_capacity(size_hint::cautious::<( | ||||||||||||||||||||||
Content, | ||||||||||||||||||||||
Content, | ||||||||||||||||||||||
)>(map.size_hint())); | ||||||||||||||||||||||
while let Some(k) = tri!(map.next_key_seed(TagOrContentVisitor::new(self.tag_name))) { | ||||||||||||||||||||||
match k { | ||||||||||||||||||||||
TagOrContent::Tag => { | ||||||||||||||||||||||
if tag.is_some() { | ||||||||||||||||||||||
return Err(de::Error::duplicate_field(self.tag_name)); | ||||||||||||||||||||||
// Read the first field. If it is a tag, immediately deserialize the typed data. | ||||||||||||||||||||||
// Otherwise, we collect everything until we find the tag, and then deserialize | ||||||||||||||||||||||
// using ContentDeserializer. | ||||||||||||||||||||||
match tri!(map.next_key_seed(TagOrContentVisitor::new(self.tag_name))) { | ||||||||||||||||||||||
Some(TagOrContent::Tag) => { | ||||||||||||||||||||||
let tag: T = tri!(map.next_value()); | ||||||||||||||||||||||
tag.deserialize(MapAccessDeserializer::new(map)) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
Some(TagOrContent::Content(key)) => { | ||||||||||||||||||||||
let mut tag = None::<T>; | ||||||||||||||||||||||
let mut vec = Vec::<(Content, Content)>::with_capacity(size_hint::cautious::<( | ||||||||||||||||||||||
Content, | ||||||||||||||||||||||
Content, | ||||||||||||||||||||||
)>( | ||||||||||||||||||||||
map.size_hint() | ||||||||||||||||||||||
)); | ||||||||||||||||||||||
|
||||||||||||||||||||||
let v = tri!(map.next_value()); | ||||||||||||||||||||||
vec.push((key, v)); | ||||||||||||||||||||||
|
||||||||||||||||||||||
while let Some(k) = | ||||||||||||||||||||||
tri!(map.next_key_seed(TagOrContentVisitor::new(self.tag_name))) | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
match k { | ||||||||||||||||||||||
TagOrContent::Tag => { | ||||||||||||||||||||||
if tag.is_some() { | ||||||||||||||||||||||
return Err(de::Error::duplicate_field(self.tag_name)); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
tag = Some(tri!(map.next_value())); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
TagOrContent::Content(k) => { | ||||||||||||||||||||||
let v = tri!(map.next_value()); | ||||||||||||||||||||||
vec.push((k, v)); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
tag = Some(tri!(map.next_value())); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
TagOrContent::Content(k) => { | ||||||||||||||||||||||
let v = tri!(map.next_value()); | ||||||||||||||||||||||
vec.push((k, v)); | ||||||||||||||||||||||
match tag { | ||||||||||||||||||||||
None => Err(de::Error::missing_field(self.tag_name)), | ||||||||||||||||||||||
Some(tag) => { | ||||||||||||||||||||||
tag.deserialize(ContentDeserializer::<M::Error>::new(Content::Map(vec))) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
match tag { | ||||||||||||||||||||||
None => Err(de::Error::missing_field(self.tag_name)), | ||||||||||||||||||||||
Some(tag) => Ok((tag, Content::Map(vec))), | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
@@ -2296,11 +2317,17 @@ mod content { | |||||||||||||||||||||
) | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
fn visit_seq<S>(self, _: S) -> Result<(), S::Error> | ||||||||||||||||||||||
fn visit_seq<S>(self, mut seq: S) -> Result<(), S::Error> | ||||||||||||||||||||||
where | ||||||||||||||||||||||
S: SeqAccess<'de>, | ||||||||||||||||||||||
{ | ||||||||||||||||||||||
Ok(()) | ||||||||||||||||||||||
match tri!(seq.next_element()) { | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This behaves quite differently from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried that initially, but that failed other tests and in general not what you want. The unit / unit struct represented in sequence as nothing, so we need to ensure that sequence is empty. This is consistent with normal behavior where struct deserialization from a sequence expects exact number of values, and those fact that flattened unit / unit struct considered as equal to the struct without fields. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, the serde/test_suite/tests/test_enum_internally_tagged.rs Lines 1447 to 1456 in 3aca38d
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hm but "nothing" should be pretty different conceptually from "ignored any". I'd expect a custom check just for the nothing case, whereas ignored any should be able to consume anything thrown at it silently. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code tries to read something, doesn't matter what. We expect an empty sequence, so if it contains some element, we fail. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, I'm sleepy - I thought you're changing how IgnoredAny works everywhere. I've expanded the context of the diff and I see this is a change on this one specific visitor. Please disregard my original comment 🤦♂️ Although I now wonder if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By default maps in serde allows unknown keys and when unit is flattened, all keys become unknown. But you're right -- in case of |
||||||||||||||||||||||
Some(IgnoredAny) => Err(de::Error::invalid_length( | ||||||||||||||||||||||
1 + seq.size_hint().unwrap_or(0), | ||||||||||||||||||||||
&ExpectedInSeq(0), | ||||||||||||||||||||||
)), | ||||||||||||||||||||||
None => Ok(()), | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
fn visit_map<M>(self, mut access: M) -> Result<(), M::Error> | ||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure, should we change behavior of
SeqAccessDeserializer
andMapAccessDeserializer
or introduce new private deserializers? From one hand those deserializers was created for support of various serde attributes. From the other hand, technically this is breaking change because those types are public.