Skip to content

Commit 4b5693a

Browse files
authored
fix: replace size compute (#204)
1 parent aea29de commit 4b5693a

File tree

12 files changed

+259
-110
lines changed

12 files changed

+259
-110
lines changed

.github/workflows/Bench.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
- name: Install codspeed
3030
uses: taiki-e/install-action@v2
3131
with:
32-
tool: cargo-codspeed
32+
tool: cargo-codspeed@4.0.5
3333

3434
- name: Build Benchmark
3535
run: cargo codspeed build --features codspeed

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: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::{
22
borrow::Cow,
3-
cell::OnceCell,
43
hash::{Hash, Hasher},
54
sync::{Arc, OnceLock},
65
};
@@ -21,6 +20,7 @@ use crate::{
2120
struct CachedData {
2221
hash: OnceLock<u64>,
2322
size: OnceLock<usize>,
23+
chunks: OnceLock<Vec<&'static str>>,
2424
columns_map: OnceLock<Option<SourceMap>>,
2525
line_only_map: OnceLock<Option<SourceMap>>,
2626
}
@@ -79,19 +79,50 @@ impl CachedSource {
7979
cache: Arc::new(CachedData::default()),
8080
}
8181
}
82+
83+
fn get_or_init_chunks(&self) -> &[&str] {
84+
self.cache.chunks.get_or_init(|| {
85+
let mut chunks = Vec::new();
86+
self.inner.rope(&mut |chunk| {
87+
chunks.push(chunk);
88+
});
89+
#[allow(unsafe_code)]
90+
// SAFETY: CachedSource guarantees that the underlying source outlives the cache,
91+
// so transmuting Vec<&str> to Vec<&'static str> is safe in this context.
92+
// This allows us to store string slices in the cache without additional allocations.
93+
unsafe {
94+
std::mem::transmute::<Vec<&str>, Vec<&'static str>>(chunks)
95+
}
96+
})
97+
}
8298
}
8399

84100
impl Source for CachedSource {
85101
fn source(&self) -> SourceValue {
86-
self.inner.source()
102+
let chunks = self.get_or_init_chunks();
103+
let mut string = String::with_capacity(self.size());
104+
for chunk in chunks {
105+
string.push_str(chunk);
106+
}
107+
SourceValue::String(Cow::Owned(string))
108+
}
109+
110+
fn rope<'a>(&'a self, on_chunk: &mut dyn FnMut(&'a str)) {
111+
let chunks = self.get_or_init_chunks();
112+
chunks.iter().for_each(|chunk| on_chunk(chunk));
87113
}
88114

89115
fn buffer(&self) -> Cow<[u8]> {
90116
self.inner.buffer()
91117
}
92118

93119
fn size(&self) -> usize {
94-
*self.cache.size.get_or_init(|| self.inner.size())
120+
*self.cache.size.get_or_init(|| {
121+
if let Some(chunks) = self.cache.chunks.get() {
122+
return chunks.iter().fold(0, |acc, chunk| acc + chunk.len());
123+
}
124+
self.inner.size()
125+
})
95126
}
96127

97128
fn map(
@@ -114,10 +145,6 @@ impl Source for CachedSource {
114145
}
115146
}
116147

117-
fn write_to_string(&self, string: &mut String) {
118-
self.inner.write_to_string(string);
119-
}
120-
121148
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
122149
self.inner.to_writer(writer)
123150
}
@@ -126,17 +153,17 @@ impl Source for CachedSource {
126153
struct CachedSourceChunks<'source> {
127154
chunks: Box<dyn Chunks + 'source>,
128155
cache: Arc<CachedData>,
129-
inner: &'source dyn Source,
130-
source: OnceCell<Cow<'source, str>>,
156+
source: Cow<'source, str>,
131157
}
132158

133159
impl<'a> CachedSourceChunks<'a> {
134160
fn new(cache_source: &'a CachedSource) -> Self {
161+
let source = cache_source.source().into_string_lossy();
162+
135163
Self {
136164
chunks: cache_source.inner.stream_chunks(),
137165
cache: cache_source.cache.clone(),
138-
inner: &cache_source.inner,
139-
source: OnceCell::new(),
166+
source,
140167
}
141168
}
142169
}
@@ -157,22 +184,19 @@ impl Chunks for CachedSourceChunks<'_> {
157184
};
158185
match cell.get() {
159186
Some(map) => {
160-
let source = self
161-
.source
162-
.get_or_init(|| self.inner.source().into_string_lossy());
163187
if let Some(map) = map {
164188
stream_chunks_of_source_map(
165189
options,
166190
object_pool,
167-
source.as_ref(),
191+
self.source.as_ref(),
168192
map,
169193
on_chunk,
170194
on_source,
171195
on_name,
172196
)
173197
} else {
174198
stream_chunks_of_raw_source(
175-
source.as_ref(),
199+
self.source.as_ref(),
176200
options,
177201
on_chunk,
178202
on_source,

src/concat_source.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,24 @@ impl Source for ConcatSource {
165165
fn source(&self) -> SourceValue {
166166
let children = self.optimized_children();
167167
if children.len() == 1 {
168-
children[0].source()
169-
} else {
170-
// Use to_writer to avoid multiple heap allocations that would occur
171-
// when concatenating nested ConcatSource instances directly
172-
let mut string = String::with_capacity(self.size());
173-
self.write_to_string(&mut string);
174-
SourceValue::String(Cow::Owned(string))
168+
return children[0].source();
175169
}
170+
171+
let mut string = String::with_capacity(self.size());
172+
let mut on_chunk = |chunk| {
173+
string.push_str(chunk);
174+
};
175+
children.iter().for_each(|child| {
176+
child.rope(&mut on_chunk);
177+
});
178+
SourceValue::String(Cow::Owned(string))
179+
}
180+
181+
fn rope<'a>(&'a self, on_chunk: &mut dyn FnMut(&'a str)) {
182+
let children = self.optimized_children();
183+
children.iter().for_each(|child| {
184+
child.rope(on_chunk);
185+
});
176186
}
177187

178188
fn buffer(&self) -> Cow<[u8]> {
@@ -206,12 +216,6 @@ impl Source for ConcatSource {
206216
result
207217
}
208218

209-
fn write_to_string(&self, string: &mut String) {
210-
for child in self.optimized_children() {
211-
child.write_to_string(string);
212-
}
213-
}
214-
215219
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
216220
for child in self.optimized_children() {
217221
child.to_writer(writer)?;

src/helpers.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,10 +460,9 @@ fn stream_chunks_of_source_map_full<'a>(
460460
on_source: OnSource<'_, 'a>,
461461
on_name: OnName<'_, 'a>,
462462
) -> GeneratedInfo {
463-
let a = split_into_lines(source);
464-
let lines: Vec<WithUtf16<'a, 'a>> = a
463+
let lines = split_into_lines(source)
465464
.map(|line| WithUtf16::new(object_pool, line))
466-
.collect::<Vec<_>>();
465+
.collect::<Vec<WithUtf16<'a, 'a>>>();
467466

468467
if lines.is_empty() {
469468
return GeneratedInfo {

src/original_source.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ impl Source for OriginalSource {
5656
SourceValue::String(Cow::Borrowed(&self.value))
5757
}
5858

59+
fn rope<'a>(&'a self, on_chunk: &mut dyn FnMut(&'a str)) {
60+
on_chunk(self.value.as_ref())
61+
}
62+
5963
fn buffer(&self) -> Cow<[u8]> {
6064
Cow::Borrowed(self.value.as_bytes())
6165
}
@@ -73,10 +77,6 @@ impl Source for OriginalSource {
7377
get_map(object_pool, chunks.as_ref(), options)
7478
}
7579

76-
fn write_to_string(&self, string: &mut String) {
77-
string.push_str(self.value.as_ref());
78-
}
79-
8080
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
8181
writer.write_all(self.value.as_bytes())
8282
}

src/raw_source.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ impl Source for RawStringSource {
6464
SourceValue::String(Cow::Borrowed(&self.0))
6565
}
6666

67+
fn rope<'a>(&'a self, on_chunk: &mut dyn FnMut(&'a str)) {
68+
on_chunk(self.0.as_ref())
69+
}
70+
6771
fn buffer(&self) -> Cow<[u8]> {
6872
Cow::Borrowed(self.0.as_bytes())
6973
}
@@ -76,10 +80,6 @@ impl Source for RawStringSource {
7680
None
7781
}
7882

79-
fn write_to_string(&self, string: &mut String) {
80-
string.push_str(self.0.as_ref());
81-
}
82-
8383
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
8484
writer.write_all(self.0.as_bytes())
8585
}
@@ -210,6 +210,10 @@ impl Source for RawBufferSource {
210210
SourceValue::Buffer(Cow::Borrowed(&self.value))
211211
}
212212

213+
fn rope<'a>(&'a self, on_chunk: &mut dyn FnMut(&'a str)) {
214+
on_chunk(self.get_or_init_value_as_string())
215+
}
216+
213217
fn buffer(&self) -> Cow<[u8]> {
214218
Cow::Borrowed(&self.value)
215219
}
@@ -222,10 +226,6 @@ impl Source for RawBufferSource {
222226
None
223227
}
224228

225-
fn write_to_string(&self, string: &mut String) {
226-
string.push_str(self.get_or_init_value_as_string());
227-
}
228-
229229
fn to_writer(&self, writer: &mut dyn std::io::Write) -> std::io::Result<()> {
230230
writer.write_all(&self.value)
231231
}

0 commit comments

Comments
 (0)