diff --git a/src/cached_source.rs b/src/cached_source.rs index 321c4774..2d08bbf2 100644 --- a/src/cached_source.rs +++ b/src/cached_source.rs @@ -177,8 +177,8 @@ mod tests { source_map: SourceMap::new( None, ";AACA".to_string(), - vec!["index.js".to_string()], - vec!["// DELETE IT\nconsole.log(1)".to_string()], + vec!["index.js".into()], + vec!["// DELETE IT\nconsole.log(1)".into()], vec![], ), }) diff --git a/src/helpers.rs b/src/helpers.rs index 65c69acb..bcff68a7 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,5 +1,9 @@ use rustc_hash::FxHashMap as HashMap; -use std::{borrow::BorrowMut, cell::RefCell, sync::Arc}; +use std::{ + borrow::{BorrowMut, Cow}, + cell::RefCell, + sync::Arc, +}; use crate::{ source::{Mapping, OriginalLocation}, @@ -17,37 +21,43 @@ pub fn get_map( stream: &S, options: &MapOptions, ) -> Option { - let mut mappings = Vec::new(); - let mut sources = Vec::new(); - let mut sources_content = Vec::new(); - let mut names = Vec::new(); + let mut mappings = Vec::with_capacity(stream.mappings_size_hint()); + let mut sources: Vec> = Vec::new(); + let mut sources_content: Vec> = Vec::new(); + let mut names: Vec> = Vec::new(); stream.stream_chunks( &MapOptions { columns: options.columns, final_source: true, }, + // on_chunk &mut |_, mapping| { mappings.push(mapping); }, + // on_source &mut |source_index, source: &str, source_content: Option<&str>| { let source_index = source_index as usize; + sources.reserve(source_index - sources.len() + 1); while sources.len() <= source_index { - sources.push("".to_string()); + sources.push("".into()); } - sources[source_index] = source.to_owned(); + sources[source_index] = source.to_string().into(); if let Some(source_content) = source_content { + sources.reserve(source_index - sources_content.len() + 1); while sources_content.len() <= source_index { - sources_content.push("".to_string()); + sources_content.push("".into()); } - sources_content[source_index] = source_content.to_owned(); + sources_content[source_index] = source_content.to_string().into(); } }, + // on_name &mut |name_index, name: &str| { let name_index = name_index as usize; + names.reserve(name_index - names.len() + 1); while names.len() <= name_index { - names.push("".to_string()); + names.push("".into()); } - names[name_index] = name.to_owned(); + names[name_index] = name.to_string().into(); }, ); let mappings = encode_mappings(&mappings, options); @@ -57,6 +67,11 @@ pub fn get_map( /// [StreamChunks] abstraction, see [webpack-sources source.streamChunks](https://github.com/webpack/webpack-sources/blob/9f98066311d53a153fdc7c633422a1d086528027/lib/helpers/streamChunks.js#L13). pub trait StreamChunks { + /// Estimate the number of mappings in the chunk + fn mappings_size_hint(&self) -> usize { + 0 + } + /// [StreamChunks] abstraction fn stream_chunks( &self, @@ -1302,16 +1317,25 @@ pub fn stream_chunks_of_combined_source_map( &mut |chunk, mapping| { let mut inner_source_map_line_data = inner_source_map_line_data.borrow_mut(); - while inner_source_map_line_data.len() - <= mapping.generated_line as usize - { - inner_source_map_line_data.push(SourceMapLineData { - mappings_data: Default::default(), - chunks: vec![], - }); + let inner_source_map_line_data_len = + inner_source_map_line_data.len(); + let mapping_generated_line_len = mapping.generated_line as usize; + if inner_source_map_line_data_len <= mapping_generated_line_len { + inner_source_map_line_data.reserve( + mapping_generated_line_len - inner_source_map_line_data_len + 1, + ); + while inner_source_map_line_data.len() + <= mapping_generated_line_len + { + inner_source_map_line_data.push(SourceMapLineData { + mappings_data: Default::default(), + chunks: vec![], + }); + } } let data = &mut inner_source_map_line_data [mapping.generated_line as usize - 1]; + data.mappings_data.reserve(5); data.mappings_data.push(mapping.generated_column as i64); data.mappings_data.push( mapping diff --git a/src/replace_source.rs b/src/replace_source.rs index 20d1bd11..f7d466b0 100644 --- a/src/replace_source.rs +++ b/src/replace_source.rs @@ -189,6 +189,10 @@ impl Source for ReplaceSource { } impl StreamChunks for ReplaceSource { + fn mappings_size_hint(&self) -> usize { + self.replacements.lock().len() * 2 + } + fn stream_chunks( &self, options: &crate::MapOptions, diff --git a/src/source.rs b/src/source.rs index c644fe62..c7a901dc 100644 --- a/src/source.rs +++ b/src/source.rs @@ -175,9 +175,9 @@ impl MapOptions { pub struct SourceMap { file: Option, mappings: String, - sources: Vec, - sources_content: Vec, - names: Vec, + sources: Vec>, + sources_content: Vec>, + names: Vec>, } impl SourceMap { @@ -185,16 +185,16 @@ impl SourceMap { pub fn new( file: Option, mappings: String, - sources: impl IntoIterator, - sources_content: impl IntoIterator, - names: impl IntoIterator, + sources: Vec>, + sources_content: Vec>, + names: Vec>, ) -> Self { Self { file, mappings, - sources: sources.into_iter().collect(), - sources_content: sources_content.into_iter().collect(), - names: names.into_iter().collect(), + sources, + sources_content, + names, } } @@ -219,63 +219,72 @@ impl SourceMap { } /// Get the sources field in [SourceMap]. - pub fn sources(&self) -> &[String] { + pub fn sources(&self) -> &[Cow<'static, str>] { &self.sources } /// Get the mutable sources field in [SourceMap]. - pub fn sources_mut(&mut self) -> &mut [String] { + pub fn sources_mut(&mut self) -> &mut [Cow<'static, str>] { &mut self.sources } /// Get the source by index from sources field in [SourceMap]. pub fn get_source(&self, index: usize) -> Option<&str> { - self.sources.get(index).map(|s| s.as_str()) + self.sources.get(index).map(|s| s.as_ref()) } /// Get the mutable source by index from sources field in [SourceMap]. - pub fn get_source_mut(&mut self, index: usize) -> Option<&mut str> { - self.sources.get_mut(index).map(|s| s.as_mut_str()) + pub fn get_source_mut( + &mut self, + index: usize, + ) -> Option<&mut Cow<'static, str>> { + self.sources.get_mut(index) } /// Get the sourcesContent field in [SourceMap]. - pub fn sources_content(&self) -> &[String] { + pub fn sources_content(&self) -> &[Cow<'static, str>] { &self.sources_content } /// Get the mutable sourcesContent field in [SourceMap]. - pub fn sources_content_mut(&mut self) -> &mut [String] { + pub fn sources_content_mut(&mut self) -> &mut [Cow<'static, str>] { &mut self.sources_content } /// Get the source content by index from sourcesContent field in [SourceMap]. pub fn get_source_content(&self, index: usize) -> Option<&str> { - self.sources_content.get(index).map(|s| s.as_str()) + self.sources_content.get(index).map(|s| s.as_ref()) } /// Get the mutable source content by index from sourcesContent field in [SourceMap]. - pub fn get_source_content_mut(&mut self, index: usize) -> Option<&mut str> { - self.sources_content.get_mut(index).map(|s| s.as_mut_str()) + pub fn get_source_content_mut( + &mut self, + index: usize, + ) -> Option<&mut Cow<'static, str>> { + self.sources_content.get_mut(index) } /// Get the names field in [SourceMap]. - pub fn names(&self) -> &[String] { + pub fn names(&self) -> &[Cow<'static, str>] { &self.names } /// Get the names field in [SourceMap]. - pub fn names_mut(&mut self) -> &mut [String] { + pub fn names_mut(&mut self) -> &mut [Cow<'static, str>] { &mut self.names } /// Get the name by index from names field in [SourceMap]. pub fn get_name(&self, index: usize) -> Option<&str> { - self.names.get(index).map(|s| s.as_str()) + self.names.get(index).map(|s| s.as_ref()) } /// Get the mutable name by index from names field in [SourceMap]. - pub fn get_name_mut(&mut self, index: usize) -> Option<&mut str> { - self.names.get_mut(index).map(|s| s.as_mut_str()) + pub fn get_name_mut( + &mut self, + index: usize, + ) -> Option<&mut Cow<'static, str>> { + self.names.get_mut(index) } } @@ -284,13 +293,13 @@ struct RawSourceMap { pub version: Option, #[serde(skip_serializing_if = "Option::is_none")] pub file: Option, - pub sources: Option>>, + pub sources: Option>>>, #[serde(rename = "sourceRoot", skip_serializing_if = "Option::is_none")] pub source_root: Option, #[serde(rename = "sourcesContent", skip_serializing_if = "Option::is_none")] - pub sources_content: Option>>, + pub sources_content: Option>>>, #[serde(skip_serializing_if = "Option::is_none")] - pub names: Option>>, + pub names: Option>>>, #[serde(skip_serializing_if = "Option::is_none")] pub mappings: Option, } @@ -370,24 +379,30 @@ impl TryFrom for SourceMap { if is_valid { x } else { - format!("{source_root}/{x}") + format!("{source_root}/{x}").into() } }) .collect() } - _ => sources.into_iter().map(Option::unwrap_or_default).collect(), + _ => sources + .into_iter() + .map(Option::unwrap_or_default) + .map(Cow::from) + .collect(), }; let sources_content = raw .sources_content .unwrap_or_default() .into_iter() .map(|v| v.unwrap_or_default()) + .map(Cow::from) .collect(); let names = raw .names .unwrap_or_default() .into_iter() .map(|v| v.unwrap_or_default()) + .map(Cow::from) .collect(); Ok(Self { file: raw.file, @@ -506,9 +521,9 @@ mod tests { let map = SourceMap::new( None, ";;".to_string(), - ["a.js".to_string()], - ["".to_string(), "".to_string(), "".to_string()], - ["".to_string(), "".to_string()], + vec!["a.js".into()], + vec!["".into(), "".into(), "".into()], + vec!["".into(), "".into()], ) .to_json() .unwrap(); diff --git a/src/source_map_source.rs b/src/source_map_source.rs index a18493ef..a892d3b0 100644 --- a/src/source_map_source.rs +++ b/src/source_map_source.rs @@ -260,15 +260,21 @@ mod tests { source_map: SourceMap::new( None, "AAAA".to_string(), - ["".to_string()], - ["".to_string()], - [], + vec!["".into()], + vec!["".into()], + vec![], ), }); let b = SourceMapSource::new(WithoutOriginalOptions { value: "hello world\n", name: "hello.txt", - source_map: SourceMap::new(None, "AAAA".to_string(), [], [], []), + source_map: SourceMap::new( + None, + "AAAA".to_string(), + vec![], + vec![], + vec![], + ), }); let c = SourceMapSource::new(WithoutOriginalOptions { value: "hello world\n", @@ -276,9 +282,9 @@ mod tests { source_map: SourceMap::new( None, "AAAA".to_string(), - ["hello-source.txt".to_string()], - ["hello world\n".to_string()], - [], + vec!["hello-source.txt".into()], + vec!["hello world\n".into()], + vec![], ), }); let sources = [a, b, c].into_iter().map(|s| { @@ -344,9 +350,9 @@ mod tests { source_map: SourceMap::new( None, "AAAA;AACA".to_string(), - ["hello1".to_string()], - [], - [], + vec!["hello1".into()], + vec![], + vec![], ), }); let b = SourceMapSource::new(WithoutOriginalOptions { @@ -355,9 +361,9 @@ mod tests { source_map: SourceMap::new( None, "AAAA,EAAE".to_string(), - ["hello2".to_string()], - [], - [], + vec!["hello2".into()], + vec![], + vec![], ), }); let b2 = SourceMapSource::new(WithoutOriginalOptions { @@ -366,9 +372,9 @@ mod tests { source_map: SourceMap::new( None, "AAAA,EAAE".to_string(), - ["hello3".to_string()], - [], - [], + vec!["hello3".into()], + vec![], + vec![], ), }); let c = SourceMapSource::new(WithoutOriginalOptions { @@ -377,9 +383,9 @@ mod tests { source_map: SourceMap::new( None, "AAAA".to_string(), - ["hello4".to_string()], - [], - [], + vec!["hello4".into()], + vec![], + vec![], ), }); let source = ConcatSource::new([ diff --git a/src/with_indices.rs b/src/with_indices.rs index 974e2e70..bd37556d 100644 --- a/src/with_indices.rs +++ b/src/with_indices.rs @@ -34,10 +34,8 @@ impl> WithIndices { }); let str_len = self.line.as_ref().len() as u32; - let start = - indices_indexes.get(start_index).unwrap_or(&str_len).clone() as usize; - let end = - indices_indexes.get(end_index).unwrap_or(&str_len).clone() as usize; + let start = *indices_indexes.get(start_index).unwrap_or(&str_len) as usize; + let end = *indices_indexes.get(end_index).unwrap_or(&str_len) as usize; unsafe { // SAFETY: Since `indices` iterates over the `CharIndices` of `self`, we can guarantee // that the indices obtained from it will always be within the bounds of `self` and they