Skip to content

Commit c5b3502

Browse files
authored
perf: replace source size (#193)
1 parent 42b4f9f commit c5b3502

File tree

4 files changed

+143
-2
lines changed

4 files changed

+143
-2
lines changed

benches/bench.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ use benchmark_repetitive_react_components::{
3030
benchmark_repetitive_react_components_source,
3131
};
3232

33+
use crate::bench_complex_replace_source::benchmark_complex_replace_source_size;
34+
3335
const HELLOWORLD_JS: &str = include_str!(concat!(
3436
env!("CARGO_MANIFEST_DIR"),
3537
"/benches/fixtures/transpile-minify/files/helloworld.js"
@@ -164,6 +166,11 @@ fn bench_rspack_sources(criterion: &mut Criterion) {
164166
benchmark_complex_replace_source_source,
165167
);
166168

169+
group.bench_function(
170+
"complex_replace_source_size",
171+
benchmark_complex_replace_source_size,
172+
);
173+
167174
group.bench_function(
168175
"parse_source_map_from_json",
169176
benchmark_parse_source_map_from_json,

benches/bench_complex_replace_source.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36735,3 +36735,11 @@ pub fn benchmark_complex_replace_source_source(b: &mut Bencher) {
3673536735
black_box(source.source());
3673636736
});
3673736737
}
36738+
36739+
pub fn benchmark_complex_replace_source_size(b: &mut Bencher) {
36740+
let source = LARGE_REPLACE_SOURCE.clone();
36741+
36742+
b.iter(|| {
36743+
black_box(source.size());
36744+
});
36745+
}

src/cached_source.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::{
1919
#[derive(Default)]
2020
struct CachedData {
2121
hash: OnceLock<u64>,
22+
size: OnceLock<usize>,
2223
line_only_map: OnceLock<Option<SourceMap>>,
2324
columns_map: OnceLock<Option<SourceMap>>,
2425
}
@@ -95,7 +96,7 @@ impl Source for CachedSource {
9596
}
9697

9798
fn size(&self) -> usize {
98-
self.inner.size()
99+
*self.cache.size.get_or_init(|| self.inner.size())
99100
}
100101

101102
fn map(&self, options: &MapOptions) -> Option<SourceMap> {

src/replace_source.rs

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,40 @@ impl Source for ReplaceSource {
255255
}
256256

257257
fn size(&self) -> usize {
258-
self.source().as_bytes().len()
258+
let inner_source_size = self.inner.size();
259+
let replacements = self.sorted_replacement();
260+
261+
if replacements.is_empty() {
262+
return inner_source_size;
263+
}
264+
265+
// Simulate the replacement process to calculate accurate size
266+
let mut size = inner_source_size;
267+
let mut inner_pos = 0u32;
268+
269+
for replacement in replacements.iter() {
270+
// Add original content before replacement
271+
if inner_pos < replacement.start {
272+
// This content is already counted in inner_source_size, so no change needed
273+
}
274+
275+
// Handle the replacement itself
276+
let original_length = replacement
277+
.end
278+
.saturating_sub(replacement.start.max(inner_pos))
279+
as usize;
280+
let replacement_length = replacement.content.len();
281+
282+
// Subtract original content length and add replacement content length
283+
size = size
284+
.saturating_sub(original_length)
285+
.saturating_add(replacement_length);
286+
287+
// Move position forward, handling overlaps
288+
inner_pos = inner_pos.max(replacement.end);
289+
}
290+
291+
size
259292
}
260293

261294
fn map(&self, options: &crate::MapOptions) -> Option<SourceMap> {
@@ -1267,4 +1300,96 @@ return <div>{data.foo}</div>
12671300
}"#
12681301
);
12691302
}
1303+
1304+
#[test]
1305+
fn size_matches_generated_content_len() {
1306+
let mut source = ReplaceSource::new(
1307+
RawStringSource::from_static("import { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\nimport React from 'react';\nimport Component__0 from './d0/f0.jsx';\n// import Component__1 from './d0/f1.jsx'\n// import Component__2 from './d0/f2.jsx'\n// import Component__3 from './d0/f3.jsx'\n// import Component__4 from './d0/f4.jsx'\n// import Component__5 from './d0/f5.jsx'\n// import Component__6 from './d0/f6.jsx'\n// import Component__7 from './d0/f7.jsx'\n// import Component__8 from './d0/f8.jsx'\nfunction Navbar(param) {\n var show = param.show;\n return /*#__PURE__*/ _jsxs(\"div\", {\n children: [\n /*#__PURE__*/ _jsx(Component__0, {}),\n /*#__PURE__*/ _jsx(Component__1, {}),\n /*#__PURE__*/ _jsx(Component__2, {}),\n /*#__PURE__*/ _jsx(Component__3, {}),\n /*#__PURE__*/ _jsx(Component__4, {}),\n /*#__PURE__*/ _jsx(Component__5, {}),\n /*#__PURE__*/ _jsx(Component__6, {}),\n /*#__PURE__*/ _jsx(Component__7, {}),\n /*#__PURE__*/ _jsx(Component__8, {})\n ]\n });\n}\nexport default Navbar;\n").boxed()
1308+
);
1309+
source.replace(0, 63, "", None);
1310+
source.replace(64, 90, "", None);
1311+
source.replace(91, 130, "", None);
1312+
source.replace(
1313+
544,
1314+
549,
1315+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)",
1316+
None,
1317+
);
1318+
source.replace(
1319+
605,
1320+
609,
1321+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1322+
None,
1323+
);
1324+
source.replace(
1325+
610,
1326+
622,
1327+
"_d0_f0_jsx__WEBPACK_IMPORTED_MODULE_2__[\"default\"]",
1328+
None,
1329+
);
1330+
source.replace(
1331+
655,
1332+
659,
1333+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1334+
None,
1335+
);
1336+
source.replace(
1337+
705,
1338+
709,
1339+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1340+
None,
1341+
);
1342+
source.replace(
1343+
755,
1344+
759,
1345+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1346+
None,
1347+
);
1348+
source.replace(
1349+
805,
1350+
809,
1351+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1352+
None,
1353+
);
1354+
source.replace(
1355+
855,
1356+
859,
1357+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1358+
None,
1359+
);
1360+
source.replace(
1361+
905,
1362+
909,
1363+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1364+
None,
1365+
);
1366+
source.replace(
1367+
955,
1368+
959,
1369+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1370+
None,
1371+
);
1372+
source.replace(
1373+
1005,
1374+
1009,
1375+
"(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)",
1376+
None,
1377+
);
1378+
source.replace(
1379+
1048,
1380+
1063,
1381+
"/* ESM default export */ const __WEBPACK_DEFAULT_EXPORT__ = (",
1382+
None,
1383+
);
1384+
source.replace(1048, 1063, "", None);
1385+
source.replace_with_enforce(
1386+
1069,
1387+
1070,
1388+
");",
1389+
None,
1390+
ReplacementEnforce::Post,
1391+
);
1392+
1393+
assert_eq!(source.size(), source.source().into_string_lossy().len());
1394+
}
12701395
}

0 commit comments

Comments
 (0)