Skip to content

Commit

Permalink
perf: using Arc<str> instead of String (#84)
Browse files Browse the repository at this point in the history
  • Loading branch information
underfin authored Mar 19, 2024
1 parent 16dab53 commit f80bd6c
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 82 deletions.
27 changes: 14 additions & 13 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::env;
use std::fs;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use debugid::DebugId;
use rustc_hash::FxHashMap;
Expand All @@ -19,14 +20,14 @@ use crate::types::{RawToken, SourceMap, Token};
/// objects is generally not very comfortable. As a general aid this
/// type can help.
pub struct SourceMapBuilder {
file: Option<String>,
name_map: FxHashMap<String, u32>,
names: Vec<String>,
file: Option<Arc<str>>,
name_map: FxHashMap<Arc<str>, u32>,
names: Vec<Arc<str>>,
tokens: Vec<RawToken>,
source_map: FxHashMap<String, u32>,
source_root: Option<String>,
sources: Vec<String>,
source_contents: Vec<Option<String>>,
source_map: FxHashMap<Arc<str>, u32>,
source_root: Option<Arc<str>>,
sources: Vec<Arc<str>>,
source_contents: Vec<Option<Arc<str>>>,
sources_mapping: Vec<u32>,
debug_id: Option<DebugId>,
}
Expand All @@ -52,7 +53,7 @@ impl SourceMapBuilder {
/// Creates a new source map builder and sets the file.
pub fn new(file: Option<&str>) -> SourceMapBuilder {
SourceMapBuilder {
file: file.map(str::to_owned),
file: file.map(Into::into),
name_map: FxHashMap::default(),
names: vec![],
tokens: vec![],
Expand All @@ -71,7 +72,7 @@ impl SourceMapBuilder {
}

/// Sets the file for the sourcemap (optional)
pub fn set_file<T: Into<String>>(&mut self, value: Option<T>) {
pub fn set_file<T: Into<Arc<str>>>(&mut self, value: Option<T>) {
self.file = value.map(Into::into);
}

Expand All @@ -81,7 +82,7 @@ impl SourceMapBuilder {
}

/// Sets a new value for the source_root.
pub fn set_source_root<T: Into<String>>(&mut self, value: Option<T>) {
pub fn set_source_root<T: Into<Arc<str>>>(&mut self, value: Option<T>) {
self.source_root = value.map(Into::into);
}

Expand All @@ -108,7 +109,7 @@ impl SourceMapBuilder {
/// Changes the source name for an already set source.
pub fn set_source(&mut self, src_id: u32, src: &str) {
assert!(src_id != !0, "Cannot set sources for tombstone source id");
self.sources[src_id as usize] = src.to_string();
self.sources[src_id as usize] = src.into();
}

/// Looks up a source name for an ID.
Expand All @@ -122,7 +123,7 @@ impl SourceMapBuilder {
if self.sources.len() > self.source_contents.len() {
self.source_contents.resize(self.sources.len(), None);
}
self.source_contents[src_id as usize] = contents.map(str::to_owned);
self.source_contents[src_id as usize] = contents.map(Into::into);
}

/// Returns the current source contents for a source.
Expand Down Expand Up @@ -272,7 +273,7 @@ impl SourceMapBuilder {
prefix.push('/');
}
if source.starts_with(&prefix) {
*source = source[prefix.len()..].to_string();
*source = source[prefix.len()..].into();
break;
}
}
Expand Down
20 changes: 14 additions & 6 deletions src/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,26 +187,34 @@ pub fn decode_regular(rsm: RawSourceMap) -> Result<SourceMap> {
}
}

let sources = sources.into_iter().map(Option::unwrap_or_default).collect();
let sources = sources
.into_iter()
.map(Option::unwrap_or_default)
.map(Into::into)
.collect();

// apparently we can encounter some non string types in real world
// sourcemaps :(
let names = names
.into_iter()
.map(|val| match val {
Value::String(s) => s,
Value::Number(num) => num.to_string(),
Value::String(s) => s.into(),
Value::Number(num) => num.to_string().into(),
_ => "".into(),
})
.collect::<Vec<String>>();
.collect::<Vec<_>>();

// file sometimes is not a string for unexplicable reasons
let file = rsm.file.map(|val| match val {
Value::String(s) => s,
Value::String(s) => s.into(),
_ => "<invalid>".into(),
});

let mut sm = SourceMap::new(file, tokens, names, sources, rsm.sources_content);
let source_content = rsm
.sources_content
.map(|x| x.into_iter().map(|v| v.map(Into::into)).collect::<Vec<_>>());

let mut sm = SourceMap::new(file, tokens, names, sources, source_content);
sm.set_source_root(rsm.source_root);
sm.set_debug_id(rsm.debug_id);

Expand Down
10 changes: 5 additions & 5 deletions src/ram_bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ impl<'a> RamBundleModule<'a> {
/// Returns a source view of the data.
///
/// This operation fails if the source code is not valid UTF-8.
pub fn source_view(&self) -> Result<SourceView<'a>> {
pub fn source_view(&self) -> Result<SourceView> {
match std::str::from_utf8(self.data) {
Ok(s) => Ok(SourceView::new(s)),
Ok(s) => Ok(SourceView::new(s.into())),
Err(e) => Err(Error::Utf8(e)),
}
}
Expand Down Expand Up @@ -361,7 +361,7 @@ impl<'a> SplitRamBundleModuleIter<'a> {
fn split_module(
&self,
module: RamBundleModule<'a>,
) -> Result<Option<(String, SourceView<'a>, SourceMap)>> {
) -> Result<Option<(String, SourceView, SourceMap)>> {
let module_offset = self
.offsets
.get(module.id())
Expand All @@ -377,7 +377,7 @@ impl<'a> SplitRamBundleModuleIter<'a> {
return Err(Error::InvalidRamBundleEntry);
}

let source: SourceView<'a> = module.source_view()?;
let source: SourceView = module.source_view()?;
let line_count = source.line_count() as u32;
let ending_line = starting_line + line_count;
let last_line_len = source
Expand Down Expand Up @@ -416,7 +416,7 @@ impl<'a> SplitRamBundleModuleIter<'a> {
}

impl<'a> Iterator for SplitRamBundleModuleIter<'a> {
type Item = Result<(String, SourceView<'a>, SourceMap)>;
type Item = Result<(String, SourceView, SourceMap)>;

fn next(&mut self) -> Option<Self::Item> {
while let Some(module_result) = self.ram_bundle_iter.next() {
Expand Down
55 changes: 23 additions & 32 deletions src/sourceview.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::borrow::Cow;
use std::fmt;
use std::slice;
use std::str;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;

use if_chain::if_chain;
Expand All @@ -14,19 +14,13 @@ use crate::js_identifiers::{get_javascript_token, is_valid_javascript_identifier
use crate::types::{idx_from_token, sourcemap_from_token, Token};

/// An iterator that iterates over tokens in reverse.
pub struct RevTokenIter<'view, 'viewbase, 'map>
where
'viewbase: 'view,
{
sv: &'view SourceView<'viewbase>,
pub struct RevTokenIter<'view, 'map> {
sv: &'view SourceView,
token: Option<Token<'map>>,
source_line: Option<(&'view str, usize, usize, usize)>,
}

impl<'view, 'viewbase, 'map> Iterator for RevTokenIter<'view, 'viewbase, 'map>
where
'viewbase: 'view,
{
impl<'view, 'map> Iterator for RevTokenIter<'view, 'map> {
type Item = (Token<'map>, Option<&'view str>);

fn next(&mut self) -> Option<(Token<'map>, Option<&'view str>)> {
Expand Down Expand Up @@ -118,7 +112,7 @@ where
}

pub struct Lines<'a> {
sv: &'a SourceView<'a>,
sv: &'a SourceView,
idx: u32,
}

Expand All @@ -139,14 +133,14 @@ impl<'a> Iterator for Lines<'a> {
///
/// This type is used to implement fairly efficient source mapping
/// operations.
pub struct SourceView<'a> {
source: Cow<'a, str>,
pub struct SourceView {
source: Arc<str>,
processed_until: AtomicUsize,
lines: Mutex<Vec<&'static str>>,
}

impl<'a> Clone for SourceView<'a> {
fn clone(&self) -> SourceView<'a> {
impl Clone for SourceView {
fn clone(&self) -> SourceView {
SourceView {
source: self.source.clone(),
processed_until: AtomicUsize::new(0),
Expand All @@ -155,28 +149,28 @@ impl<'a> Clone for SourceView<'a> {
}
}

impl<'a> fmt::Debug for SourceView<'a> {
impl fmt::Debug for SourceView {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("SourceView")
.field("source", &self.source())
.finish()
}
}

impl<'a> SourceView<'a> {
impl SourceView {
/// Creates an optimized view of a given source.
pub fn new(source: &'a str) -> SourceView<'a> {
pub fn new(source: Arc<str>) -> SourceView {
SourceView {
source: Cow::Borrowed(source),
source,
processed_until: AtomicUsize::new(0),
lines: Mutex::new(vec![]),
}
}

/// Creates an optimized view from a given source string
pub fn from_string(source: String) -> SourceView<'static> {
pub fn from_string(source: String) -> SourceView {
SourceView {
source: Cow::Owned(source),
source: source.into(),
processed_until: AtomicUsize::new(0),
lines: Mutex::new(vec![]),
}
Expand Down Expand Up @@ -264,7 +258,7 @@ impl<'a> SourceView<'a> {
}

/// Returns an iterator over all lines.
pub fn lines(&'a self) -> Lines<'a> {
pub fn lines(&self) -> Lines {
Lines { sv: self, idx: 0 }
}

Expand All @@ -273,10 +267,7 @@ impl<'a> SourceView<'a> {
&self.source
}

fn rev_token_iter<'this, 'map>(
&'this self,
token: Token<'map>,
) -> RevTokenIter<'this, 'a, 'map> {
fn rev_token_iter<'this, 'map>(&'this self, token: Token<'map>) -> RevTokenIter<'this, 'map> {
RevTokenIter {
sv: self,
token: Some(token),
Expand Down Expand Up @@ -332,7 +323,7 @@ impl<'a> SourceView<'a> {
#[test]
#[allow(clippy::cognitive_complexity)]
fn test_minified_source_view() {
let view = SourceView::new("a\nb\nc");
let view = SourceView::new("a\nb\nc".into());
assert_eq!(view.get_line(0), Some("a"));
assert_eq!(view.get_line(0), Some("a"));
assert_eq!(view.get_line(2), Some("c"));
Expand All @@ -341,7 +332,7 @@ fn test_minified_source_view() {

assert_eq!(view.line_count(), 3);

let view = SourceView::new("a\r\nb\r\nc");
let view = SourceView::new("a\r\nb\r\nc".into());
assert_eq!(view.get_line(0), Some("a"));
assert_eq!(view.get_line(0), Some("a"));
assert_eq!(view.get_line(2), Some("c"));
Expand All @@ -350,7 +341,7 @@ fn test_minified_source_view() {

assert_eq!(view.line_count(), 3);

let view = SourceView::new("abc👌def\nblah");
let view = SourceView::new("abc👌def\nblah".into());
assert_eq!(view.get_line_slice(0, 0, 3), Some("abc"));
assert_eq!(view.get_line_slice(0, 3, 1), Some("👌"));
assert_eq!(view.get_line_slice(0, 3, 2), Some("👌"));
Expand All @@ -362,7 +353,7 @@ fn test_minified_source_view() {
assert_eq!(view.get_line_slice(1, 0, 5), None);
assert_eq!(view.get_line_slice(1, 0, 12), None);

let view = SourceView::new("a\nb\nc\n");
let view = SourceView::new("a\nb\nc\n".into());
assert_eq!(view.get_line(0), Some("a"));
assert_eq!(view.get_line(1), Some("b"));
assert_eq!(view.get_line(2), Some("c"));
Expand All @@ -371,6 +362,6 @@ fn test_minified_source_view() {

fn is_send<T: Send>() {}
fn is_sync<T: Sync>() {}
is_send::<SourceView<'static>>();
is_sync::<SourceView<'static>>();
is_send::<SourceView>();
is_sync::<SourceView>();
}
Loading

0 comments on commit f80bd6c

Please sign in to comment.