diff --git a/src/helpers.rs b/src/helpers.rs index 05a1f91e..030b4640 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,10 +1,10 @@ -use arrayvec::ArrayVec; use std::{ borrow::{BorrowMut, Cow}, - cell::{OnceCell, RefCell}, + cell::RefCell, rc::Rc, }; +use memchr::Memchr2; use rustc_hash::FxHashMap as HashMap; use crate::{ @@ -13,7 +13,7 @@ use crate::{ source::{Mapping, OriginalLocation}, vlq::decode, with_indices::WithIndices, - MapOptions, SourceMap, + Error, MapOptions, SourceMap, }; // Adding this type because sourceContentLine not happy @@ -115,87 +115,86 @@ pub struct GeneratedInfo { pub generated_column: u32, } -pub fn decode_mappings<'b, 'a: 'b>( - source_map: &'a SourceMap, -) -> impl Iterator + 'b { +pub fn decode_mappings( + source_map: &SourceMap, +) -> impl Iterator + '_ { SegmentIter::new(source_map.mappings()) } pub struct SegmentIter<'a> { - mapping_str: &'a str, + mapping_str: &'a [u8], + mapping_iter: Memchr2<'a>, generated_line: usize, generated_column: u32, source_index: u32, original_line: u32, original_column: u32, name_index: u32, - line: &'a str, - nums: ArrayVec, - segment_cursor: usize, + tracing_index: usize, + tracing_newline: bool, } impl<'a> SegmentIter<'a> { pub fn new(mapping_str: &'a str) -> Self { + let mapping_str = mapping_str.as_bytes(); + let mapping_iter = memchr::memchr2_iter(b',', b';', mapping_str); SegmentIter { - line: "", mapping_str, + mapping_iter, source_index: 0, original_line: 1, original_column: 0, name_index: 0, - generated_line: 0, - segment_cursor: 0, + generated_line: 1, generated_column: 0, - nums: ArrayVec::new(), + tracing_index: 0, + tracing_newline: false, } } - fn next_segment(&mut self) -> Option<&'a str> { - if self.line.is_empty() { - loop { - match self.next_line() { - Some(line) => { - self.generated_line += 1; - if line.is_empty() { - continue; + fn next_segment(&mut self) -> Option<&'a [u8]> { + let mapping_str_len = self.mapping_str.len(); + + loop { + if self.tracing_newline { + self.generated_line += 1; + self.generated_column = 0; + self.tracing_newline = false; + } + + match self.mapping_iter.next() { + Some(index) => match self.mapping_str[index] { + b',' => { + if self.tracing_index != index { + let segment = &self.mapping_str[self.tracing_index..index]; + self.tracing_index = index + 1; + return Some(segment); } - self.line = line; - self.generated_column = 0; - self.segment_cursor = 0; - break; + self.tracing_index = index + 1; + } + b';' => { + self.tracing_newline = true; + if self.tracing_index != index { + let segment = &self.mapping_str[self.tracing_index..index]; + self.tracing_index = index + 1; + return Some(segment); + } + self.tracing_index = index + 1; + } + _ => unreachable!(), + }, + None => { + if self.tracing_index != mapping_str_len { + let segment = + &self.mapping_str[self.tracing_index..mapping_str_len]; + self.tracing_index = mapping_str_len; + return Some(segment); } - None => return None, } } - } - - if let Some(i) = - memchr::memchr(b',', self.line[self.segment_cursor..].as_bytes()) - { - let cursor = self.segment_cursor; - self.segment_cursor = self.segment_cursor + i + 1; - Some(&self.line[cursor..cursor + i]) - } else { - let line = self.line; - self.line = ""; - Some(&line[self.segment_cursor..]) - } - } - fn next_line(&mut self) -> Option<&'a str> { - if self.mapping_str.is_empty() { - return None; - } - match memchr::memchr(b';', self.mapping_str.as_bytes()) { - Some(i) => { - let temp_str = self.mapping_str; - self.mapping_str = &self.mapping_str[i + 1..]; - Some(&temp_str[..i]) - } - None => { - let tem_str = self.mapping_str; - self.mapping_str = ""; - Some(tem_str) + if self.tracing_index == mapping_str_len { + return None; } } } @@ -207,30 +206,38 @@ impl<'a> Iterator for SegmentIter<'a> { fn next(&mut self) -> Option { match self.next_segment() { Some(segment) => { - self.nums.clear(); - decode(segment, &mut self.nums).unwrap(); - self.generated_column = - (i64::from(self.generated_column) + self.nums[0]) as u32; + let mut vlq = decode(segment); + self.generated_column = (i64::from(self.generated_column) + + vlq + .next() + .unwrap_or_else(|| Err(Error::VlqNoValues)) + .unwrap()) as u32; let mut src = None; let mut name = None; - if self.nums.len() > 1 { - if self.nums.len() != 4 && self.nums.len() != 5 { - panic!("got {} segments, expected 4 or 5", self.nums.len()); - } + if let Some(source_index) = vlq.next() { + // if self.nums.len() != 4 && self.nums.len() != 5 { + // panic!("got {} segments, expected 4 or 5", self.nums.len()); + // } self.source_index = - (i64::from(self.source_index) + self.nums[1]) as u32; + (i64::from(self.source_index) + source_index.unwrap()) as u32; src = Some(self.source_index); - self.original_line = - (i64::from(self.original_line) + self.nums[2]) as u32; - self.original_column = - (i64::from(self.original_column) + self.nums[3]) as u32; - - if self.nums.len() > 4 { + self.original_line = (i64::from(self.original_line) + + vlq + .next() + .unwrap_or_else(|| Err(Error::VlqNoValues)) + .unwrap()) as u32; + self.original_column = (i64::from(self.original_column) + + vlq + .next() + .unwrap_or_else(|| Err(Error::VlqNoValues)) + .unwrap()) as u32; + + if let Some(name_index) = vlq.next() { self.name_index = - (i64::from(self.name_index) + self.nums[4]) as u32; - name = Some(self.name_index); + (i64::from(self.name_index) + name_index.unwrap()) as u32; + name = Some(self.name_index) } } @@ -466,20 +473,20 @@ fn stream_chunks_of_source_map_final<'a>( on_name(i as u32, Cow::Borrowed(name)); } let mut mapping_active_line = 0; - let mut on_mapping = |mapping: &Mapping| { + let mut on_mapping = |mapping: Mapping| { if mapping.generated_line >= result.generated_line && (mapping.generated_column >= result.generated_column || mapping.generated_line > result.generated_line) { return; } - if let Some(original) = &mapping.original { + if let Some(original) = mapping.original { on_chunk( None, Mapping { generated_line: mapping.generated_line, generated_column: mapping.generated_column, - original: Some(*original), + original: Some(original), }, ); mapping_active_line = mapping.generated_line; @@ -527,11 +534,11 @@ fn stream_chunks_of_source_map_full<'a>( let mut tracking_generated_column: u32 = 0; let mut tracking_mapping_original: Option = None; - let mut mappings_iter = source_map.decoded_mappings().iter(); + let mut mappings_iter = source_map.decoded_mappings(); let mut current_mapping = mappings_iter.next(); for (current_generated_index, c) in source.char_indices() { - if let Some(mapping) = current_mapping.take() { + if let Some(mapping) = ¤t_mapping { if mapping.generated_line == current_generated_line && mapping.generated_column == current_generated_column { @@ -553,8 +560,6 @@ fn stream_chunks_of_source_map_full<'a>( tracking_mapping_original = mapping.original; current_mapping = mappings_iter.next(); - } else { - current_mapping = Some(mapping); } } @@ -629,25 +634,15 @@ fn stream_chunks_of_source_map_lines_final<'a>( }; let mut current_generated_line = 1; - let mut on_mapping = |mapping: &Mapping| { - if let Some(original) = &mapping.original.filter(|_| { + let mut on_mapping = |mut mapping: Mapping| { + if let Some(mut original) = mapping.original.filter(|_| { current_generated_line <= mapping.generated_line && mapping.generated_line <= final_line }) { - on_chunk( - None, - Mapping { - generated_line: mapping.generated_line, - generated_column: 0, - original: Some(OriginalLocation { - source_index: original.source_index, - original_line: original.original_line, - original_column: original.original_column, - name_index: None, - }), - }, - ); + mapping.generated_column = 0; + original.name_index = None; current_generated_line = mapping.generated_line + 1; + on_chunk(None, mapping); } }; for mapping in source_map.decoded_mappings() { @@ -678,7 +673,7 @@ fn stream_chunks_of_source_map_lines_full<'a>( ) } let mut current_generated_line = 1; - let mut on_mapping = |mapping: &Mapping| { + let mut on_mapping = |mut mapping: Mapping| { if mapping.original.is_none() || mapping.generated_line < current_generated_line || mapping.generated_line as usize > lines.len() @@ -699,24 +694,14 @@ fn stream_chunks_of_source_map_lines_full<'a>( } current_generated_line += 1; } - if let Some(original) = &mapping + if let Some(mut original) = mapping .original .filter(|_| mapping.generated_line as usize <= lines.len()) { let chunk = lines[current_generated_line as usize - 1]; - on_chunk( - Some(Cow::Borrowed(chunk)), - Mapping { - generated_line: mapping.generated_line, - generated_column: 0, - original: Some(OriginalLocation { - source_index: original.source_index, - original_line: original.original_line, - original_column: original.original_column, - name_index: None, - }), - }, - ); + mapping.generated_column = 0; + original.name_index = None; + on_chunk(Some(Cow::Borrowed(chunk)), mapping); current_generated_line += 1; } }; @@ -752,29 +737,7 @@ fn stream_chunks_of_source_map_lines_full<'a>( #[derive(Debug)] struct SourceMapLineData<'a> { pub mappings_data: Vec, - pub chunks: Vec>, -} - -#[derive(Debug)] -struct SourceMapLineChunk<'a> { - content: Cow<'a, str>, - cached: OnceCell>>, -} - -impl<'a> SourceMapLineChunk<'a> { - pub fn new(content: Cow<'a, str>) -> Self { - Self { - content, - cached: OnceCell::new(), - } - } - - pub fn substring(&self, start_index: usize, end_index: usize) -> &str { - let cached = self - .cached - .get_or_init(|| WithIndices::new(self.content.clone())); - cached.substring(start_index, end_index) - } + pub chunks: Vec>>, } type InnerSourceIndexValueMapping<'a> = @@ -1223,7 +1186,7 @@ pub fn stream_chunks_of_combined_source_map<'a>( .unwrap_or(-1), ); // SAFETY: final_source is false - let chunk = SourceMapLineChunk::new(chunk.unwrap()); + let chunk = WithIndices::new(chunk.unwrap()); data.chunks.push(chunk); }, &mut |i, source, source_content| { diff --git a/src/replace_source.rs b/src/replace_source.rs index b7e03c80..8e7b613b 100644 --- a/src/replace_source.rs +++ b/src/replace_source.rs @@ -4,7 +4,7 @@ use std::{ hash::{Hash, Hasher}, sync::{ atomic::{AtomicBool, Ordering}, - Arc, Mutex, MutexGuard, OnceLock, + Arc, Mutex, MutexGuard, }, }; @@ -37,7 +37,6 @@ use crate::{ /// ``` pub struct ReplaceSource { inner: Arc, - inner_source_code: OnceLock>, replacements: Mutex>, /// Whether `replacements` is sorted. is_sorted: AtomicBool, @@ -87,7 +86,6 @@ impl ReplaceSource { pub fn new(source: T) -> Self { Self { inner: Arc::new(source), - inner_source_code: OnceLock::new(), replacements: Mutex::new(Vec::new()), is_sorted: AtomicBool::new(true), } @@ -114,12 +112,6 @@ impl ReplaceSource { } impl ReplaceSource { - fn get_inner_source_code(&self) -> &str { - self - .inner_source_code - .get_or_init(|| Box::from(self.inner.source())) - } - /// Insert a content at start. pub fn insert(&mut self, start: u32, content: &str, name: Option<&str>) { self.replace(start, start, content, name) @@ -178,7 +170,7 @@ impl Source for ReplaceSource { fn source(&self) -> Cow { self.sort_replacement(); - let inner_source_code = self.get_inner_source_code(); + let inner_source_code = self.inner.source(); // mut_string_push_str is faster that vec join // concatenate strings benchmark, see https://github.com/hoodie/concatenation_benchmarks-rs @@ -240,13 +232,6 @@ impl std::fmt::Debug for ReplaceSource { ) -> Result<(), std::fmt::Error> { f.debug_struct("ReplaceSource") .field("inner", self.inner.as_ref()) - .field( - "inner_source_code", - &self - .inner_source_code - .get() - .map(|s| s.chars().take(50).collect::()), - ) .field( "replacements", &self.replacements.lock().iter().take(3).collect::>(), @@ -438,7 +423,6 @@ impl<'a, T: Source> StreamChunks<'a> for ReplaceSource { // Insert replacement content split into chunks by lines let repl = &repls[i]; - let lines: Vec<&str> = split_into_lines(&repl.content).collect(); let mut replacement_name_index = mapping .original .as_ref() @@ -456,7 +440,8 @@ impl<'a, T: Source> StreamChunks<'a> for ReplaceSource { } replacement_name_index = global_index; } - for (m, content_line) in lines.iter().enumerate() { + let mut consumed_len = 0; + for content_line in split_into_lines(&repl.content) { on_chunk( Some(Cow::Owned(content_line.to_string())), Mapping { @@ -479,8 +464,10 @@ impl<'a, T: Source> StreamChunks<'a> for ReplaceSource { ); // Only the first chunk has name assigned replacement_name_index = None; - - if m == lines.len() - 1 && !content_line.ends_with('\n') { + consumed_len += content_line.len(); + if consumed_len == repl.content.len() + && !content_line.ends_with('\n') + { if generated_column_offset_line == line { generated_column_offset += content_line.len() as i64; } else { @@ -679,7 +666,6 @@ impl Clone for ReplaceSource { fn clone(&self) -> Self { Self { inner: self.inner.clone(), - inner_source_code: self.inner_source_code.clone(), replacements: Mutex::new(self.replacements().clone()), is_sorted: AtomicBool::new(self.is_sorted.load(Ordering::SeqCst)), } diff --git a/src/source.rs b/src/source.rs index 38dc1332..e5bdf188 100644 --- a/src/source.rs +++ b/src/source.rs @@ -4,7 +4,7 @@ use std::{ convert::{TryFrom, TryInto}, fmt, hash::{Hash, Hasher}, - sync::{Arc, OnceLock}, + sync::Arc, }; use dyn_clone::DynClone; @@ -194,7 +194,6 @@ pub struct SourceMap { sources_content: Vec>, names: Vec>, source_root: Option, - decoded_mappings: OnceLock>, } impl Hash for SourceMap { @@ -224,7 +223,6 @@ impl SourceMap { sources_content, names, source_root: None, - decoded_mappings: Default::default(), } } @@ -239,10 +237,8 @@ impl SourceMap { } /// Get the decoded mappings in [SourceMap]. - pub fn decoded_mappings(&'_ self) -> &[Mapping] { - self - .decoded_mappings - .get_or_init(|| decode_mappings(self).collect::>()) + pub fn decoded_mappings(&self) -> impl Iterator + '_ { + decode_mappings(self) } /// Get the mappings string in [SourceMap]. @@ -433,7 +429,6 @@ impl TryFrom for SourceMap { sources_content, names, source_root: raw.source_root, - decoded_mappings: Default::default(), }) } } diff --git a/src/vlq.rs b/src/vlq.rs index 419dcff4..1741c6db 100644 --- a/src/vlq.rs +++ b/src/vlq.rs @@ -1,301 +1,80 @@ //! Implements utilities for dealing with the sourcemap vlq encoding. //! forked from [rust-sourcemap](https://github.com/getsentry/rust-sourcemap/blob/851f12bfa6c4cf2c737b94734b27f7d9bfb4de86/src/vlq.rs) -use arrayvec::ArrayVec; - use crate::{Error, Result}; const B64_CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const B64: [i8; 256] = [ - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - 62, - -1, - -1, - -1, - 63, - 52, - 53, - 54, - 55, - 56, - 57, - 58, - 59, - 60, - 61, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - -1, - -1, - -1, - -1, - -1, - -1, - 26, - 27, - 28, - 29, - 30, - 31, - 32, - 33, - 34, - 35, - 36, - 37, - 38, - 39, - 40, - 41, - 42, - 43, - 44, - 45, - 46, - 47, - 48, - 49, - 50, - 51, - -1, - -1, - -1, - -1, - -1 - 1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, - -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, + 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, ]; -/// Parses a VLQ segment into a pre-allocated `Vec` instead of returning a new allocation. -pub fn decode(segment: &str, rv: &mut ArrayVec) -> Result<()> { - let mut cur = 0; - let mut shift = 0; +pub struct VlqIter<'a> { + segment: std::slice::Iter<'a, u8>, + cur: i64, + shift: u32, +} + +impl<'a> Iterator for VlqIter<'a> { + type Item = Result; - for c in segment.bytes() { - let enc = i64::from(B64[c as usize]); - let val = enc & 0b11111; - let cont = enc >> 5; - cur += val.checked_shl(shift).ok_or(Error::VlqOverflow)?; - shift += 5; + fn next(&mut self) -> Option { + loop { + let c = self.segment.next(); + match c { + Some(c) => { + let enc = i64::from(B64[*c as usize]); + let val = enc & 0b11111; + let cont = enc >> 5; + self.cur += + match val.checked_shl(self.shift).ok_or(Error::VlqOverflow) { + Ok(v) => v, + Err(e) => return Some(Err(e)), + }; + self.shift += 5; - if cont == 0 { - let sign = cur & 1; - cur >>= 1; - if sign != 0 { - cur = -cur; + if cont == 0 { + let sign = self.cur & 1; + self.cur >>= 1; + if sign != 0 { + self.cur = -self.cur; + } + let result = self.cur; + self.cur = 0; + self.shift = 0; + return Some(Ok(result)); + } + } + None => { + if self.cur != 0 || self.shift != 0 { + return Some(Err(Error::VlqLeftover)); + } else { + return None; + } + } } - rv.push(cur); - cur = 0; - shift = 0; } } +} - if cur != 0 || shift != 0 { - Err(Error::VlqLeftover) - } else if rv.is_empty() { - Err(Error::VlqNoValues) - } else { - Ok(()) +pub fn decode(segment: &[u8]) -> VlqIter<'_> { + VlqIter { + segment: segment.iter(), + cur: 0, + shift: 0, } }