Skip to content

Commit 9c06266

Browse files
committed
perf: Use memchr for writing attributes
Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
1 parent d1aca74 commit 9c06266

1 file changed

Lines changed: 24 additions & 18 deletions

File tree

css-inline/src/html/serializer.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use super::{
66
};
77
use crate::{html::ElementStyleMap, parser, InlineError};
88
use html5ever::{local_name, ns, tendril::StrTendril, LocalName, QualName};
9-
use memchr::memchr_iter;
9+
use memchr::{memchr3_iter, memchr_iter};
1010
use smallvec::{smallvec, SmallVec};
1111
use std::io::Write;
1212

@@ -216,27 +216,33 @@ impl<'a, W: Write> HtmlSerializer<'a, W> {
216216
Ok(())
217217
}
218218

219+
#[allow(clippy::arithmetic_side_effects)]
219220
fn write_attributes(&mut self, text: &str) -> Result<(), InlineError> {
221+
let bytes = text.as_bytes();
220222
let mut last_end = 0;
221-
for (start, part) in text.match_indices(['&', '\u{00A0}', '"']) {
222-
self.writer.write_all(
223-
text.get(last_end..start)
224-
.expect("Invalid substring")
225-
.as_bytes(),
226-
)?;
227-
match part {
228-
"&" => self.writer.write_all(b"&amp;")?,
229-
"\u{00A0}" => self.writer.write_all(b"&nbsp;")?,
230-
"\"" => self.writer.write_all(b"&quot;")?,
231-
_ => unreachable!("Only the variants above are searched"),
223+
224+
// Scan for '&' (0x26), '"' (0x22), and 0xC2 (first byte of \u{00A0})
225+
for idx in memchr3_iter(b'&', b'"', 0xC2, bytes) {
226+
match bytes[idx] {
227+
b'&' => {
228+
self.writer.write_all(&bytes[last_end..idx])?;
229+
self.writer.write_all(b"&amp;")?;
230+
last_end = idx + 1;
231+
}
232+
b'"' => {
233+
self.writer.write_all(&bytes[last_end..idx])?;
234+
self.writer.write_all(b"&quot;")?;
235+
last_end = idx + 1;
236+
}
237+
0xC2 if bytes.get(idx + 1) == Some(&0xA0) => {
238+
self.writer.write_all(&bytes[last_end..idx])?;
239+
self.writer.write_all(b"&nbsp;")?;
240+
last_end = idx + 2; // Skip both bytes of \u{00A0}
241+
}
242+
_ => {} // False positive for 0xC2 not followed by 0xA0
232243
}
233-
last_end = start.checked_add(part.len()).expect("Size overflow");
234244
}
235-
self.writer.write_all(
236-
text.get(last_end..text.len())
237-
.expect("Invalid substring")
238-
.as_bytes(),
239-
)?;
245+
self.writer.write_all(&bytes[last_end..])?;
240246
Ok(())
241247
}
242248

0 commit comments

Comments
 (0)