Skip to content
/ rust Public
forked from rust-lang/rust

Commit 2494905

Browse files
Rollup merge of rust-lang#136848 - Shourya742:2025-02-11-add-docs-and-ut-for-util-cache, r=clubby789
add docs and ut for bootstrap util cache This PR adds doc and unit test for bootstrap utils/cache module
2 parents 993eb34 + 24150eb commit 2494905

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

src/bootstrap/src/utils/cache.rs

+46
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
//! This module helps you efficiently store and retrieve values using interning.
2+
//!
3+
//! Interning is a neat trick that keeps only one copy of identical values, saving memory
4+
//! and making comparisons super fast. Here, we provide the `Interned<T>` struct and the `Internable` trait
5+
//! to make interning easy for different data types.
6+
//!
7+
//! The `Interner` struct handles caching for common types like `String`, `PathBuf`, and `Vec<String>`,
8+
//! while the `Cache` struct acts as a write-once storage for linking computation steps with their results.
9+
//!
10+
//! # Thread Safety
11+
//!
12+
//! We use `Mutex` to make sure interning and retrieval are thread-safe. But keep in mind—once a value is
13+
//! interned, it sticks around for the entire lifetime of the program.
14+
115
use std::any::{Any, TypeId};
216
use std::borrow::Borrow;
317
use std::cell::RefCell;
@@ -12,6 +26,9 @@ use std::{fmt, mem};
1226

1327
use crate::core::builder::Step;
1428

29+
/// Represents an interned value of type `T`, allowing for efficient comparisons and retrieval.
30+
///
31+
/// This struct stores a unique index referencing the interned value within an internal cache.
1532
pub struct Interned<T>(usize, PhantomData<*const T>);
1633

1734
impl<T: Internable + Default> Default for Interned<T> {
@@ -111,6 +128,10 @@ impl<T: Internable + Ord> Ord for Interned<T> {
111128
}
112129
}
113130

131+
/// A structure for managing the interning of values of type `T`.
132+
///
133+
/// `TyIntern<T>` maintains a mapping between values and their interned representations,
134+
/// ensuring that duplicate values are not stored multiple times.
114135
struct TyIntern<T: Clone + Eq> {
115136
items: Vec<T>,
116137
set: HashMap<T, Interned<T>>,
@@ -123,6 +144,9 @@ impl<T: Hash + Clone + Eq> Default for TyIntern<T> {
123144
}
124145

125146
impl<T: Hash + Clone + Eq> TyIntern<T> {
147+
/// Interns a borrowed value, ensuring it is stored uniquely.
148+
///
149+
/// If the value has been previously interned, the same `Interned<T>` instance is returned.
126150
fn intern_borrow<B>(&mut self, item: &B) -> Interned<T>
127151
where
128152
B: Eq + Hash + ToOwned<Owned = T> + ?Sized,
@@ -138,6 +162,9 @@ impl<T: Hash + Clone + Eq> TyIntern<T> {
138162
interned
139163
}
140164

165+
/// Interns an owned value, storing it uniquely.
166+
///
167+
/// If the value has been previously interned, the existing `Interned<T>` is returned.
141168
fn intern(&mut self, item: T) -> Interned<T> {
142169
if let Some(i) = self.set.get(&item) {
143170
return *i;
@@ -148,18 +175,27 @@ impl<T: Hash + Clone + Eq> TyIntern<T> {
148175
interned
149176
}
150177

178+
/// Retrieves a reference to the interned value associated with the given `Interned<T>` instance.
151179
fn get(&self, i: Interned<T>) -> &T {
152180
&self.items[i.0]
153181
}
154182
}
155183

184+
/// A global interner for managing interned values of common types.
185+
///
186+
/// This structure maintains caches for `String`, `PathBuf`, and `Vec<String>`, ensuring efficient storage
187+
/// and retrieval of frequently used values.
156188
#[derive(Default)]
157189
pub struct Interner {
158190
strs: Mutex<TyIntern<String>>,
159191
paths: Mutex<TyIntern<PathBuf>>,
160192
lists: Mutex<TyIntern<Vec<String>>>,
161193
}
162194

195+
/// Defines the behavior required for a type to be internable.
196+
///
197+
/// Types implementing this trait must provide access to a static cache and define an `intern` method
198+
/// that ensures values are stored uniquely.
163199
trait Internable: Clone + Eq + Hash + 'static {
164200
fn intern_cache() -> &'static Mutex<TyIntern<Self>>;
165201

@@ -187,11 +223,15 @@ impl Internable for Vec<String> {
187223
}
188224

189225
impl Interner {
226+
/// Interns a string reference, ensuring it is stored uniquely.
227+
///
228+
/// If the string has been previously interned, the same `Interned<String>` instance is returned.
190229
pub fn intern_str(&self, s: &str) -> Interned<String> {
191230
self.strs.lock().unwrap().intern_borrow(s)
192231
}
193232
}
194233

234+
/// A global instance of `Interner` that caches common interned values.
195235
pub static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default);
196236

197237
/// This is essentially a `HashMap` which allows storing any type in its input and
@@ -209,10 +249,12 @@ pub struct Cache(
209249
);
210250

211251
impl Cache {
252+
/// Creates a new empty cache.
212253
pub fn new() -> Cache {
213254
Cache(RefCell::new(HashMap::new()))
214255
}
215256

257+
/// Stores the result of a computation step in the cache.
216258
pub fn put<S: Step>(&self, step: S, value: S::Output) {
217259
let mut cache = self.0.borrow_mut();
218260
let type_id = TypeId::of::<S>();
@@ -225,6 +267,7 @@ impl Cache {
225267
stepcache.insert(step, value);
226268
}
227269

270+
/// Retrieves a cached result for the given step, if available.
228271
pub fn get<S: Step>(&self, step: &S) -> Option<S::Output> {
229272
let mut cache = self.0.borrow_mut();
230273
let type_id = TypeId::of::<S>();
@@ -255,3 +298,6 @@ impl Cache {
255298
self.0.borrow().contains_key(&TypeId::of::<S>())
256299
}
257300
}
301+
302+
#[cfg(test)]
303+
mod tests;
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use std::path::PathBuf;
2+
3+
use crate::utils::cache::{INTERNER, Internable, TyIntern};
4+
5+
#[test]
6+
fn test_string_interning() {
7+
let s1 = INTERNER.intern_str("Hello");
8+
let s2 = INTERNER.intern_str("Hello");
9+
let s3 = INTERNER.intern_str("world");
10+
11+
assert_eq!(s1, s2, "Same strings should be interned to the same instance");
12+
assert_ne!(s1, s3, "Different strings should have different interned values");
13+
}
14+
15+
#[test]
16+
fn test_path_interning() {
17+
let p1 = PathBuf::from("/tmp/file").intern();
18+
let p2 = PathBuf::from("/tmp/file").intern();
19+
let p3 = PathBuf::from("/tmp/other").intern();
20+
21+
assert_eq!(p1, p2);
22+
assert_ne!(p1, p3);
23+
}
24+
25+
#[test]
26+
fn test_vec_interning() {
27+
let v1 = vec!["a".to_string(), "b".to_string()].intern();
28+
let v2 = vec!["a".to_string(), "b".to_string()].intern();
29+
let v3 = vec!["c".to_string()].intern();
30+
31+
assert_eq!(v1, v2);
32+
assert_ne!(v1, v3);
33+
}
34+
35+
#[test]
36+
fn test_interned_equality() {
37+
let s1 = INTERNER.intern_str("test");
38+
let s2 = INTERNER.intern_str("test");
39+
40+
assert_eq!(s1, s2);
41+
assert_eq!(s1, "test");
42+
}
43+
44+
#[test]
45+
fn test_ty_intern_intern_borrow() {
46+
let mut interner = TyIntern::default();
47+
let s1 = interner.intern_borrow("borrowed");
48+
let s2 = interner.intern("borrowed".to_string());
49+
50+
assert_eq!(s1, s2);
51+
assert_eq!(interner.get(s1), "borrowed");
52+
}

0 commit comments

Comments
 (0)