From 6b8d8e4235d35b0bbc18afc08b2122a6586e79d3 Mon Sep 17 00:00:00 2001 From: Mingun Date: Wed, 9 Aug 2023 21:02:01 +0500 Subject: [PATCH 1/5] Move bytes_to_str closer to use location --- serde_derive/src/de.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 996e97e86..d3c0bb5f8 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -2219,14 +2219,6 @@ fn deserialize_identifier( "field identifier" }); - let bytes_to_str = if fallthrough.is_some() || collect_other_fields { - None - } else { - Some(quote! { - let __value = &_serde::__private::from_utf8_lossy(__value); - }) - }; - let ( value_as_str_content, value_as_borrowed_str_content, @@ -2393,6 +2385,14 @@ fn deserialize_identifier( } }; + let bytes_to_str = if fallthrough.is_some() || collect_other_fields { + None + } else { + Some(quote! { + let __value = &_serde::__private::from_utf8_lossy(__value); + }) + }; + let visit_borrowed = if fallthrough_borrowed.is_some() || collect_other_fields { let str_mapping = str_mapping.clone(); let bytes_mapping = bytes_mapping.clone(); From 180335ff945cdcc4541e87a065c899401b236427 Mon Sep 17 00:00:00 2001 From: Mingun Date: Wed, 9 Aug 2023 20:21:54 +0500 Subject: [PATCH 2/5] Do not generate Field structure for each struct (review this commit with "ignore whitespace changes" option on) --- serde/src/de/identifier.rs | 488 +++++++++++++++++++++++++++++++++++++ serde/src/de/mod.rs | 2 + serde/src/private/de.rs | 3 + serde_derive/src/de.rs | 119 +++++---- 4 files changed, 551 insertions(+), 61 deletions(-) create mode 100644 serde/src/de/identifier.rs diff --git a/serde/src/de/identifier.rs b/serde/src/de/identifier.rs new file mode 100644 index 000000000..1463b3d31 --- /dev/null +++ b/serde/src/de/identifier.rs @@ -0,0 +1,488 @@ +use crate::lib::*; + +#[cfg(any(feature = "std", feature = "alloc"))] +use crate::__private::de::Content; +use crate::__private::from_utf8_lossy; +use crate::format; +use crate::de::{DeserializeSeed, Deserializer, Error, Unexpected, Visitor}; + +/// Represents field of a struct +#[derive(Debug)] +pub enum Field { + /// Represents non-skipped field with specified index assigned to the field + /// after skipping all non-deserializable fields. For example, for a struct + /// + /// ``` + /// # use serde_derive::Deserialize; + /// #[derive(Deserialize)] + /// struct StructWithSkippedFields { + /// #[serde(skip_deserializing)] + /// skipped: (), + /// + /// field: (), + /// } + /// ``` + /// field `field` will have an index `0`, because it is the first non-skipped + /// field of the struct (and counting starts with zero). + Field(usize), + /// Field that is not present in struct + Unknown, +} + +/// Creates a field deserialization seed that wrapped array with all possible +/// field names and their aliases. +/// +/// # Example +/// +/// ``` +/// # use serde::__private::de::FieldSeed; +/// let seed = FieldSeed::new(&[ +/// // First field with two aliases +/// &["a", "alias 1", "alias 2"], +/// // Second field with one alias +/// &["b", "alias 3"], +/// // Third field without aliases +/// &["c"], +/// ]); +/// ``` +#[derive(Debug)] +pub struct FieldSeed<'a> { + /// List of all field aliases in order of their presence in the struct + aliases: &'a [&'a [&'a str]], +} + +impl<'a> FieldSeed<'a> { + #[allow(missing_docs)] + pub const fn new(aliases: &'a [&'a [&'a str]]) -> Self { + Self { aliases } + } + + fn matches(&self, value: &[u8]) -> Option { + self.aliases + .iter() + .position(|aliases| aliases.iter().any(|a| a.as_bytes() == value)) + } + + fn index(&self, value: u64) -> Field { + if value < self.aliases.len() as u64 { + Field::Field(value as usize) + } else { + Field::Unknown + } + } +} + +impl<'de, 'a, 'b> Visitor<'de> for &'b mut FieldSeed<'a> { + type Value = Field; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("field identifier") + } + + fn visit_u64(self, value: u64) -> Result + where + E: Error, + { + Ok(self.index(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + self.visit_bytes(value.as_bytes()) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + match self.matches(value) { + Some(i) => Ok(Field::Field(i)), + None => Ok(Field::Unknown), + } + } +} + +impl<'de, 'a, 'b> DeserializeSeed<'de> for &'b mut FieldSeed<'a> { + type Value = Field; + + #[inline] + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(self) + } +} + +//////////////////////////////////////////////////////////////////////////////// + +/// Represents field of a struct that denies unknown fields +#[derive(Debug)] +pub enum FieldStrong { + /// Represents non-skipped field with specified index assigned to the field + /// after skipping all non-deserializable fields. For example, for a struct + /// + /// ``` + /// # use serde_derive::Deserialize; + /// #[derive(Deserialize)] + /// struct StructWithSkippedFields { + /// #[serde(skip_deserializing)] + /// skipped: (), + /// + /// field: (), + /// } + /// ``` + /// field `field` will have an index `0`, because it is the first non-skipped + /// field of the struct (and counting starts with zero). + Field(usize), +} + +/// Creates a field deserialization seed that wrapped array with all possible +/// field names and their aliases. +/// +/// # Example +/// +/// ``` +/// # use serde::__private::de::FieldStrongSeed; +/// let seed = FieldStrongSeed::new( +/// &[ +/// // First field with two aliases +/// &["a", "alias 1", "alias 2"], +/// // Second field with one alias +/// &["b", "alias 3"], +/// // Third field without aliases +/// &["c"], +/// ], +/// &[ +/// // First field with two aliases +/// "a", "alias 1", "alias 2", +/// // Second field with one alias +/// "b", "alias 3", +/// // Third field without aliases +/// "c", +/// ], +/// ); +/// ``` +#[derive(Debug)] +pub struct FieldStrongSeed<'a> { + seed: FieldSeed<'a>, + fields: &'static [&'static str], +} + +impl<'a> FieldStrongSeed<'a> { + #[allow(missing_docs)] + pub const fn new(aliases: &'a [&'a [&'a str]], fields: &'static [&'static str]) -> Self { + Self { + seed: FieldSeed::new(aliases), + fields, + } + } + + fn matches(&self, value: &[u8]) -> Option { + match self.seed.matches(value) { + Some(i) => Some(FieldStrong::Field(i)), + None => None, + } + } +} + +impl<'de, 'a, 'b> Visitor<'de> for &'b mut FieldStrongSeed<'a> { + type Value = FieldStrong; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("field identifier") + } + + fn visit_u64(self, value: u64) -> Result + where + E: Error, + { + match self.seed.index(value) { + Field::Field(i) => Ok(FieldStrong::Field(i)), + Field::Unknown => { + // length of string "field index 0 <= i < " and u64::MAX.to_string() + let mut buf = [0u8; 21 + 20]; + let mut writer = format::Buf::new(&mut buf); + fmt::Write::write_fmt(&mut writer, format_args!("field index 0 <= i < {}", value)) + .unwrap(); + Err(Error::invalid_value( + Unexpected::Unsigned(value), + &writer.as_str(), + )) + } + } + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match self.matches(value.as_bytes()) { + Some(field) => Ok(field), + None => Err(Error::unknown_field(value, self.fields)), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + match self.matches(value) { + Some(field) => Ok(field), + None => Err(Error::unknown_field(&from_utf8_lossy(value), self.fields)), + } + } +} + +impl<'de, 'a, 'b> DeserializeSeed<'de> for &'b mut FieldStrongSeed<'a> { + type Value = FieldStrong; + + #[inline] + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(self) + } +} + +//////////////////////////////////////////////////////////////////////////////// + +/// Represents field of a struct with flatten fields +#[cfg(any(feature = "std", feature = "alloc"))] +#[derive(Debug)] +pub enum FieldFlatten<'de> { + /// Represents non-skipped field with specified index assigned to the field + /// after skipping all non-deserializable fields. For example, for a struct + /// + /// ``` + /// # use serde_derive::Deserialize; + /// #[derive(Deserialize)] + /// struct StructWithSkippedFields { + /// #[serde(skip_deserializing)] + /// skipped: (), + /// + /// field: (), + /// } + /// ``` + /// field `field` will have an index `0`, because it is the first non-skipped + /// field of the struct (and counting starts with zero). + Field(usize), + /// Field that is not present in the struct and will be forwarded to the + /// flatten fields + Other(Content<'de>), +} + +/// Creates a field deserialization seed that wrapped array with all possible +/// field names and their aliases. +/// +/// # Example +/// +/// ``` +/// # use serde::__private::de::FieldFlattenSeed; +/// let seed = FieldFlattenSeed::new(&[ +/// // First field with two aliases +/// &["a", "alias 1", "alias 2"], +/// // Second field with one alias +/// &["b", "alias 3"], +/// // Third field without aliases +/// &["c"], +/// ]); +/// ``` +#[cfg(any(feature = "std", feature = "alloc"))] +#[derive(Debug)] +pub struct FieldFlattenSeed<'a> { + seed: FieldSeed<'a>, +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'a> FieldFlattenSeed<'a> { + #[allow(missing_docs)] + pub const fn new(aliases: &'a [&'a [&'a str]]) -> Self { + Self { + seed: FieldSeed::new(aliases), + } + } + + fn matches(&self, value: &[u8]) -> Option> { + match self.seed.matches(value) { + Some(i) => Some(FieldFlatten::Field(i)), + None => None, + } + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'de, 'a, 'b> Visitor<'de> for &'b mut FieldFlattenSeed<'a> { + type Value = FieldFlatten<'de>; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("field identifier") + } + + fn visit_bool(self, value: bool) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::Bool(value))) + } + + fn visit_i8(self, value: i8) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::I8(value))) + } + + fn visit_i16(self, value: i16) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::I16(value))) + } + + fn visit_i32(self, value: i32) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::I32(value))) + } + + fn visit_i64(self, value: i64) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::I64(value))) + } + + fn visit_u8(self, value: u8) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::U8(value))) + } + + fn visit_u16(self, value: u16) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::U16(value))) + } + + fn visit_u32(self, value: u32) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::U32(value))) + } + + fn visit_u64(self, value: u64) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::U64(value))) + } + + fn visit_f32(self, value: f32) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::F32(value))) + } + + fn visit_f64(self, value: f64) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::F64(value))) + } + + fn visit_char(self, value: char) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::Char(value))) + } + + fn visit_unit(self) -> Result + where + E: Error, + { + Ok(FieldFlatten::Other(Content::Unit)) + } + + fn visit_string(self, value: String) -> Result + where + E: Error, + { + match self.matches(value.as_bytes()) { + Some(field) => Ok(field), + None => Ok(FieldFlatten::Other(Content::String(value))), + } + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match self.matches(value.as_bytes()) { + Some(field) => Ok(field), + None => Ok(FieldFlatten::Other(Content::String(value.to_string()))), + } + } + + fn visit_borrowed_str(self, value: &'de str) -> Result + where + E: Error, + { + match self.matches(value.as_bytes()) { + Some(field) => Ok(field), + None => Ok(FieldFlatten::Other(Content::Str(value))), + } + } + + fn visit_byte_buf(self, value: Vec) -> Result + where + E: Error, + { + match self.matches(&value) { + Some(field) => Ok(field), + None => Ok(FieldFlatten::Other(Content::ByteBuf(value))), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + match self.matches(value) { + Some(field) => Ok(field), + None => Ok(FieldFlatten::Other(Content::ByteBuf(value.to_vec()))), + } + } + + fn visit_borrowed_bytes(self, value: &'de [u8]) -> Result + where + E: Error, + { + match self.matches(value) { + Some(field) => Ok(field), + None => Ok(FieldFlatten::Other(Content::Bytes(value))), + } + } +} + +#[cfg(any(feature = "std", feature = "alloc"))] +impl<'de, 'a, 'b> DeserializeSeed<'de> for &'b mut FieldFlattenSeed<'a> { + type Value = FieldFlatten<'de>; + + #[inline] + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(self) + } +} diff --git a/serde/src/de/mod.rs b/serde/src/de/mod.rs index 21cfd0414..e6b657a46 100644 --- a/serde/src/de/mod.rs +++ b/serde/src/de/mod.rs @@ -118,6 +118,8 @@ use crate::lib::*; pub mod value; +#[cfg(not(no_serde_derive))] +pub(crate) mod identifier; mod ignored_any; mod impls; pub(crate) mod size_hint; diff --git a/serde/src/private/de.rs b/serde/src/private/de.rs index 50ae6ed15..c6755299a 100644 --- a/serde/src/private/de.rs +++ b/serde/src/private/de.rs @@ -16,6 +16,9 @@ pub use self::content::{ TagOrContentField, TagOrContentFieldVisitor, TaggedContentVisitor, UntaggedUnitVisitor, }; +pub use crate::de::identifier::{Field, FieldSeed, FieldStrong, FieldStrongSeed}; +#[cfg(any(feature = "std", feature = "alloc"))] +pub use crate::de::identifier::{FieldFlatten, FieldFlattenSeed}; pub use crate::seed::InPlaceSeed; /// If the missing field is of type `Option` then treat is as `None`, diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index d3c0bb5f8..c17e6889e 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -966,7 +966,7 @@ fn deserialize_struct( .collect(); let has_flatten = has_flatten(fields); - let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs, has_flatten); + let (consts, field, field_seed) = deserialize_field_identifier(&field_names_idents, cattrs, has_flatten); // untagged struct variants do not get a visit_seq method. The same applies to // structs that only have a map representation. @@ -1001,6 +1001,8 @@ fn deserialize_struct( fields, cattrs, has_flatten, + field, + field_seed, )); let visitor_seed = match form { @@ -1063,8 +1065,6 @@ fn deserialize_struct( }; quote_block! { - #field_visitor - #[doc(hidden)] struct __Visitor #de_impl_generics #where_clause { marker: _serde::__private::PhantomData<#this_type #ty_generics>, @@ -1092,6 +1092,7 @@ fn deserialize_struct( #visitor_seed #fields_stmt + #consts #dispatch } @@ -1130,7 +1131,7 @@ fn deserialize_struct_in_place( }) .collect(); - let field_visitor = deserialize_field_identifier(&field_names_idents, cattrs, false); + let (consts, field, field_seed) = deserialize_field_identifier(&field_names_idents, cattrs, false); let mut_seq = if field_names_idents.is_empty() { quote!(_) @@ -1138,7 +1139,9 @@ fn deserialize_struct_in_place( quote!(mut __seq) }; let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, expecting)); - let visit_map = Stmts(deserialize_map_in_place(params, fields, cattrs)); + let visit_map = Stmts(deserialize_map_in_place( + params, fields, cattrs, field, field_seed, + )); let field_names = field_names_idents .iter() .flat_map(|&(_, _, aliases)| aliases); @@ -1149,8 +1152,6 @@ fn deserialize_struct_in_place( let place_life = place_lifetime(); Some(quote_block! { - #field_visitor - #[doc(hidden)] struct __Visitor #in_place_impl_generics #where_clause { place: &#place_life mut #this_type #ty_generics, @@ -1183,6 +1184,7 @@ fn deserialize_struct_in_place( #[doc(hidden)] const FIELDS: &'static [&'static str] = &[ #(#field_names),* ]; + #consts _serde::Deserializer::deserialize_struct(__deserializer, #type_name, FIELDS, __Visitor { place: __place, @@ -2055,26 +2057,34 @@ fn deserialize_field_identifier( fields: &[(&str, Ident, &BTreeSet)], cattrs: &attr::Container, has_flatten: bool, -) -> Stmts { - let (ignore_variant, fallthrough) = if has_flatten { - let ignore_variant = quote!(__other(_serde::__private::de::Content<'de>),); - let fallthrough = quote!(_serde::__private::Ok(__Field::__other(__value))); - (Some(ignore_variant), Some(fallthrough)) +) -> (TokenStream, TokenStream, TokenStream) { + let (field, field_seed) = if has_flatten { + ( + quote!(_serde::__private::de::FieldFlatten), + quote!(_serde::__private::de::FieldFlattenSeed::new(ALIASES)), + ) } else if cattrs.deny_unknown_fields() { - (None, None) + ( + quote!(_serde::__private::de::FieldStrong), + quote!(_serde::__private::de::FieldStrongSeed::new(ALIASES, FIELDS)), + ) } else { - let ignore_variant = quote!(__ignore,); - let fallthrough = quote!(_serde::__private::Ok(__Field::__ignore)); - (Some(ignore_variant), Some(fallthrough)) + ( + quote!(_serde::__private::de::Field), + quote!(_serde::__private::de::FieldSeed::new(ALIASES)), + ) }; - Stmts(deserialize_generated_identifier( - fields, - has_flatten, - false, - ignore_variant, - fallthrough, - )) + // `aliases` also contains a main name + let aliases = fields + .iter() + .map(|(_, _, aliases)| quote!(&[ #(#aliases),* ])); + let consts = quote! { + #[doc(hidden)] + const ALIASES: &[&[&str]] = &[ #(#aliases),* ]; + }; + + (consts, field, field_seed) } // Generates `Deserialize::deserialize` body for an enum with @@ -2473,6 +2483,8 @@ fn deserialize_map( fields: &[Field], cattrs: &attr::Container, has_flatten: bool, + field_enum: TokenStream, + field_seed: TokenStream, ) -> Fragment { // Create the field names for the fields. let fields_names: Vec<_> = fields @@ -2508,7 +2520,8 @@ fn deserialize_map( let value_arms = fields_names .iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(field, name)| { + .enumerate() + .map(|(i, (field, name))| { let deser_name = field.attrs.name().deserialize_name(); let visit = match field.attrs.deserialize_with() { @@ -2535,7 +2548,7 @@ fn deserialize_map( } }; quote! { - __Field::#name => { + #field_enum::Field(#i) => { if _serde::__private::Option::is_some(&#name) { return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name)); } @@ -2547,7 +2560,7 @@ fn deserialize_map( // Visit ignored values to consume them let ignored_arm = if has_flatten { Some(quote! { - __Field::__other(__name) => { + #field_enum::Other(__name) => { __collect.push(_serde::__private::Some(( __name, _serde::de::MapAccess::next_value(&mut __map)?))); @@ -2561,22 +2574,13 @@ fn deserialize_map( }) }; - let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); - let match_keys = if cattrs.deny_unknown_fields() && all_skipped { - quote! { - // FIXME: Once feature(exhaustive_patterns) is stable: - // let _serde::__private::None::<__Field> = _serde::de::MapAccess::next_key(&mut __map)?; - _serde::__private::Option::map( - _serde::de::MapAccess::next_key::<__Field>(&mut __map)?, - |__impossible| match __impossible {}); - } - } else { - quote! { - while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - #(#value_arms)* - #ignored_arm - } + let match_keys = quote! { + let mut __seed = #field_seed; + while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key_seed(&mut __map, &mut __seed)? { + match __key { + #(#value_arms)* + #field_enum::Field(_) => unreachable!(), + #ignored_arm } } }; @@ -2690,6 +2694,8 @@ fn deserialize_map_in_place( params: &Parameters, fields: &[Field], cattrs: &attr::Container, + field_enum: TokenStream, + field_seed: TokenStream, ) -> Fragment { assert!( !has_flatten(fields), @@ -2718,7 +2724,8 @@ fn deserialize_map_in_place( let value_arms_from = fields_names .iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing()) - .map(|(field, name)| { + .enumerate() + .map(|(i, (field, name))| { let deser_name = field.attrs.name().deserialize_name(); let member = &field.member; @@ -2742,7 +2749,7 @@ fn deserialize_map_in_place( } }; quote! { - __Field::#name => { + #field_enum::Field(#i) => { if #name { return _serde::__private::Err(<__A::Error as _serde::de::Error>::duplicate_field(#deser_name)); } @@ -2761,23 +2768,13 @@ fn deserialize_map_in_place( }) }; - let all_skipped = fields.iter().all(|field| field.attrs.skip_deserializing()); - - let match_keys = if cattrs.deny_unknown_fields() && all_skipped { - quote! { - // FIXME: Once feature(exhaustive_patterns) is stable: - // let _serde::__private::None::<__Field> = _serde::de::MapAccess::next_key(&mut __map)?; - _serde::__private::Option::map( - _serde::de::MapAccess::next_key::<__Field>(&mut __map)?, - |__impossible| match __impossible {}); - } - } else { - quote! { - while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key::<__Field>(&mut __map)? { - match __key { - #(#value_arms_from)* - #ignored_arm - } + let match_keys = quote! { + let mut __seed = #field_seed; + while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key_seed(&mut __map, &mut __seed)? { + match __key { + #(#value_arms_from)* + #field_enum::Field(_) => unreachable!(), + #ignored_arm } } }; From c29a360bad92c9f306030e4f2a118938157d474a Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 10 Aug 2023 19:03:39 +0500 Subject: [PATCH 3/5] Inline #match_keys --- serde_derive/src/de.rs | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index c17e6889e..80db51ba0 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -2574,17 +2574,6 @@ fn deserialize_map( }) }; - let match_keys = quote! { - let mut __seed = #field_seed; - while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key_seed(&mut __map, &mut __seed)? { - match __key { - #(#value_arms)* - #field_enum::Field(_) => unreachable!(), - #ignored_arm - } - } - }; - let extract_values = fields_names .iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) @@ -2675,7 +2664,14 @@ fn deserialize_map( #let_collect - #match_keys + let mut __seed = #field_seed; + while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key_seed(&mut __map, &mut __seed)? { + match __key { + #(#value_arms)* + #field_enum::Field(_) => unreachable!(), + #ignored_arm + } + } #let_default @@ -2768,17 +2764,6 @@ fn deserialize_map_in_place( }) }; - let match_keys = quote! { - let mut __seed = #field_seed; - while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key_seed(&mut __map, &mut __seed)? { - match __key { - #(#value_arms_from)* - #field_enum::Field(_) => unreachable!(), - #ignored_arm - } - } - }; - let check_flags = fields_names .iter() .filter(|&&(field, _)| !field.attrs.skip_deserializing()) @@ -2827,7 +2812,14 @@ fn deserialize_map_in_place( quote_block! { #(#let_flags)* - #match_keys + let mut __seed = #field_seed; + while let _serde::__private::Some(__key) = _serde::de::MapAccess::next_key_seed(&mut __map, &mut __seed)? { + match __key { + #(#value_arms_from)* + #field_enum::Field(_) => unreachable!(), + #ignored_arm + } + } #let_default From 6f03a9eff7682dc8cff596b64772044319d67780 Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 10 Aug 2023 19:25:16 +0500 Subject: [PATCH 4/5] Remove generation of unused components of tuple --- serde_derive/src/de.rs | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index 80db51ba0..afeb9e10a 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -952,17 +952,9 @@ fn deserialize_struct( let field_names_idents: Vec<_> = fields .iter() - .enumerate() // Skip fields that shouldn't be deserialized or that were flattened, // so they don't appear in the storage in their literal form - .filter(|&(_, field)| !field.attrs.skip_deserializing() && !field.attrs.flatten()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) - }) + .filter(|field| !field.attrs.skip_deserializing() && !field.attrs.flatten()) .collect(); let has_flatten = has_flatten(fields); @@ -1026,7 +1018,7 @@ fn deserialize_struct( } else { let field_names = field_names_idents .iter() - .flat_map(|&(_, _, aliases)| aliases); + .flat_map(|field| field.attrs.aliases()); Some(quote! { #[doc(hidden)] @@ -1120,15 +1112,7 @@ fn deserialize_struct_in_place( let field_names_idents: Vec<_> = fields .iter() - .enumerate() - .filter(|&(_, field)| !field.attrs.skip_deserializing()) - .map(|(i, field)| { - ( - field.attrs.name().deserialize_name(), - field_i(i), - field.attrs.aliases(), - ) - }) + .filter(|field| !field.attrs.skip_deserializing()) .collect(); let (consts, field, field_seed) = deserialize_field_identifier(&field_names_idents, cattrs, false); @@ -1144,7 +1128,7 @@ fn deserialize_struct_in_place( )); let field_names = field_names_idents .iter() - .flat_map(|&(_, _, aliases)| aliases); + .flat_map(|field| field.attrs.aliases()); let type_name = cattrs.name().deserialize_name(); let in_place_impl_generics = de_impl_generics.in_place(); @@ -2054,7 +2038,7 @@ fn deserialize_generated_identifier( /// Generates enum and its `Deserialize` implementation that represents each /// non-skipped field of the struct fn deserialize_field_identifier( - fields: &[(&str, Ident, &BTreeSet)], + fields: &[&Field], cattrs: &attr::Container, has_flatten: bool, ) -> (TokenStream, TokenStream, TokenStream) { @@ -2075,10 +2059,11 @@ fn deserialize_field_identifier( ) }; - // `aliases` also contains a main name - let aliases = fields - .iter() - .map(|(_, _, aliases)| quote!(&[ #(#aliases),* ])); + let aliases = fields.iter().map(|field| { + // `aliases` also contains a main name + let aliases = field.attrs.aliases(); + quote!(&[ #(#aliases),* ]) + }); let consts = quote! { #[doc(hidden)] const ALIASES: &[&[&str]] = &[ #(#aliases),* ]; From d4aad552cab3e64735605d28ac3c2b99c863f341 Mon Sep 17 00:00:00 2001 From: Mingun Date: Thu, 10 Aug 2023 23:18:23 +0500 Subject: [PATCH 5/5] Do not generate Field structure for each variant --- serde/src/de/identifier.rs | 198 +++++++++++++++++++++++++++++++++++++ serde/src/private/de.rs | 4 +- serde_derive/src/de.rs | 62 ++++++------ 3 files changed, 235 insertions(+), 29 deletions(-) diff --git a/serde/src/de/identifier.rs b/serde/src/de/identifier.rs index 1463b3d31..3af5cb261 100644 --- a/serde/src/de/identifier.rs +++ b/serde/src/de/identifier.rs @@ -486,3 +486,201 @@ impl<'de, 'a, 'b> DeserializeSeed<'de> for &'b mut FieldFlattenSeed<'a> { deserializer.deserialize_identifier(self) } } + +//////////////////////////////////////////////////////////////////////////////// + +/// Creates a field deserialization seed that wrapped array with all possible +/// field names and their aliases. +/// +/// # Example +/// +/// ``` +/// # use serde::__private::de::VariantSeed; +/// let seed = VariantSeed::new( +/// &[ +/// // First field with two aliases +/// &["a", "alias 1", "alias 2"], +/// // Second field with one alias +/// &["b", "alias 3"], +/// // Third field without aliases +/// &["c"], +/// ], +/// &[ +/// // First field with two aliases +/// "a", "alias 1", "alias 2", +/// // Second field with one alias +/// "b", "alias 3", +/// // Third field without aliases +/// "c", +/// ], +/// ); +/// ``` +#[derive(Debug)] +pub struct VariantSeed<'a> { + aliases: &'a [&'a [&'a str]], + variants: &'static [&'static str], +} + +impl<'a> VariantSeed<'a> { + #[allow(missing_docs)] + pub const fn new(aliases: &'a [&'a [&'a str]], variants: &'static [&'static str]) -> Self { + Self { aliases, variants } + } + + fn matches(&self, value: &[u8]) -> Option { + self.aliases + .iter() + .position(|variant| variant.iter().any(|v| v.as_bytes() == value)) + } +} + +impl<'de, 'a> Visitor<'de> for VariantSeed<'a> { + type Value = usize; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("variant identifier") + } + + fn visit_u64(self, value: u64) -> Result + where + E: Error, + { + if value < self.aliases.len() as u64 { + Ok(value as usize) + } else { + // length of string "variant index 0 <= i < " and u64::MAX.to_string() + let mut buf = [0u8; 23 + 20]; + let mut writer = format::Buf::new(&mut buf); + fmt::Write::write_fmt( + &mut writer, + format_args!("variant index 0 <= i < {}", value), + ) + .unwrap(); + Err(Error::invalid_value( + Unexpected::Unsigned(value), + &writer.as_str(), + )) + } + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + match self.matches(value.as_bytes()) { + Some(index) => Ok(index), + None => Err(Error::unknown_variant(value, self.variants)), + } + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + match self.matches(value) { + Some(index) => Ok(index), + None => Err(Error::unknown_variant( + &from_utf8_lossy(value), + self.variants, + )), + } + } +} + +impl<'de, 'a> DeserializeSeed<'de> for VariantSeed<'a> { + type Value = usize; + + #[inline] + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(self) + } +} + +//////////////////////////////////////////////////////////////////////////////// + +/// Creates a field deserialization seed that wrapped array with all possible +/// field names and their aliases. +/// +/// # Example +/// +/// ``` +/// # use serde::__private::de::VariantOtherSeed; +/// let seed = VariantOtherSeed::new( +/// &[ +/// // First field with two aliases +/// &["a", "alias 1", "alias 2"], +/// // Second field with one alias +/// &["b", "alias 3"], +/// // Third field without aliases +/// &["c"], +/// ], +/// 2, +/// ); +/// ``` +#[derive(Debug)] +pub struct VariantOtherSeed<'a> { + aliases: &'a [&'a [&'a str]], + other: usize, +} + +impl<'a> VariantOtherSeed<'a> { + #[allow(missing_docs)] + pub const fn new(aliases: &'a [&'a [&'a str]], other: usize) -> Self { + Self { aliases, other } + } + + fn matches(&self, value: &[u8]) -> usize { + self.aliases + .iter() + .position(|variant| variant.iter().any(|v| v.as_bytes() == value)) + .unwrap_or(self.other) + } +} + +impl<'de, 'a> Visitor<'de> for VariantOtherSeed<'a> { + type Value = usize; + + fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("variant identifier") + } + + fn visit_u64(self, value: u64) -> Result + where + E: Error, + { + if value < self.aliases.len() as u64 { + Ok(value as usize) + } else { + Ok(self.other) + } + } + + fn visit_str(self, value: &str) -> Result + where + E: Error, + { + Ok(self.matches(value.as_bytes())) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: Error, + { + Ok(self.matches(value)) + } +} + +impl<'de, 'a> DeserializeSeed<'de> for VariantOtherSeed<'a> { + type Value = usize; + + #[inline] + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_identifier(self) + } +} diff --git a/serde/src/private/de.rs b/serde/src/private/de.rs index c6755299a..7f2d0c604 100644 --- a/serde/src/private/de.rs +++ b/serde/src/private/de.rs @@ -16,7 +16,9 @@ pub use self::content::{ TagOrContentField, TagOrContentFieldVisitor, TaggedContentVisitor, UntaggedUnitVisitor, }; -pub use crate::de::identifier::{Field, FieldSeed, FieldStrong, FieldStrongSeed}; +pub use crate::de::identifier::{ + Field, FieldSeed, FieldStrong, FieldStrongSeed, VariantOtherSeed, VariantSeed, +}; #[cfg(any(feature = "std", feature = "alloc"))] pub use crate::de::identifier::{FieldFlatten, FieldFlattenSeed}; pub use crate::seed::InPlaceSeed; diff --git a/serde_derive/src/de.rs b/serde_derive/src/de.rs index afeb9e10a..af08fd9a1 100644 --- a/serde_derive/src/de.rs +++ b/serde_derive/src/de.rs @@ -1267,50 +1267,50 @@ fn deserialize_externally_tagged_enum( let expecting = format!("enum {}", params.type_name()); let expecting = cattrs.expecting().unwrap_or(&expecting); - let (variants_stmt, variant_visitor) = prepare_enum_variant_enum(variants); - // Match arms to extract a variant from a string let variant_arms = variants .iter() + .filter(|variant| !variant.attrs.skip_deserializing()) .enumerate() - .filter(|&(_, variant)| !variant.attrs.skip_deserializing()) .map(|(i, variant)| { - let variant_name = field_i(i); - let block = Match(deserialize_externally_tagged_variant( params, variant, cattrs, )); quote! { - (__Field::#variant_name, __variant) => #block + (#i, __variant) => #block } }); - let all_skipped = variants + let seed = match variants .iter() - .all(|variant| variant.attrs.skip_deserializing()); - let match_variant = if all_skipped { - // This is an empty enum like `enum Impossible {}` or an enum in which - // all variants have `#[serde(skip_deserializing)]`. - quote! { - // FIXME: Once feature(exhaustive_patterns) is stable: - // let _serde::__private::Err(__err) = _serde::de::EnumAccess::variant::<__Field>(__data); - // _serde::__private::Err(__err) - _serde::__private::Result::map( - _serde::de::EnumAccess::variant::<__Field>(__data), - |(__impossible, _)| match __impossible {}) - } - } else { - quote! { - match _serde::de::EnumAccess::variant(__data)? { - #(#variant_arms)* - } + .filter(|variant| !variant.attrs.skip_deserializing()) + .position(|variant| variant.attrs.other()) + { + Some(other) => { + quote!(_serde::__private::de::VariantOtherSeed::new(VARIANT_ALIASES, #other)) } + None => quote!(_serde::__private::de::VariantSeed::new( + VARIANT_ALIASES, + VARIANTS + )), }; - quote_block! { - #variant_visitor + let variant_names = variants + .iter() + .filter(|variant| !variant.attrs.skip_deserializing()) + .map(|variant| variant.attrs.name().deserialize_name()); + let aliases = variants.iter().filter_map(|variant| { + if variant.attrs.skip_deserializing() { + None + } else { + let aliases = variant.attrs.aliases(); + Some(quote!(&[ #(#aliases),* ])) + } + }); + + quote_block! { #[doc(hidden)] struct __Visitor #de_impl_generics #where_clause { marker: _serde::__private::PhantomData<#this_type #ty_generics>, @@ -1328,11 +1328,17 @@ fn deserialize_externally_tagged_enum( where __A: _serde::de::EnumAccess<#delife>, { - #match_variant + match _serde::de::EnumAccess::variant_seed(__data, #seed)? { + #(#variant_arms)* + _ => unreachable!(), + } } } - #variants_stmt + #[doc(hidden)] + const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ]; + #[doc(hidden)] + const VARIANT_ALIASES: &[&[&str]] = &[ #(#aliases),* ]; _serde::Deserializer::deserialize_enum( __deserializer,