Skip to content

Commit 79be155

Browse files
committed
perf: Rope enum
1 parent 9d2263c commit 79be155

File tree

9 files changed

+75
-57
lines changed

9 files changed

+75
-57
lines changed

benches/benchmark_repetitive_react_components.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ pub use criterion::*;
99
pub use codspeed_criterion_compat::*;
1010

1111
use rspack_sources::{
12-
BoxSource, CachedSource, ConcatSource, MapOptions, ObjectPool,
13-
OriginalSource, RawStringSource, ReplaceSource, ReplacementEnforce, Source,
14-
SourceExt, SourceMap, SourceMapSource, SourceMapSourceOptions,
12+
BoxSource, ConcatSource, MapOptions, ObjectPool, OriginalSource,
13+
RawStringSource, ReplaceSource, ReplacementEnforce, Source, SourceExt,
14+
SourceMap, SourceMapSource, SourceMapSourceOptions,
1515
};
1616

1717
static REPETITIVE_1K_REACT_COMPONENTS_SOURCE: LazyLock<BoxSource> =

src/cached_source.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
},
1414
object_pool::ObjectPool,
1515
source::SourceValue,
16-
BoxSource, MapOptions, Source, SourceExt, SourceMap,
16+
BoxSource, MapOptions, Rope, Source, SourceExt, SourceMap,
1717
};
1818

1919
#[derive(Default)]
@@ -82,14 +82,17 @@ impl CachedSource {
8282

8383
fn get_or_init_chunks(&self) -> &[&str] {
8484
self.cache.chunks.get_or_init(|| {
85+
let rope = self.inner.rope();
86+
let chunks = match rope {
87+
Rope::Light(s) => vec![s],
88+
Rope::Full(iter) => iter.collect::<Vec<_>>(),
89+
};
8590
#[allow(unsafe_code)]
8691
// SAFETY: CachedSource guarantees that the underlying source outlives the cache,
8792
// so transmuting Vec<&str> to Vec<&'static str> is safe in this context.
8893
// This allows us to store string slices in the cache without additional allocations.
8994
unsafe {
90-
std::mem::transmute::<Vec<&str>, Vec<&'static str>>(
91-
self.rope().collect(),
92-
)
95+
std::mem::transmute::<Vec<&str>, Vec<&'static str>>(chunks)
9396
}
9497
})
9598
}
@@ -98,19 +101,16 @@ impl CachedSource {
98101
impl Source for CachedSource {
99102
fn source(&self) -> SourceValue {
100103
let chunks = self.get_or_init_chunks();
101-
if chunks.len() == 1 {
102-
return SourceValue::String(Cow::Borrowed(chunks[0]));
103-
}
104104
let mut string = String::with_capacity(self.size());
105105
for chunk in chunks {
106106
string.push_str(chunk);
107107
}
108108
SourceValue::String(Cow::Owned(string))
109109
}
110110

111-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_> {
111+
fn rope(&self) -> Rope {
112112
let chunks = self.get_or_init_chunks();
113-
Box::new(chunks.iter().cloned())
113+
Rope::Full(Box::new(chunks.iter().cloned()))
114114
}
115115

116116
fn buffer(&self) -> Cow<[u8]> {

src/concat_source.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
linear_map::LinearMap,
1313
object_pool::ObjectPool,
1414
source::{Mapping, OriginalLocation},
15-
BoxSource, MapOptions, RawStringSource, Source, SourceExt, SourceMap,
15+
BoxSource, MapOptions, RawStringSource, Rope, Source, SourceExt, SourceMap,
1616
SourceValue,
1717
};
1818

@@ -163,24 +163,29 @@ impl ConcatSource {
163163

164164
impl Source for ConcatSource {
165165
fn source(&self) -> SourceValue {
166-
let children = self.optimized_children();
167-
if children.len() == 1 {
168-
children[0].source()
169-
} else {
170-
let mut string = String::with_capacity(self.size());
171-
for chunk in self.rope() {
172-
string.push_str(chunk);
166+
match self.rope() {
167+
Rope::Light(s) => SourceValue::String(Cow::Borrowed(s)),
168+
Rope::Full(iter) => {
169+
let mut string = String::with_capacity(self.size());
170+
for chunk in iter {
171+
string.push_str(chunk);
172+
}
173+
SourceValue::String(Cow::Owned(string))
173174
}
174-
SourceValue::String(Cow::Owned(string))
175175
}
176176
}
177177

178-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_> {
178+
fn rope(&self) -> Rope {
179179
let children = self.optimized_children();
180180
if children.len() == 1 {
181181
children[0].rope()
182182
} else {
183-
Box::new(children.iter().flat_map(|child| child.rope()))
183+
Rope::Full(Box::new(children.iter().flat_map(
184+
|child| match child.rope() {
185+
Rope::Light(s) => Box::new(std::iter::once(s)),
186+
Rope::Full(iter) => iter,
187+
},
188+
)))
184189
}
185190
}
186191

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub use original_source::OriginalSource;
2323
pub use raw_source::{RawBufferSource, RawStringSource};
2424
pub use replace_source::{ReplaceSource, ReplacementEnforce};
2525
pub use source::{
26-
BoxSource, MapOptions, Mapping, OriginalLocation, Source, SourceExt,
26+
BoxSource, MapOptions, Mapping, OriginalLocation, Rope, Source, SourceExt,
2727
SourceMap, SourceValue,
2828
};
2929
pub use source_map_source::{

src/original_source.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::{
1111
},
1212
object_pool::ObjectPool,
1313
source::{Mapping, OriginalLocation},
14-
MapOptions, Source, SourceMap, SourceValue,
14+
MapOptions, Rope, Source, SourceMap, SourceValue,
1515
};
1616

1717
/// Represents source code, it will create source map for the source code,
@@ -56,8 +56,8 @@ impl Source for OriginalSource {
5656
SourceValue::String(Cow::Borrowed(&self.value))
5757
}
5858

59-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_> {
60-
Box::new(std::iter::once(self.value.as_ref()))
59+
fn rope(&self) -> Rope {
60+
Rope::Light(self.value.as_ref())
6161
}
6262

6363
fn buffer(&self) -> Cow<[u8]> {

src/raw_source.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
GeneratedInfo, StreamChunks,
1111
},
1212
object_pool::ObjectPool,
13-
MapOptions, Source, SourceMap, SourceValue,
13+
MapOptions, Rope, Source, SourceMap, SourceValue,
1414
};
1515

1616
/// A string variant of [RawStringSource].
@@ -64,8 +64,8 @@ impl Source for RawStringSource {
6464
SourceValue::String(Cow::Borrowed(&self.0))
6565
}
6666

67-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_> {
68-
Box::new(std::iter::once(self.0.as_ref()))
67+
fn rope(&self) -> Rope {
68+
Rope::Light(self.0.as_ref())
6969
}
7070

7171
fn buffer(&self) -> Cow<[u8]> {
@@ -210,8 +210,8 @@ impl Source for RawBufferSource {
210210
SourceValue::Buffer(Cow::Borrowed(&self.value))
211211
}
212212

213-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_> {
214-
Box::new(std::iter::once(self.get_or_init_value_as_string()))
213+
fn rope(&self) -> Rope {
214+
Rope::Light(self.get_or_init_value_as_string())
215215
}
216216

217217
fn buffer(&self) -> Cow<[u8]> {

src/replace_source.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::{
1212
linear_map::LinearMap,
1313
object_pool::ObjectPool,
1414
source_content_lines::SourceContentLines,
15-
BoxSource, MapOptions, Mapping, OriginalLocation, OriginalSource, Source,
16-
SourceExt, SourceMap, SourceValue,
15+
BoxSource, MapOptions, Mapping, OriginalLocation, OriginalSource, Rope,
16+
Source, SourceExt, SourceMap, SourceValue,
1717
};
1818

1919
/// Decorates a Source with replacements and insertions of source code,
@@ -161,24 +161,30 @@ impl ReplaceSource {
161161

162162
impl Source for ReplaceSource {
163163
fn source(&self) -> SourceValue {
164-
if self.replacements.is_empty() {
165-
return self.inner.source();
166-
}
167-
let mut string = String::with_capacity(self.size());
168-
for chunk in self.rope() {
169-
string.push_str(chunk);
164+
match self.rope() {
165+
Rope::Light(s) => SourceValue::String(Cow::Borrowed(s)),
166+
Rope::Full(iter) => {
167+
let mut string = String::with_capacity(self.size());
168+
for chunk in iter {
169+
string.push_str(chunk);
170+
}
171+
SourceValue::String(Cow::Owned(string))
172+
}
170173
}
171-
SourceValue::String(Cow::Owned(string))
172174
}
173175

174-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_> {
176+
fn rope(&self) -> Rope {
175177
if self.replacements.is_empty() {
176178
return self.inner.rope();
177179
}
178-
Box::new(ReplaceSourceRopeIterator::new(
179-
self.inner.rope(),
180+
let inner_chunks = match self.inner.rope() {
181+
Rope::Light(s) => Box::new(std::iter::once(s)),
182+
Rope::Full(iter) => iter,
183+
};
184+
Rope::Full(Box::new(ReplaceSourceRopeIterator::new(
185+
inner_chunks,
180186
&self.replacements,
181-
))
187+
)))
182188
}
183189

184190
fn buffer(&self) -> Cow<[u8]> {
@@ -239,8 +245,13 @@ impl Source for ReplaceSource {
239245
}
240246

241247
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
242-
for text in self.rope() {
243-
writer.write_all(text.as_bytes())?;
248+
match self.rope() {
249+
Rope::Light(s) => writer.write_all(s.as_bytes())?,
250+
Rope::Full(iter) => {
251+
for chunk in iter {
252+
writer.write_all(chunk.as_bytes())?
253+
}
254+
}
244255
}
245256
Ok(())
246257
}

src/source.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ impl<'a> SourceValue<'a> {
107107
}
108108
}
109109

110+
pub enum Rope<'a> {
111+
Light(&'a str),
112+
Full(Box<dyn Iterator<Item = &'a str> + 'a>),
113+
}
114+
110115
/// [Source] abstraction, [webpack-sources docs](https://github.com/webpack/webpack-sources/#source).
111116
pub trait Source:
112117
StreamChunks + DynHash + AsAny + DynEq + DynClone + fmt::Debug + Sync + Send
@@ -115,7 +120,7 @@ pub trait Source:
115120
fn source(&self) -> SourceValue;
116121

117122
/// Return a lightweight "rope" view of the source as borrowed string slices.
118-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_>;
123+
fn rope(&self) -> Rope;
119124

120125
/// Get the source buffer.
121126
fn buffer(&self) -> Cow<[u8]>;
@@ -144,7 +149,7 @@ impl Source for BoxSource {
144149
self.as_ref().source()
145150
}
146151

147-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_> {
152+
fn rope(&self) -> Rope {
148153
self.as_ref().rope()
149154
}
150155

src/source_map_source.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@ use std::{
55
};
66

77
use crate::{
8-
helpers::{
9-
get_map, stream_chunks_of_combined_source_map, stream_chunks_of_source_map,
10-
Chunks, StreamChunks,
11-
},
12-
object_pool::ObjectPool,
13-
MapOptions, Source, SourceMap, SourceValue,
8+
MapOptions, Rope, Source, SourceMap, SourceValue, helpers::{
9+
Chunks, StreamChunks, get_map, stream_chunks_of_combined_source_map, stream_chunks_of_source_map
10+
}, object_pool::ObjectPool
1411
};
1512

1613
/// Options for [SourceMapSource::new].
@@ -94,8 +91,8 @@ impl Source for SourceMapSource {
9491
SourceValue::String(Cow::Borrowed(&self.value))
9592
}
9693

97-
fn rope(&self) -> Box<dyn Iterator<Item = &str> + '_> {
98-
Box::new(std::iter::once(self.value.as_ref()))
94+
fn rope(&self) -> Rope {
95+
Rope::Light(&self.value)
9996
}
10097

10198
fn buffer(&self) -> Cow<[u8]> {

0 commit comments

Comments
 (0)