perf(formatter_core): skip scanning texts with nothing to rewrite (#27561)

Most texts have no byte that `normalize_string` or `normalize_newlines` rewrites, so find the first candidate with `memchr` and return the text borrowed when there is none.

`is_all_printable_ascii` drops its early exit so that it vectorizes.

JSON benefited the most, and instructions tested locally showed a 12% reduction.
This commit is contained in:
leaysgur committed 2026-10-14 07:06:46 +00:00
1 parent 1d3ef102fe
commit 1c393c0289
4 files changed
+23 -3

No files matched your search

Generated
+1
View File
@@ -2081,6 +2081,7 @@ name = "oxc_formatter_core"
version = "0.73.0"
dependencies = [
"cow-utils",
"memchr",
"oxc_allocator",
"oxc_data_structures",
"oxc_span",
+1
View File
@@ -22,6 +22,7 @@ oxc_data_structures = { workspace = true, features = ["code_buffer"] }
oxc_span = { workspace = true }
cow-utils = { workspace = true }
memchr = { workspace = true }
rustc-hash = { workspace = true }
unicode-width = { workspace = true }
@@ -215,10 +215,23 @@ pub const LINE_TERMINATORS: [char; 3] = ['\r', LINE_SEPARATOR, PARAGRAPH_SEPARAT
/// Replace the line terminators matching the provided list with "\n"
/// since its the only line break type supported by the printer
pub fn normalize_newlines<const N: usize>(text: &str, terminators: [char; N]) -> Cow<'_, str> {
// Most texts have none: rule them out by the terminators' first UTF-8 bytes, all at once
const { assert!(N >= 1 && N <= 3) };
let first_bytes = terminators.map(|c| c.encode_utf8(&mut [0; 4]).as_bytes()[0]);
let first = if N == 1 {
memchr::memchr(first_bytes[0], text.as_bytes())
} else {
let byte = |i: usize| first_bytes[i.min(N - 1)];
memchr::memchr3(byte(0), byte(1), byte(2), text.as_bytes())
};
// A first UTF-8 byte starts a char, so the search can resume there
let Some(first) = first else { return Cow::Borrowed(text) };
let mut result = String::new();
let mut last_end = 0;
for (start, part) in text.match_indices(terminators) {
for (start, part) in text[first..].match_indices(terminators) {
let start = first + start;
result.push_str(&text[last_end..start]);
result.push('\n');
@@ -431,7 +444,8 @@ pub struct TextWidth(u32);
/// Every byte in `0x20..=0x7E` is a single-width ASCII character,
/// so display width equals byte length and the text is guaranteed single-line.
fn is_all_printable_ascii(s: &str) -> bool {
s.as_bytes().iter().all(|&b| matches!(b, 0x20..=0x7e))
// Without an early exit, so that it vectorizes
s.bytes().fold(true, |all, b| all & matches!(b, 0x20..=0x7e))
}
impl TextWidth {
+5 -1
View File
@@ -17,10 +17,14 @@ pub fn normalize_string(
preferred_quote: u8,
quotes_will_change: bool,
) -> Cow<'_, str> {
// Most strings have no byte to rewrite: find the first one all at once
let Some(first) = memchr::memchr3(b'\\', b'\r', preferred_quote, raw_content.as_bytes()) else {
return Cow::Borrowed(raw_content);
};
let alternate_quote = if preferred_quote == b'"' { b'\'' } else { b'"' };
let mut reduced_string = String::new();
let mut copy_start = 0;
let mut bytes = raw_content.bytes().enumerate().peekable();
let mut bytes = raw_content.bytes().enumerate().skip(first).peekable();
while let Some((byte_index, byte)) = bytes.next() {
match byte {
// If the next character is escaped