-
-
Notifications
You must be signed in to change notification settings - Fork 794
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
44bd78d
aa8da3e
d7cec3b
b1fe66c
f8967fb
2c8ad9e
3c2b4ce
04de575
712c385
141f2aa
8fb8957
d4d8c24
52f9521
cfe357b
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.