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