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