Skip to content

Commit 456c198

Browse files
committed
Avoid reallocations for reading qualified names
Qualified names accumulate into buf, but drop their scratch space after every completion of reading a qualified name. At the end of reading a qualified name, the scratch space only needs to be read by reference. The allocation of the value returned to the API client happens in OwnedName. By using a separate scratch space `qualified_name_buf`, we can operate on the same scratch space string for each time reading a qualified name. Results in a 2.8% speedup in a local measurement against a test similar to [1]. [1] https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/perf_tests/parser/xml-parser.html;l=8?q=xml-parser.html
1 parent ee2d4db commit 456c198

1 file changed

Lines changed: 13 additions & 10 deletions

File tree

src/reader/parser.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub(crate) struct PullParser {
7070
st: State,
7171
state_after_reference: State,
7272
buf: String,
73+
qualified_name_buf: String,
7374

7475
/// From DTD internal subset
7576
entities: HashMap<String, String>,
@@ -122,6 +123,7 @@ impl PullParser {
122123
st: State::DocumentStart,
123124
state_after_reference: State::OutsideTag,
124125
buf: String::with_capacity(STRING_RESERVE_CAPACITY),
126+
qualified_name_buf: String::with_capacity(STRING_RESERVE_CAPACITY),
125127
entities: HashMap::new(),
126128
nst: NamespaceStack::default(),
127129

@@ -543,28 +545,29 @@ impl PullParser {
543545
where F: Fn(&mut Self, Token, OwnedName) -> Option<Result> {
544546

545547
let try_consume_name = move |this: &mut Self, t| {
546-
let name = this.take_buf();
547548
this.seen_prefix_separator = false;
548-
match name.parse() {
549+
let result = match this.qualified_name_buf.parse() {
549550
Ok(name) => on_name(this, t, name),
550-
Err(()) => Some(this.error(SyntaxError::InvalidQualifiedName(name.into()))),
551-
}
551+
Err(()) => Some(this.error(SyntaxError::InvalidQualifiedName(this.qualified_name_buf.clone().into()))),
552+
};
553+
this.qualified_name_buf.clear();
554+
result
552555
};
553556

554557
match t {
555558
// There can be only one colon, and not as the first character
556-
Token::Character(':') if self.buf_has_data() && !self.seen_prefix_separator => {
557-
self.buf.push(':');
559+
Token::Character(':') if !self.qualified_name_buf.is_empty() && !self.seen_prefix_separator => {
560+
self.qualified_name_buf.push(':');
558561
self.seen_prefix_separator = true;
559562
None
560563
},
561564

562-
Token::Character(c) if c != ':' && (self.buf.is_empty() && is_name_start_char(c) ||
563-
self.buf_has_data() && is_name_char(c)) => {
564-
if self.buf.len() > self.config.max_name_length {
565+
Token::Character(c) if c != ':' && (self.qualified_name_buf.is_empty() && is_name_start_char(c) ||
566+
!self.qualified_name_buf.is_empty() && is_name_char(c)) => {
567+
if self.qualified_name_buf.len() > self.config.max_name_length {
565568
return Some(self.error(SyntaxError::ExceededConfiguredLimit));
566569
}
567-
self.buf.push(c);
570+
self.qualified_name_buf.push(c);
568571
None
569572
},
570573

0 commit comments

Comments
 (0)