From 4b90da471370a85239456b6341d0ae0e3cf054fa Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sun, 29 Dec 2024 18:10:12 -0500 Subject: [PATCH] perf: pre-allocate text change string capacity (#289) --- Cargo.lock | 14 ++++++++++-- Cargo.toml | 1 + src/text_changes.rs | 52 ++++++++++++++++++++++----------------------- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd9766a..dca2948 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,6 +237,7 @@ dependencies = [ "serde", "serde_json", "sourcemap", + "string_capacity", "swc_atoms", "swc_bundler", "swc_common", @@ -479,9 +480,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "lazy_static" @@ -963,6 +964,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_capacity" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d66c912ea6a5e9d1b1cc1be9c72325a5502f0b868578a7dd0fa97bb710626c2" +dependencies = [ + "itoa", +] + [[package]] name = "string_enum" version = "0.4.4" diff --git a/Cargo.toml b/Cargo.toml index a0daa2c..13925b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -77,6 +77,7 @@ swc_visit = { version = "=0.6.2", optional = true } swc_visit_macros = { version = "=0.5.13", optional = true } # just for error handling sourcemap = { version = "9.0.0", optional = true } +string_capacity = "0.1.4" thiserror = "2" [dev-dependencies] diff --git a/src/text_changes.rs b/src/text_changes.rs index 2b389f4..796bbbd 100644 --- a/src/text_changes.rs +++ b/src/text_changes.rs @@ -3,6 +3,8 @@ use std::cmp::Ordering; use std::ops::Range; +use string_capacity::StringBuilder; + #[derive(Clone, Debug)] pub struct TextChange { /// Range start to end byte index. @@ -30,34 +32,32 @@ pub fn apply_text_changes( ordering => ordering, }); - let mut last_index = 0; - let mut final_text = String::new(); - - for (i, change) in changes.iter().enumerate() { - if change.range.start > change.range.end { - panic!( - "Text change had start index {} greater than end index {}.\n\n{:?}", - change.range.start, - change.range.end, - &changes[0..i + 1], - ) - } - if change.range.start < last_index { - panic!("Text changes were overlapping. Past index was {}, but new change had index {}.\n\n{:?}", last_index, change.range.start, &changes[0..i + 1]); - } else if change.range.start > last_index && last_index < source.len() { - final_text.push_str( - &source[last_index..std::cmp::min(source.len(), change.range.start)], - ); + StringBuilder::build(|builder| { + let mut last_index = 0; + for (i, change) in changes.iter().enumerate() { + if change.range.start > change.range.end { + panic!( + "Text change had start index {} greater than end index {}.\n\n{:?}", + change.range.start, + change.range.end, + &changes[0..i + 1], + ) + } + if change.range.start < last_index { + panic!("Text changes were overlapping. Past index was {}, but new change had index {}.\n\n{:?}", last_index, change.range.start, &changes[0..i + 1]); + } else if change.range.start > last_index && last_index < source.len() { + builder.append( + &source[last_index..std::cmp::min(source.len(), change.range.start)], + ); + } + builder.append(&change.new_text); + last_index = change.range.end; } - final_text.push_str(&change.new_text); - last_index = change.range.end; - } - if last_index < source.len() { - final_text.push_str(&source[last_index..]); - } - - final_text + if last_index < source.len() { + builder.append(&source[last_index..]); + } + }).unwrap() } #[cfg(test)]