Skip to content

Commit 2b19331

Browse files
authored
refactor: reduce memory consumption of CachedSource (#144)
* refactor: init * perf: faster lines iterator for `Rope` (#145) * refactor: init * refactor: faster lines * refactor: try * chore: clippy * test: more * chore: more * perf * finish * finish
1 parent d85db3f commit 2b19331

15 files changed

+1731
-270
lines changed

Cargo.lock

Lines changed: 13 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ readme = "README.md"
1414
include = ["/src/**/*.rs", "/*.toml", "/LICENSE", "/README.md"]
1515

1616
[lints.rust]
17-
unsafe_code = "warn"
17+
unsafe_code = "warn"
1818
missing_docs = "warn"
1919

2020
[lints.clippy]
21-
dbg_macro = "warn"
22-
todo = "warn"
21+
dbg_macro = "warn"
22+
todo = "warn"
2323
unimplemented = "warn"
24-
print_stdout = "warn"
25-
print_stderr = "warn"
24+
print_stdout = "warn"
25+
print_stderr = "warn"
2626

2727
[[bench]]
28-
name = "bench"
28+
name = "bench"
2929
path = "benches/bench.rs"
3030
harness = false
3131

@@ -36,6 +36,8 @@ dyn-clone = "1"
3636
rustc-hash = "1"
3737
dashmap = "5"
3838
memchr = "2.6.4"
39+
itertools = "0.13"
40+
3941

4042
codspeed-criterion-compat = { version = "2.3.3", default-features = false, optional = true }
4143
static_assertions = "1.1.0"

src/cached_source.rs

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::{
1212
stream_and_get_source_and_map, stream_chunks_of_raw_source,
1313
stream_chunks_of_source_map, StreamChunks,
1414
},
15+
rope::Rope,
1516
MapOptions, Source, SourceMap,
1617
};
1718

@@ -50,8 +51,6 @@ use crate::{
5051
/// ```
5152
pub struct CachedSource<T> {
5253
inner: Arc<T>,
53-
cached_buffer: Arc<OnceLock<Vec<u8>>>,
54-
cached_source: Arc<OnceLock<Arc<str>>>,
5554
cached_hash: Arc<OnceLock<u64>>,
5655
cached_maps:
5756
Arc<DashMap<MapOptions, Option<SourceMap>, BuildHasherDefault<FxHasher>>>,
@@ -62,8 +61,6 @@ impl<T> CachedSource<T> {
6261
pub fn new(inner: T) -> Self {
6362
Self {
6463
inner: Arc::new(inner),
65-
cached_buffer: Default::default(),
66-
cached_source: Default::default(),
6764
cached_hash: Default::default(),
6865
cached_maps: Default::default(),
6966
}
@@ -77,17 +74,15 @@ impl<T> CachedSource<T> {
7774

7875
impl<T: Source + Hash + PartialEq + Eq + 'static> Source for CachedSource<T> {
7976
fn source(&self) -> Cow<str> {
80-
let cached = self
81-
.cached_source
82-
.get_or_init(|| self.inner.source().into());
83-
Cow::Borrowed(cached)
77+
self.inner.source()
78+
}
79+
80+
fn rope(&self) -> Rope<'_> {
81+
self.inner.rope()
8482
}
8583

8684
fn buffer(&self) -> Cow<[u8]> {
87-
let cached = self
88-
.cached_buffer
89-
.get_or_init(|| self.inner.buffer().to_vec());
90-
Cow::Borrowed(cached)
85+
self.inner.buffer()
9186
}
9287

9388
fn size(&self) -> usize {
@@ -109,7 +104,7 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> Source for CachedSource<T> {
109104
}
110105
}
111106

112-
impl<T: Source + Hash + PartialEq + Eq + 'static> StreamChunks<'_>
107+
impl<T: Source + Hash + PartialEq + Eq + 'static> StreamChunks
113108
for CachedSource<T>
114109
{
115110
fn stream_chunks<'a>(
@@ -122,9 +117,7 @@ impl<T: Source + Hash + PartialEq + Eq + 'static> StreamChunks<'_>
122117
let cached_map = self.cached_maps.entry(options.clone());
123118
match cached_map {
124119
Entry::Occupied(entry) => {
125-
let source = self
126-
.cached_source
127-
.get_or_init(|| self.inner.source().into());
120+
let source = self.rope();
128121
if let Some(map) = entry.get() {
129122
#[allow(unsafe_code)]
130123
// SAFETY: We guarantee that once a `SourceMap` is stored in the cache, it will never be removed.
@@ -162,8 +155,6 @@ impl<T> Clone for CachedSource<T> {
162155
fn clone(&self) -> Self {
163156
Self {
164157
inner: self.inner.clone(),
165-
cached_buffer: self.cached_buffer.clone(),
166-
cached_source: self.cached_source.clone(),
167158
cached_hash: self.cached_hash.clone(),
168159
cached_maps: self.cached_maps.clone(),
169160
}
@@ -196,17 +187,14 @@ impl<T: std::fmt::Debug> std::fmt::Debug for CachedSource<T> {
196187
) -> Result<(), std::fmt::Error> {
197188
f.debug_struct("CachedSource")
198189
.field("inner", self.inner.as_ref())
199-
.field("cached_buffer", &self.cached_buffer.get().is_some())
200-
.field("cached_source", &self.cached_source.get().is_some())
190+
.field("cached_hash", self.cached_hash.as_ref())
201191
.field("cached_maps", &(!self.cached_maps.is_empty()))
202192
.finish()
203193
}
204194
}
205195

206196
#[cfg(test)]
207197
mod tests {
208-
use std::borrow::Borrow;
209-
210198
use crate::{
211199
ConcatSource, OriginalSource, RawSource, ReplaceSource, SourceExt,
212200
SourceMapSource, WithoutOriginalOptions,
@@ -247,11 +235,6 @@ mod tests {
247235
source.size();
248236
source.map(&map_options);
249237

250-
assert_eq!(clone.cached_source.get().unwrap().borrow(), source.source());
251-
assert_eq!(
252-
*clone.cached_buffer.get().unwrap(),
253-
source.buffer().to_vec()
254-
);
255238
assert_eq!(
256239
*clone.cached_maps.get(&map_options).unwrap().value(),
257240
source.map(&map_options)

src/concat_source.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
helpers::{get_map, GeneratedInfo, OnChunk, OnName, OnSource, StreamChunks},
1111
linear_map::LinearMap,
1212
source::{Mapping, OriginalLocation},
13-
BoxSource, MapOptions, Source, SourceExt, SourceMap,
13+
BoxSource, MapOptions, Rope, Source, SourceExt, SourceMap,
1414
};
1515

1616
/// Concatenate multiple [Source]s to a single [Source].
@@ -109,6 +109,20 @@ impl Source for ConcatSource {
109109
}
110110
}
111111

112+
fn rope(&self) -> Rope<'_> {
113+
let children = self.children();
114+
if children.len() == 1 {
115+
children[0].rope()
116+
} else {
117+
let mut rope = Rope::new();
118+
for child in children {
119+
let child_rope = child.rope();
120+
rope.append(child_rope);
121+
}
122+
rope
123+
}
124+
}
125+
112126
fn buffer(&self) -> Cow<[u8]> {
113127
let children = self.children();
114128
if children.len() == 1 {
@@ -155,8 +169,8 @@ impl PartialEq for ConcatSource {
155169
}
156170
impl Eq for ConcatSource {}
157171

158-
impl<'a> StreamChunks<'a> for ConcatSource {
159-
fn stream_chunks(
172+
impl StreamChunks for ConcatSource {
173+
fn stream_chunks<'a>(
160174
&'a self,
161175
options: &MapOptions,
162176
on_chunk: OnChunk<'_, 'a>,

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@ pub type Result<T> = result::Result<T, Error>;
88
pub enum Error {
99
/// a JSON parsing related failure
1010
BadJson(simd_json::Error),
11+
/// rope related failure
12+
Rope(&'static str),
1113
}
1214

1315
impl fmt::Display for Error {
1416
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1517
match self {
1618
Error::BadJson(err) => write!(f, "bad json: {err}"),
19+
Error::Rope(err) => write!(f, "rope error: {err}"),
1720
}
1821
}
1922
}

0 commit comments

Comments
 (0)