Skip to content

Commit ca1c09a

Browse files
authored
Merge pull request #662 from Mingun/followup-580
Fix incorrect test for 580 and get rid of allocations in hot path
2 parents c79ad58 + 9e8158c commit ca1c09a

File tree

6 files changed

+109
-95
lines changed

6 files changed

+109
-95
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ MSRV bumped to 1.56! Crate now uses Rust 2021 edition.
3737
- [#649]: Make features linkable and reference them in the docs.
3838
- [#619]: Allow to raise application errors in `ElementWriter::write_inner_content`
3939
(and newly added `ElementWriter::write_inner_content_async` of course).
40+
- [#662]: Get rid of some allocations during serde deserialization.
4041

4142
[#545]: https://github.com/tafia/quick-xml/pull/545
4243
[#580]: https://github.com/tafia/quick-xml/issues/580
@@ -47,6 +48,7 @@ MSRV bumped to 1.56! Crate now uses Rust 2021 edition.
4748
[#651]: https://github.com/tafia/quick-xml/pull/651
4849
[#660]: https://github.com/tafia/quick-xml/pull/660
4950
[#661]: https://github.com/tafia/quick-xml/pull/661
51+
[#662]: https://github.com/tafia/quick-xml/pull/662
5052

5153

5254
## 0.30.0 -- 2023-07-23

src/de/map.rs

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ use crate::{
1212
name::QName,
1313
};
1414
use serde::de::value::BorrowedStrDeserializer;
15-
use serde::de::{self, DeserializeSeed, SeqAccess, Visitor};
15+
use serde::de::{self, DeserializeSeed, MapAccess, SeqAccess, Visitor};
1616
use serde::serde_if_integer128;
1717
use std::borrow::Cow;
1818
use std::ops::Range;
1919

2020
/// Defines a source that should be used to deserialize a value in the next call
21-
/// to [`next_value_seed()`](de::MapAccess::next_value_seed)
21+
/// to [`next_value_seed()`](MapAccess::next_value_seed)
2222
#[derive(Debug, PartialEq)]
2323
enum ValueSource {
2424
/// Source are not specified, because [`next_key_seed()`] not yet called.
@@ -28,8 +28,8 @@ enum ValueSource {
2828
/// Attempt to call [`next_value_seed()`] while accessor in this state would
2929
/// return a [`DeError::KeyNotRead`] error.
3030
///
31-
/// [`next_key_seed()`]: de::MapAccess::next_key_seed
32-
/// [`next_value_seed()`]: de::MapAccess::next_value_seed
31+
/// [`next_key_seed()`]: MapAccess::next_key_seed
32+
/// [`next_value_seed()`]: MapAccess::next_value_seed
3333
Unknown,
3434
/// Next value should be deserialized from an attribute value; value is located
3535
/// at specified span.
@@ -62,7 +62,7 @@ enum ValueSource {
6262
/// When in this state, next event, returned by [`next()`], will be a [`Start`],
6363
/// which represents both a key, and a value. Value would be deserialized from
6464
/// the whole element and how is will be done determined by the value deserializer.
65-
/// The [`MapAccess`] do not consume any events in that state.
65+
/// The [`ElementMapAccess`] do not consume any events in that state.
6666
///
6767
/// Because in that state any encountered `<tag>` is mapped to the [`VALUE_KEY`]
6868
/// field, it is possible to use tag name as an enum discriminator, so `enum`s
@@ -105,7 +105,7 @@ enum ValueSource {
105105
/// [`next()`]: Deserializer::next()
106106
/// [`name()`]: BytesStart::name()
107107
/// [`Text`]: Self::Text
108-
/// [list of known fields]: MapAccess::fields
108+
/// [list of known fields]: ElementMapAccess::fields
109109
Content,
110110
/// Next value should be deserialized from an element with a dedicated name.
111111
/// If deserialized type is a sequence, then that sequence will collect all
@@ -118,7 +118,7 @@ enum ValueSource {
118118
/// When in this state, next event, returned by [`next()`], will be a [`Start`],
119119
/// which represents both a key, and a value. Value would be deserialized from
120120
/// the whole element and how is will be done determined by the value deserializer.
121-
/// The [`MapAccess`] do not consume any events in that state.
121+
/// The [`ElementMapAccess`] do not consume any events in that state.
122122
///
123123
/// An illustration below shows, what data is used to deserialize key and value:
124124
/// ```xml
@@ -164,16 +164,16 @@ enum ValueSource {
164164
/// internal buffer of deserializer (i.e. deserializer itself) or an input
165165
/// (in that case it is possible to approach zero-copy deserialization).
166166
///
167-
/// - `'a` lifetime represents a parent deserializer, which could own the data
167+
/// - `'d` lifetime represents a parent deserializer, which could own the data
168168
/// buffer.
169-
pub(crate) struct MapAccess<'de, 'a, R, E>
169+
pub(crate) struct ElementMapAccess<'de, 'd, R, E>
170170
where
171171
R: XmlRead<'de>,
172172
E: EntityResolver,
173173
{
174174
/// Tag -- owner of attributes
175175
start: BytesStart<'de>,
176-
de: &'a mut Deserializer<'de, R, E>,
176+
de: &'d mut Deserializer<'de, R, E>,
177177
/// State of the iterator over attributes. Contains the next position in the
178178
/// inner `start` slice, from which next attribute should be parsed.
179179
iter: IterState,
@@ -192,18 +192,18 @@ where
192192
has_value_field: bool,
193193
}
194194

195-
impl<'de, 'a, R, E> MapAccess<'de, 'a, R, E>
195+
impl<'de, 'd, R, E> ElementMapAccess<'de, 'd, R, E>
196196
where
197197
R: XmlRead<'de>,
198198
E: EntityResolver,
199199
{
200-
/// Create a new MapAccess
200+
/// Create a new ElementMapAccess
201201
pub fn new(
202-
de: &'a mut Deserializer<'de, R, E>,
202+
de: &'d mut Deserializer<'de, R, E>,
203203
start: BytesStart<'de>,
204204
fields: &'static [&'static str],
205205
) -> Result<Self, DeError> {
206-
Ok(MapAccess {
206+
Ok(Self {
207207
de,
208208
iter: IterState::new(start.name().as_ref().len(), false),
209209
start,
@@ -214,7 +214,7 @@ where
214214
}
215215
}
216216

217-
impl<'de, 'a, R, E> de::MapAccess<'de> for MapAccess<'de, 'a, R, E>
217+
impl<'de, 'd, R, E> MapAccess<'de> for ElementMapAccess<'de, 'd, R, E>
218218
where
219219
R: XmlRead<'de>,
220220
E: EntityResolver,
@@ -289,9 +289,14 @@ where
289289
seed.deserialize(de).map(Some)
290290
}
291291
// Stop iteration after reaching a closing tag
292-
DeEvent::End(e) if e.name() == self.start.name() => Ok(None),
293-
// This is a unmatched closing tag, so the XML is invalid
294-
DeEvent::End(e) => Err(DeError::UnexpectedEnd(e.name().as_ref().to_owned())),
292+
// The matching tag name is guaranteed by the reader if our
293+
// deserializer implementation is correct
294+
DeEvent::End(e) => {
295+
debug_assert_eq!(self.start.name(), e.name());
296+
// Consume End
297+
self.de.next()?;
298+
Ok(None)
299+
}
295300
// We cannot get `Eof` legally, because we always inside of the
296301
// opened tag `self.start`
297302
DeEvent::Eof => Err(DeError::UnexpectedEof),
@@ -403,7 +408,7 @@ macro_rules! forward {
403408
/// with the same deserializer;
404409
/// - sequences, tuples and tuple structs are deserialized by iterating within the
405410
/// parent tag and deserializing each tag or text content using [`SeqItemDeserializer`];
406-
/// - structs and maps are deserialized using new instance of [`MapAccess`];
411+
/// - structs and maps are deserialized using new instance of [`ElementMapAccess`];
407412
/// - enums:
408413
/// - in case of [`DeEvent::Text`] event the text content is deserialized as
409414
/// a `$text` variant. Enum content is deserialized from the text using
@@ -419,14 +424,14 @@ macro_rules! forward {
419424
///
420425
/// [`deserialize_tuple`]: #method.deserialize_tuple
421426
/// [`deserialize_struct`]: #method.deserialize_struct
422-
struct MapValueDeserializer<'de, 'a, 'm, R, E>
427+
struct MapValueDeserializer<'de, 'd, 'm, R, E>
423428
where
424429
R: XmlRead<'de>,
425430
E: EntityResolver,
426431
{
427432
/// Access to the map that created this deserializer. Gives access to the
428433
/// context, such as list of fields, that current map known about.
429-
map: &'m mut MapAccess<'de, 'a, R, E>,
434+
map: &'m mut ElementMapAccess<'de, 'd, R, E>,
430435
/// Determines, should [`Deserializer::read_string_impl()`] expand the second
431436
/// level of tags or not.
432437
///
@@ -504,7 +509,7 @@ where
504509
allow_start: bool,
505510
}
506511

507-
impl<'de, 'a, 'm, R, E> MapValueDeserializer<'de, 'a, 'm, R, E>
512+
impl<'de, 'd, 'm, R, E> MapValueDeserializer<'de, 'd, 'm, R, E>
508513
where
509514
R: XmlRead<'de>,
510515
E: EntityResolver,
@@ -520,7 +525,7 @@ where
520525
}
521526
}
522527

523-
impl<'de, 'a, 'm, R, E> de::Deserializer<'de> for MapValueDeserializer<'de, 'a, 'm, R, E>
528+
impl<'de, 'd, 'm, R, E> de::Deserializer<'de> for MapValueDeserializer<'de, 'd, 'm, R, E>
524529
where
525530
R: XmlRead<'de>,
526531
E: EntityResolver,
@@ -543,11 +548,14 @@ where
543548

544549
forward!(deserialize_any);
545550

546-
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, DeError>
551+
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
547552
where
548553
V: Visitor<'de>,
549554
{
550-
deserialize_option!(self.map.de, self, visitor)
555+
match self.map.de.peek()? {
556+
DeEvent::Text(t) if t.is_empty() => visitor.visit_none(),
557+
_ => visitor.visit_some(self),
558+
}
551559
}
552560

553561
/// Forwards deserialization of the inner type. Always calls [`Visitor::visit_newtype_struct`]
@@ -582,7 +590,7 @@ where
582590
// Clone is cheap if event borrows from the input
583591
DeEvent::Start(e) => TagFilter::Include(e.clone()),
584592
// SAFETY: we use that deserializer with `allow_start == true`
585-
// only from the `MapAccess::next_value_seed` and only when we
593+
// only from the `ElementMapAccess::next_value_seed` and only when we
586594
// peeked `Start` event
587595
_ => unreachable!(),
588596
}
@@ -597,11 +605,6 @@ where
597605
filter,
598606
})
599607
}
600-
601-
#[inline]
602-
fn is_human_readable(&self) -> bool {
603-
self.map.de.is_human_readable()
604-
}
605608
}
606609

607610
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -693,14 +696,14 @@ impl<'de> TagFilter<'de> {
693696
///
694697
/// [`Text`]: crate::events::Event::Text
695698
/// [`CData`]: crate::events::Event::CData
696-
struct MapValueSeqAccess<'de, 'a, 'm, R, E>
699+
struct MapValueSeqAccess<'de, 'd, 'm, R, E>
697700
where
698701
R: XmlRead<'de>,
699702
E: EntityResolver,
700703
{
701704
/// Accessor to a map that creates this accessor and to a deserializer for
702705
/// a sequence items.
703-
map: &'m mut MapAccess<'de, 'a, R, E>,
706+
map: &'m mut ElementMapAccess<'de, 'd, R, E>,
704707
/// Filter that determines whether a tag is a part of this sequence.
705708
///
706709
/// When feature [`overlapped-lists`] is not activated, iteration will stop
@@ -720,7 +723,7 @@ where
720723
}
721724

722725
#[cfg(feature = "overlapped-lists")]
723-
impl<'de, 'a, 'm, R, E> Drop for MapValueSeqAccess<'de, 'a, 'm, R, E>
726+
impl<'de, 'd, 'm, R, E> Drop for MapValueSeqAccess<'de, 'd, 'm, R, E>
724727
where
725728
R: XmlRead<'de>,
726729
E: EntityResolver,
@@ -730,7 +733,7 @@ where
730733
}
731734
}
732735

733-
impl<'de, 'a, 'm, R, E> SeqAccess<'de> for MapValueSeqAccess<'de, 'a, 'm, R, E>
736+
impl<'de, 'd, 'm, R, E> SeqAccess<'de> for MapValueSeqAccess<'de, 'd, 'm, R, E>
734737
where
735738
R: XmlRead<'de>,
736739
E: EntityResolver,
@@ -810,7 +813,7 @@ where
810813
/// contains something else other than text, an error is returned, but if it
811814
/// contains a text and something else (for example, `<item>text<tag/></item>`),
812815
/// then the trail is just ignored;
813-
/// - structs and maps are deserialized using new [`MapAccess`];
816+
/// - structs and maps are deserialized using new [`ElementMapAccess`];
814817
/// - enums:
815818
/// - in case of [`DeEvent::Text`] event the text content is deserialized as
816819
/// a `$text` variant. Enum content is deserialized from the text using
@@ -826,17 +829,17 @@ where
826829
///
827830
/// [`deserialize_tuple`]: #method.deserialize_tuple
828831
/// [`deserialize_struct`]: #method.deserialize_struct
829-
struct SeqItemDeserializer<'de, 'a, 'm, R, E>
832+
struct SeqItemDeserializer<'de, 'd, 'm, R, E>
830833
where
831834
R: XmlRead<'de>,
832835
E: EntityResolver,
833836
{
834837
/// Access to the map that created this deserializer. Gives access to the
835838
/// context, such as list of fields, that current map known about.
836-
map: &'m mut MapAccess<'de, 'a, R, E>,
839+
map: &'m mut ElementMapAccess<'de, 'd, R, E>,
837840
}
838841

839-
impl<'de, 'a, 'm, R, E> SeqItemDeserializer<'de, 'a, 'm, R, E>
842+
impl<'de, 'd, 'm, R, E> SeqItemDeserializer<'de, 'd, 'm, R, E>
840843
where
841844
R: XmlRead<'de>,
842845
E: EntityResolver,
@@ -852,7 +855,7 @@ where
852855
}
853856
}
854857

855-
impl<'de, 'a, 'm, R, E> de::Deserializer<'de> for SeqItemDeserializer<'de, 'a, 'm, R, E>
858+
impl<'de, 'd, 'm, R, E> de::Deserializer<'de> for SeqItemDeserializer<'de, 'd, 'm, R, E>
856859
where
857860
R: XmlRead<'de>,
858861
E: EntityResolver,
@@ -875,25 +878,27 @@ where
875878

876879
forward!(deserialize_any);
877880

878-
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, DeError>
881+
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
879882
where
880883
V: Visitor<'de>,
881884
{
882-
deserialize_option!(self.map.de, self, visitor)
885+
match self.map.de.peek()? {
886+
DeEvent::Text(t) if t.is_empty() => visitor.visit_none(),
887+
_ => visitor.visit_some(self),
888+
}
883889
}
884890

885891
/// Forwards deserialization of the inner type. Always calls [`Visitor::visit_newtype_struct`]
886-
/// with the [`SimpleTypeDeserializer`].
892+
/// with this deserializer.
887893
fn deserialize_newtype_struct<V>(
888-
mut self,
894+
self,
889895
_name: &'static str,
890896
visitor: V,
891897
) -> Result<V::Value, Self::Error>
892898
where
893899
V: Visitor<'de>,
894900
{
895-
let text = self.read_string()?;
896-
visitor.visit_newtype_struct(SimpleTypeDeserializer::from_text(text))
901+
visitor.visit_newtype_struct(self)
897902
}
898903

899904
/// This method deserializes a sequence inside of element that itself is a
@@ -915,11 +920,6 @@ where
915920
let text = self.read_string()?;
916921
SimpleTypeDeserializer::from_text(text).deserialize_seq(visitor)
917922
}
918-
919-
#[inline]
920-
fn is_human_readable(&self) -> bool {
921-
self.map.de.is_human_readable()
922-
}
923923
}
924924

925925
////////////////////////////////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)