mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
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:
1 parent
1d3ef102fe
commit
1c393c0289
4 files changed
+23
-3
No files matched your search
Generated
+1
@@ -2081,6 +2081,7 @@ name = "oxc_formatter_core"
|
||||
version = "0.73.0"
|
||||
dependencies = [
|
||||
"cow-utils",
|
||||
"memchr",
|
||||
"oxc_allocator",
|
||||
"oxc_data_structures",
|
||||
"oxc_span",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user