Skip to main content

priority_lfu/
builder.rs

1use crate::cache::Cache;
2use crate::lifecycle::{DefaultLifecycle, Lifecycle};
3
4/// Builder for configuring a Cache.
5///
6/// # Example
7///
8/// ```
9/// use priority_lfu::CacheBuilder;
10///
11/// let cache = CacheBuilder::new(1024 * 1024 * 512) // 512 MB
12///     .shards(128)
13///     .build();
14/// ```
15///
16/// # With Lifecycle Hooks
17///
18/// ```
19/// use std::any::Any;
20/// use priority_lfu::{CacheBuilder, Lifecycle};
21///
22/// struct MyLifecycle;
23///
24/// impl Lifecycle for MyLifecycle {
25///     fn on_evict(&self, key: &dyn Any) {
26///         println!("Entry evicted!");
27///     }
28/// }
29///
30/// let cache = CacheBuilder::new(1024 * 1024)
31///     .lifecycle(MyLifecycle)
32///     .build();
33/// ```
34///
35/// # Automatic Shard Scaling
36///
37/// By default, the cache uses up to 64 shards, but automatically scales down
38/// for smaller caches to ensure each shard has at least 4KB capacity. This
39/// prevents premature eviction due to uneven hash distribution.
40///
41/// - 256KB+ capacity: 64 shards (4KB+ per shard)
42/// - 64KB capacity: 16 shards (4KB per shard)
43/// - 4KB capacity: 1 shard (4KB per shard)
44///
45/// You can override this with [`shards()`], but the count may still be reduced
46/// if the capacity is too small to support the requested number.
47pub struct CacheBuilder<L: Lifecycle = DefaultLifecycle> {
48	max_size: usize,
49	shard_count: Option<usize>,
50	lifecycle: L,
51}
52
53impl CacheBuilder<DefaultLifecycle> {
54	/// Create a new builder with the given maximum size in bytes.
55	pub fn new(max_size_bytes: usize) -> Self {
56		Self {
57			max_size: max_size_bytes,
58			shard_count: None,
59			lifecycle: DefaultLifecycle,
60		}
61	}
62}
63
64impl<L: Lifecycle> CacheBuilder<L> {
65	/// Set the number of shards.
66	///
67	/// More shards reduce contention but increase memory overhead.
68	/// Will be rounded up to the next power of 2.
69	///
70	/// **Note**: The shard count may be reduced if the capacity is too small to
71	/// support the requested number of shards (minimum 4KB per shard). This prevents
72	/// premature eviction due to uneven hash distribution.
73	///
74	/// Default: up to 64 shards, scaled based on capacity
75	pub fn shards(mut self, count: usize) -> Self {
76		self.shard_count = Some(count);
77		self
78	}
79
80	/// Set the lifecycle hooks.
81	///
82	/// Lifecycle hooks are called when entries are evicted, removed, or cleared.
83	/// See [`Lifecycle`] for details.
84	///
85	/// # Example
86	///
87	/// ```
88	/// use std::any::Any;
89	/// use priority_lfu::{CacheBuilder, Lifecycle};
90	///
91	/// struct MyLifecycle;
92	///
93	/// impl Lifecycle for MyLifecycle {
94	///     fn on_evict(&self, _key: &dyn Any) {
95	///         println!("Entry evicted!");
96	///     }
97	/// }
98	///
99	/// let cache = CacheBuilder::new(1024)
100	///     .lifecycle(MyLifecycle)
101	///     .build();
102	/// ```
103	pub fn lifecycle<L2: Lifecycle>(self, lifecycle: L2) -> CacheBuilder<L2> {
104		CacheBuilder {
105			max_size: self.max_size,
106			shard_count: self.shard_count,
107			lifecycle,
108		}
109	}
110
111	/// Build the cache with the configured settings.
112	pub fn build(self) -> Cache<L> {
113		match self.shard_count {
114			Some(count) => Cache::with_shards_and_lifecycle(self.max_size, count, self.lifecycle),
115			None => Cache::with_lifecycle(self.max_size, self.lifecycle),
116		}
117	}
118}
119
120impl Default for CacheBuilder<DefaultLifecycle> {
121	/// Create a builder with default settings and 1GB capacity.
122	fn default() -> Self {
123		Self::new(1024 * 1024 * 1024) // 1 GB
124	}
125}
126
127#[cfg(test)]
128mod tests {
129	use super::*;
130
131	#[test]
132	fn test_builder_default() {
133		let cache = CacheBuilder::new(1024).build();
134		assert!(cache.is_empty());
135	}
136
137	#[test]
138	fn test_builder_with_shards() {
139		let cache = CacheBuilder::new(1024).shards(32).build();
140		assert!(cache.is_empty());
141	}
142
143	#[test]
144	fn test_builder_full_config() {
145		let cache = CacheBuilder::new(10240).shards(32).build();
146
147		assert!(cache.is_empty());
148		assert_eq!(cache.size(), 0);
149	}
150}