Skip to content

Commit 237eb7b

Browse files
authored
chore: improve replace source perf (#92)
* chore: name_index_mapping should use array * fix: lazy new WithIndices * fix: mappings_size_hint not good * chore: use resize to fill * perf: out variable is unnecessary * perf: nums max capacity is 5 * chore: make struct field private
1 parent 98580d8 commit 237eb7b

File tree

2 files changed

+58
-63
lines changed

2 files changed

+58
-63
lines changed

src/helpers.rs

+45-51
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ pub fn get_map<S: StreamChunks>(
2222
stream: &S,
2323
options: &MapOptions,
2424
) -> Option<SourceMap> {
25-
let mut mappings = Vec::with_capacity(stream.mappings_size_hint());
25+
let mut mappings = Vec::new();
2626
let mut sources: Vec<Cow<'static, str>> = Vec::new();
2727
let mut sources_content: Vec<Cow<'static, str>> = Vec::new();
2828
let mut names: Vec<Cow<'static, str>> = Vec::new();
@@ -38,25 +38,22 @@ pub fn get_map<S: StreamChunks>(
3838
// on_source
3939
&mut |source_index, source: &str, source_content: Option<&str>| {
4040
let source_index = source_index as usize;
41-
sources.reserve(source_index - sources.len() + 1);
42-
while sources.len() <= source_index {
43-
sources.push("".into());
41+
if sources.len() <= source_index {
42+
sources.resize(source_index + 1, Cow::Borrowed(""));
4443
}
4544
sources[source_index] = source.to_string().into();
4645
if let Some(source_content) = source_content {
47-
sources.reserve(source_index - sources_content.len() + 1);
48-
while sources_content.len() <= source_index {
49-
sources_content.push("".into());
46+
if sources_content.len() <= source_index {
47+
sources_content.resize(source_index + 1, Cow::Borrowed(""));
5048
}
5149
sources_content[source_index] = source_content.to_string().into();
5250
}
5351
},
5452
// on_name
5553
&mut |name_index, name: &str| {
5654
let name_index = name_index as usize;
57-
names.reserve(name_index - names.len() + 1);
58-
while names.len() <= name_index {
59-
names.push("".into());
55+
if names.len() <= name_index {
56+
names.resize(name_index + 1, Cow::Borrowed(""));
6057
}
6158
names[name_index] = name.to_string().into();
6259
},
@@ -68,11 +65,6 @@ pub fn get_map<S: StreamChunks>(
6865

6966
/// [StreamChunks] abstraction, see [webpack-sources source.streamChunks](https://github.com/webpack/webpack-sources/blob/9f98066311d53a153fdc7c633422a1d086528027/lib/helpers/streamChunks.js#L13).
7067
pub trait StreamChunks {
71-
/// Estimate the number of mappings in the chunk
72-
fn mappings_size_hint(&self) -> usize {
73-
0
74-
}
75-
7668
/// [StreamChunks] abstraction
7769
fn stream_chunks(
7870
&self,
@@ -132,34 +124,38 @@ pub struct GeneratedInfo {
132124
pub fn decode_mappings<'b, 'a: 'b>(
133125
source_map: &'a SourceMap,
134126
) -> impl Iterator<Item = Mapping> + 'b {
135-
SegmentIter {
136-
line: "",
137-
mapping_str: source_map.mappings(),
138-
source_index: 0,
139-
original_line: 1,
140-
original_column: 0,
141-
name_index: 0,
142-
generated_line: 0,
143-
segment_cursor: 0,
144-
generated_column: 0,
145-
nums: Vec::with_capacity(6),
146-
}
127+
SegmentIter::new(source_map.mappings())
147128
}
148129

149130
pub struct SegmentIter<'a> {
150-
pub mapping_str: &'a str,
151-
pub generated_line: usize,
152-
pub generated_column: u32,
153-
pub source_index: u32,
154-
pub original_line: u32,
155-
pub original_column: u32,
156-
pub name_index: u32,
157-
pub line: &'a str,
158-
pub nums: Vec<i64>,
159-
pub segment_cursor: usize,
131+
mapping_str: &'a str,
132+
generated_line: usize,
133+
generated_column: u32,
134+
source_index: u32,
135+
original_line: u32,
136+
original_column: u32,
137+
name_index: u32,
138+
line: &'a str,
139+
nums: Vec<i64>,
140+
segment_cursor: usize,
160141
}
161142

162143
impl<'a> SegmentIter<'a> {
144+
pub fn new(mapping_str: &'a str) -> Self {
145+
SegmentIter {
146+
line: "",
147+
mapping_str,
148+
source_index: 0,
149+
original_line: 1,
150+
original_column: 0,
151+
name_index: 0,
152+
generated_line: 0,
153+
segment_cursor: 0,
154+
generated_column: 0,
155+
nums: Vec::with_capacity(5),
156+
}
157+
}
158+
163159
fn next_segment(&mut self) -> Option<&'a str> {
164160
if self.line.is_empty() {
165161
loop {
@@ -279,10 +275,9 @@ fn encode_full_mappings(mappings: &[Mapping]) -> String {
279275
let mut active_name = false;
280276
let mut initial = true;
281277

282-
let mut out = String::new();
283278
mappings.iter().fold(
284-
String::with_capacity(mappings.len() * 4),
285-
|acc, mapping| {
279+
String::with_capacity(mappings.len() * 6),
280+
|mut acc, mapping| {
286281
if active_mapping && current_line == mapping.generated_line {
287282
// A mapping is still active
288283
if mapping.original.is_some_and(|original| {
@@ -303,38 +298,37 @@ fn encode_full_mappings(mappings: &[Mapping]) -> String {
303298
}
304299
}
305300

306-
out.clear();
307301
if current_line < mapping.generated_line {
308-
(0..mapping.generated_line - current_line).for_each(|_| out.push(';'));
302+
(0..mapping.generated_line - current_line).for_each(|_| acc.push(';'));
309303
current_line = mapping.generated_line;
310304
current_column = 0;
311305
initial = false;
312306
} else if initial {
313307
initial = false;
314308
} else {
315-
out.push(',');
309+
acc.push(',');
316310
}
317311

318-
encode(&mut out, mapping.generated_column, current_column);
312+
encode(&mut acc, mapping.generated_column, current_column);
319313
current_column = mapping.generated_column;
320314
if let Some(original) = &mapping.original {
321315
active_mapping = true;
322316
if original.source_index == current_source_index {
323-
out.push('A');
317+
acc.push('A');
324318
} else {
325-
encode(&mut out, original.source_index, current_source_index);
319+
encode(&mut acc, original.source_index, current_source_index);
326320
current_source_index = original.source_index;
327321
}
328-
encode(&mut out, original.original_line, current_original_line);
322+
encode(&mut acc, original.original_line, current_original_line);
329323
current_original_line = original.original_line;
330324
if original.original_column == current_original_column {
331-
out.push('A');
325+
acc.push('A');
332326
} else {
333-
encode(&mut out, original.original_column, current_original_column);
327+
encode(&mut acc, original.original_column, current_original_column);
334328
current_original_column = original.original_column;
335329
}
336330
if let Some(name_index) = original.name_index {
337-
encode(&mut out, name_index, current_name_index);
331+
encode(&mut acc, name_index, current_name_index);
338332
current_name_index = name_index;
339333
active_name = true;
340334
} else {
@@ -343,7 +337,7 @@ fn encode_full_mappings(mappings: &[Mapping]) -> String {
343337
} else {
344338
active_mapping = false;
345339
}
346-
acc + &out
340+
acc
347341
},
348342
)
349343
}

src/replace_source.rs

+13-12
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,6 @@ impl<T: std::fmt::Debug> std::fmt::Debug for ReplaceSource<T> {
223223
}
224224

225225
impl<T: Source> StreamChunks for ReplaceSource<T> {
226-
fn mappings_size_hint(&self) -> usize {
227-
self.replacements.lock().unwrap().len() * 2
228-
}
229-
230226
fn stream_chunks(
231227
&self,
232228
options: &crate::MapOptions,
@@ -244,12 +240,11 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
244240
let mut generated_line_offset: i64 = 0;
245241
let mut generated_column_offset: i64 = 0;
246242
let mut generated_column_offset_line = 0;
247-
let source_content_lines: RefCell<Vec<Option<Vec<WithIndices<String>>>>> =
243+
let source_content_lines: RefCell<Vec<Option<Vec<String>>>> =
248244
RefCell::new(Vec::new());
249245
let name_mapping: RefCell<HashMap<String, u32>> =
250246
RefCell::new(HashMap::default());
251-
let name_index_mapping: RefCell<HashMap<u32, u32>> =
252-
RefCell::new(HashMap::default());
247+
let name_index_mapping: RefCell<Vec<u32>> = RefCell::new(Vec::new());
253248

254249
// check if source_content[line][col] is equal to expect
255250
// Why this is needed?
@@ -283,7 +278,7 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
283278
source_content_lines.borrow().get(source_index as usize)
284279
{
285280
if let Some(content_line) = content_lines.get(line as usize - 1) {
286-
content_line.substring(
281+
WithIndices::new(content_line).substring(
287282
column as usize,
288283
column as usize + expected_chunk.len(),
289284
) == expected_chunk
@@ -376,7 +371,10 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
376371
original_line: original.original_line,
377372
original_column: original.original_column,
378373
name_index: original.name_index.and_then(|name_index| {
379-
name_index_mapping.borrow().get(&name_index).copied()
374+
name_index_mapping
375+
.borrow()
376+
.get(name_index as usize)
377+
.copied()
380378
}),
381379
}
382380
}),
@@ -552,7 +550,10 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
552550
original_line: original.original_line,
553551
original_column: original.original_column,
554552
name_index: original.name_index.and_then(|name_index| {
555-
name_index_mapping.borrow().get(&name_index).copied()
553+
name_index_mapping
554+
.borrow()
555+
.get(name_index as usize)
556+
.copied()
556557
}),
557558
}
558559
}),
@@ -570,7 +571,7 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
570571
source_content.map(|source_content| {
571572
split_into_lines(source_content)
572573
.into_iter()
573-
.map(|line| WithIndices::new(line.into()))
574+
.map(|line| line.to_string())
574575
.collect()
575576
});
576577
on_source(source_index, source, source_content);
@@ -586,7 +587,7 @@ impl<T: Source> StreamChunks for ReplaceSource<T> {
586587
}
587588
name_index_mapping
588589
.borrow_mut()
589-
.insert(name_index, global_index.unwrap());
590+
.insert(name_index as usize, global_index.unwrap());
590591
},
591592
);
592593

0 commit comments

Comments
 (0)