1use parking_lot::Mutex;
12
13use crate::prefix_cache::{EvictedBlocks, PrefixCache, PrefixMatch};
14
15mod inner;
16mod snapshot;
17mod snapshot_insert;
18mod snapshot_stats;
19mod snapshot_tier;
20
21#[cfg(test)]
22mod tests;
23
24use inner::RadixTreeInner;
25use snapshot::SsmSnapshotIndex;
26
27pub(crate) fn hash_token_prefix(tokens: &[u32], count: usize, adapter_id: u64) -> u64 {
37 let mut h: u64 = 0xcbf29ce484222325; if adapter_id != 0 {
39 h ^= adapter_id;
40 h = h.wrapping_mul(0x100000001b3);
41 }
42 for &t in &tokens[..count] {
43 h ^= t as u64;
44 h = h.wrapping_mul(0x100000001b3);
45 }
46 h
47}
48
49pub struct RadixTree {
55 inner: Mutex<RadixTreeInner>,
56 snapshot_index: Mutex<SsmSnapshotIndex>,
57}
58
59impl Default for RadixTree {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65impl RadixTree {
66 pub fn new() -> Self {
67 Self {
68 inner: Mutex::new(RadixTreeInner::new()),
69 snapshot_index: Mutex::new(SsmSnapshotIndex::new()),
70 }
71 }
72}
73
74impl PrefixCache for RadixTree {
75 fn lookup(
76 &self,
77 tokens: &[u32],
78 block_size: usize,
79 session_hash: u64,
80 adapter_id: u64,
81 ) -> PrefixMatch {
82 let (matched_blocks, matched_disk_block_ids, matched_tokens) = {
84 let mut inner = self.inner.lock();
85 let (blocks, disk, matched) = inner.walk(tokens, block_size, adapter_id);
86 if matched > 0 {
87 inner.inc_refs(tokens, block_size, matched, adapter_id);
88 crate::prefix_cache::record_cache_hit(matched);
89 } else {
90 crate::prefix_cache::record_cache_miss();
91 }
92 (blocks, disk, matched)
93 };
94 let mut ssm_snapshot = None;
101 let mut ssm_snapshot_tokens = 0;
102 let mut ssm_snapshot_tier_key = None;
103 let mut ssm_snapshot_tier_tokens = 0;
104 let mut ssm_snapshot_is_tail = false;
105 if matched_tokens > 0 {
106 let mut idx = self.snapshot_index.lock();
107 if let Some(m) = idx.lookup_tiered(tokens, matched_tokens, session_hash, adapter_id) {
108 ssm_snapshot_is_tail = m.is_tail;
109 match m.loc {
110 snapshot::SnapLoc::Hbm(slot) => {
111 ssm_snapshot = Some(slot);
112 ssm_snapshot_tokens = m.token_count;
113 }
114 snapshot::SnapLoc::Tier(key) => {
115 ssm_snapshot_tier_key = Some(key);
116 ssm_snapshot_tier_tokens = m.token_count;
117 }
118 }
119 }
120 }
121 let matched_disk_block_ids = if matched_disk_block_ids.iter().all(|&id| id == u32::MAX) {
126 Vec::new()
127 } else {
128 matched_disk_block_ids
129 };
130 PrefixMatch {
131 matched_blocks,
132 matched_disk_block_ids,
133 matched_tokens,
134 ssm_snapshot,
135 ssm_snapshot_tokens,
136 ssm_snapshot_tier_key,
137 ssm_snapshot_tier_tokens,
138 ssm_snapshot_is_tail,
139 }
140 }
141
142 fn peek_matched_tokens(&self, tokens: &[u32], block_size: usize, adapter_id: u64) -> usize {
143 self.inner.lock().walk(tokens, block_size, adapter_id).2
144 }
145
146 fn insert(
147 &self,
148 tokens: &[u32],
149 block_table: &[u32],
150 disk_block_ids: &[u32],
151 block_size: usize,
152 matched_tokens: usize,
153 adapter_id: u64,
154 ) -> crate::prefix_cache::InsertAcquired {
155 self.inner.lock().insert(
156 tokens,
157 block_table,
158 disk_block_ids,
159 block_size,
160 matched_tokens,
161 adapter_id,
162 )
163 }
164
165 fn insert_with_snapshot(
166 &self,
167 tokens: &[u32],
168 block_table: &[u32],
169 disk_block_ids: &[u32],
170 block_size: usize,
171 snapshot_id: usize,
172 session_hash: u64,
173 matched_tokens: usize,
174 adapter_id: u64,
175 ) -> (Option<usize>, crate::prefix_cache::InsertAcquired) {
176 let newly_acquired = self.inner.lock().insert(
178 tokens,
179 block_table,
180 disk_block_ids,
181 block_size,
182 matched_tokens,
183 adapter_id,
184 );
185 let prefix_hash = hash_token_prefix(tokens, tokens.len(), adapter_id);
187 let mut idx = self.snapshot_index.lock();
188 let displaced = idx.insert(prefix_hash, snapshot_id, session_hash, tokens.len());
189 (displaced, newly_acquired)
190 }
191
192 fn insert_tail_snapshot(
193 &self,
194 tokens: &[u32],
195 snapshot_id: usize,
196 session_hash: u64,
197 adapter_id: u64,
198 ) -> Vec<usize> {
199 let prefix_hash = hash_token_prefix(tokens, tokens.len(), adapter_id);
203 self.snapshot_index
204 .lock()
205 .insert_tail(prefix_hash, snapshot_id, session_hash, tokens.len())
206 }
207
208 fn insert_tail_sibling_snapshot(
209 &self,
210 tokens: &[u32],
211 snapshot_id: usize,
212 session_hash: u64,
213 adapter_id: u64,
214 ) -> Option<usize> {
215 let prefix_hash = hash_token_prefix(tokens, tokens.len(), adapter_id);
217 self.snapshot_index.lock().insert_tail_sibling(
218 prefix_hash,
219 snapshot_id,
220 session_hash,
221 tokens.len(),
222 )
223 }
224
225 fn insert_intermediate_snapshot(
226 &self,
227 tokens: &[u32],
228 _block_table: &[u32],
229 _disk_block_ids: &[u32],
230 _block_size: usize,
231 snapshot_id: usize,
232 session_hash: u64,
233 _matched_tokens: usize,
234 adapter_id: u64,
235 ) -> Option<usize> {
236 let prefix_hash = hash_token_prefix(tokens, tokens.len(), adapter_id);
240 let mut idx = self.snapshot_index.lock();
241 idx.insert(prefix_hash, snapshot_id, session_hash, tokens.len())
242 }
243
244 fn release(&self, tokens: &[u32], block_size: usize, adapter_id: u64) {
245 self.inner
246 .lock()
247 .dec_refs(tokens, block_size, tokens.len(), adapter_id);
248 }
249
250 fn release_matched(
251 &self,
252 tokens: &[u32],
253 block_size: usize,
254 matched_tokens: usize,
255 adapter_id: u64,
256 ) {
257 self.inner
258 .lock()
259 .dec_refs(tokens, block_size, matched_tokens, adapter_id);
260 }
261
262 fn evict(&self, num_blocks: usize) -> EvictedBlocks {
263 let (physical, disk) = self.inner.lock().evict(num_blocks);
264 let disk_block_ids: Vec<u32> = disk.into_iter().filter(|&id| id != u32::MAX).collect();
267 EvictedBlocks {
268 physical,
269 disk_block_ids,
270 }
271 }
272
273 fn evict_snapshot_lru(&self) -> Option<usize> {
274 self.snapshot_index.lock().evict_lru()
275 }
276
277 fn evict_snapshot_to_tier(&self, min_tokens: usize) -> Option<crate::prefix_cache::TierEvict> {
278 self.snapshot_index.lock().evict_to_tier(min_tokens)
279 }
280
281 fn promote_snapshot(&self, key: u64, new_slot: usize) -> bool {
282 self.snapshot_index.lock().promote(key, new_slot)
283 }
284
285 fn forget_snapshot_tier_key(&self, key: u64) -> bool {
286 self.snapshot_index.lock().forget_tiered(key)
287 }
288
289 fn snapshot_count(&self) -> usize {
290 self.snapshot_index.lock().len()
291 }
292
293 fn stats(&self) -> (usize, usize) {
294 let inner = self.inner.lock();
295 let entries = inner.num_entries();
296 (entries, entries)
297 }
298}