Skip to content

Commit f3e8b1f

Browse files
authored
Merge pull request #681 from adamreichold/key-buf
RFC: Re-use a long-lived buffer for prefixing attribute names.
2 parents bbc7bda + efbb166 commit f3e8b1f

File tree

4 files changed

+60
-7
lines changed

4 files changed

+60
-7
lines changed

src/de/key.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,25 @@ pub struct QNameDeserializer<'i, 'd> {
7676

7777
impl<'i, 'd> QNameDeserializer<'i, 'd> {
7878
/// Creates deserializer from name of an attribute
79-
pub fn from_attr(name: QName<'d>, decoder: Decoder) -> Result<Self, DeError> {
79+
pub fn from_attr(
80+
name: QName<'d>,
81+
decoder: Decoder,
82+
key_buf: &'d mut String,
83+
) -> Result<Self, DeError> {
84+
key_buf.clear();
85+
key_buf.push('@');
86+
8087
// https://github.com/tafia/quick-xml/issues/537
8188
// Namespace bindings (xmlns:xxx) map to `@xmlns:xxx` instead of `@xxx`
82-
let field = if name.as_namespace_binding().is_some() {
83-
decoder.decode(name.into_inner())?
89+
if name.as_namespace_binding().is_some() {
90+
decoder.decode_into(name.into_inner(), key_buf)?;
8491
} else {
85-
decode_name(name, decoder)?
92+
let local = name.local_name();
93+
decoder.decode_into(local.into_inner(), key_buf)?;
8694
};
8795

8896
Ok(Self {
89-
name: CowRef::Owned(format!("@{field}")),
97+
name: CowRef::Slice(key_buf),
9098
})
9199
}
92100

src/de/map.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@ where
238238
let (key, value) = a.into();
239239
self.source = ValueSource::Attribute(value.unwrap_or_default());
240240

241-
let de = QNameDeserializer::from_attr(QName(&slice[key]), decoder)?;
241+
let de =
242+
QNameDeserializer::from_attr(QName(&slice[key]), decoder, &mut self.de.key_buf)?;
242243
seed.deserialize(de).map(Some)
243244
} else {
244245
// try getting from events (<key>value</key>)

src/de/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,6 +2376,9 @@ where
23762376

23772377
#[cfg(not(feature = "overlapped-lists"))]
23782378
peek: Option<DeEvent<'de>>,
2379+
2380+
/// Buffer to store attribute name as a field name exposed to serde consumers
2381+
key_buf: String,
23792382
}
23802383

23812384
impl<'de, R, E> Deserializer<'de, R, E>
@@ -2402,6 +2405,8 @@ where
24022405

24032406
#[cfg(not(feature = "overlapped-lists"))]
24042407
peek: None,
2408+
2409+
key_buf: String::new(),
24052410
}
24062411
}
24072412

src/encoding.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::borrow::Cow;
44

55
#[cfg(feature = "encoding")]
6-
use encoding_rs::{Encoding, UTF_16BE, UTF_16LE, UTF_8};
6+
use encoding_rs::{DecoderResult, Encoding, UTF_16BE, UTF_16LE, UTF_8};
77

88
#[cfg(feature = "encoding")]
99
use crate::Error;
@@ -88,6 +88,17 @@ impl Decoder {
8888

8989
decoded
9090
}
91+
92+
/// Like [`decode`][Self::decode] but using a pre-allocated buffer.
93+
pub fn decode_into(&self, bytes: &[u8], buf: &mut String) -> Result<()> {
94+
#[cfg(not(feature = "encoding"))]
95+
buf.push_str(std::str::from_utf8(bytes)?);
96+
97+
#[cfg(feature = "encoding")]
98+
decode_into(bytes, self.encoding, buf)?;
99+
100+
Ok(())
101+
}
91102
}
92103

93104
/// Decodes the provided bytes using the specified encoding.
@@ -100,6 +111,34 @@ pub fn decode<'b>(bytes: &'b [u8], encoding: &'static Encoding) -> Result<Cow<'b
100111
.ok_or(Error::NonDecodable(None))
101112
}
102113

114+
/// Like [`decode`] but using a pre-allocated buffer.
115+
#[cfg(feature = "encoding")]
116+
pub fn decode_into(bytes: &[u8], encoding: &'static Encoding, buf: &mut String) -> Result<()> {
117+
if encoding == UTF_8 {
118+
buf.push_str(std::str::from_utf8(bytes)?);
119+
return Ok(());
120+
}
121+
122+
let mut decoder = encoding.new_decoder_without_bom_handling();
123+
buf.reserve(
124+
decoder
125+
.max_utf8_buffer_length_without_replacement(bytes.len())
126+
// SAFETY: None can be returned only if required size will overflow usize,
127+
// but in that case String::reserve also panics
128+
.unwrap(),
129+
);
130+
let (result, read) = decoder.decode_to_string_without_replacement(bytes, buf, true);
131+
match result {
132+
DecoderResult::InputEmpty => {
133+
debug_assert_eq!(read, bytes.len());
134+
Ok(())
135+
}
136+
DecoderResult::Malformed(_, _) => Err(Error::NonDecodable(None)),
137+
// SAFETY: We allocate enough space above
138+
DecoderResult::OutputFull => unreachable!(),
139+
}
140+
}
141+
103142
/// Automatic encoding detection of XML files based using the
104143
/// [recommended algorithm](https://www.w3.org/TR/xml11/#sec-guessing).
105144
///

0 commit comments

Comments
 (0)