1use std::{
4 collections::{BTreeMap, BTreeSet},
5 num::NonZeroUsize,
6 ops::{Bound, Range},
7 sync::Arc,
8};
9
10use bytes::Bytes;
11use parking_lot::Mutex;
12
13use crate::RangeError;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub enum CacheCapacity {
18 Bounded(NonZeroUsize),
20 Unbounded,
22}
23
24#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26pub enum InsertOutcome {
27 Inserted,
29 AlreadyCovered,
31 TooLarge,
33}
34
35#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
37pub struct Invalidation {
38 pub ranges: usize,
40 pub bytes: usize,
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub struct CacheSnapshot {
47 pub capacity: CacheCapacity,
49 pub resident_bytes: usize,
51 pub keys: usize,
53 pub ranges: usize,
55 pub hits: u64,
57 pub partial_hits: u64,
59 pub misses: u64,
61 pub insertions: u64,
63 pub admissions_rejected_too_large: u64,
65 pub evictions: u64,
67}
68
69struct CacheBlock {
70 end: usize,
71 bytes: Bytes,
72 last_access: u64,
73}
74
75#[derive(Default)]
76struct Statistics {
77 hits: u64,
78 partial_hits: u64,
79 misses: u64,
80 insertions: u64,
81 admissions_rejected_too_large: u64,
82 evictions: u64,
83}
84
85struct State<K> {
86 ranges: BTreeMap<K, BTreeMap<usize, CacheBlock>>,
87 lru: BTreeSet<(u64, K, usize)>,
88 resident_bytes: usize,
89 next_access: u64,
90 statistics: Statistics,
91}
92
93impl<K> Default for State<K> {
94 fn default() -> Self {
95 Self {
96 ranges: BTreeMap::new(),
97 lru: BTreeSet::new(),
98 resident_bytes: 0,
99 next_access: 0,
100 statistics: Statistics::default(),
101 }
102 }
103}
104
105impl<K: Ord + Clone> State<K> {
106 fn take_access(&mut self) -> u64 {
107 let access = self.next_access;
108 self.next_access = self
109 .next_access
110 .checked_add(1)
111 .expect("range cache LRU clock exhausted");
112 access
113 }
114
115 fn touch(&mut self, key: &K, start: usize) {
116 let Some(previous) = self
117 .ranges
118 .get(key)
119 .and_then(|ranges| ranges.get(&start))
120 .map(|block| block.last_access)
121 else {
122 return;
123 };
124
125 assert!(
126 self.lru.remove(&(previous, key.clone(), start)),
127 "resident range must have an LRU entry"
128 );
129 let next = self.take_access();
130 self.ranges
131 .get_mut(key)
132 .and_then(|ranges| ranges.get_mut(&start))
133 .expect("touched range remains resident")
134 .last_access = next;
135 assert!(
136 self.lru.insert((next, key.clone(), start)),
137 "LRU access value is unique"
138 );
139 }
140
141 fn remove(&mut self, key: &K, start: usize) -> Option<CacheBlock> {
142 let (block, key_is_empty) = {
143 let ranges = self.ranges.get_mut(key)?;
144 let block = ranges.remove(&start)?;
145 (block, ranges.is_empty())
146 };
147
148 if key_is_empty {
149 self.ranges.remove(key);
150 }
151 assert!(
152 self.lru.remove(&(block.last_access, key.clone(), start)),
153 "removed range must have an LRU entry"
154 );
155 self.resident_bytes = self
156 .resident_bytes
157 .checked_sub(block.bytes.len())
158 .expect("resident byte accounting cannot underflow");
159 Some(block)
160 }
161
162 fn covered(&self, key: &K, requested: &Range<usize>) -> Vec<(usize, Range<usize>)> {
163 let Some(ranges) = self.ranges.get(key) else {
164 return Vec::new();
165 };
166
167 let mut covered = Vec::new();
168 match ranges.range(..=requested.start).next_back() {
169 Some((&start, block)) if block.end > requested.start => {
170 covered.push((start, requested.start..block.end.min(requested.end)));
171 }
172 Some(_) | None => {}
173 }
174
175 covered.extend(
176 ranges
177 .range((
178 Bound::Excluded(requested.start),
179 Bound::Excluded(requested.end),
180 ))
181 .map(|(&start, block)| {
182 (
183 start,
184 start.max(requested.start)..block.end.min(requested.end),
185 )
186 }),
187 );
188 covered
189 }
190}
191
192pub struct RangeCache<K> {
194 inner: Arc<Mutex<State<K>>>,
195 capacity: CacheCapacity,
196}
197
198impl<K> Clone for RangeCache<K> {
199 fn clone(&self) -> Self {
200 Self {
201 inner: Arc::clone(&self.inner),
202 capacity: self.capacity,
203 }
204 }
205}
206
207impl<K> RangeCache<K> {
208 #[must_use]
210 pub fn new(capacity: CacheCapacity) -> Self {
211 Self {
212 inner: Arc::new(Mutex::new(State::default())),
213 capacity,
214 }
215 }
216
217 #[must_use]
219 pub const fn capacity(&self) -> CacheCapacity {
220 self.capacity
221 }
222}
223
224impl<K: Ord + Clone> RangeCache<K> {
225 pub fn get(&self, key: &K, range: Range<usize>) -> Result<Option<Bytes>, RangeError> {
234 validate_range(&range)?;
235 let mut state = self.inner.lock();
236
237 if range.is_empty() {
238 state.statistics.hits += 1;
239 return Ok(Some(Bytes::new()));
240 }
241
242 let hit = state.ranges.get(key).and_then(|ranges| {
243 ranges
244 .range(..=range.start)
245 .next_back()
246 .filter(|(_, block)| range.end <= block.end)
247 .map(|(&start, block)| {
248 let offset = range.start - start;
249 (start, block.bytes.slice(offset..offset + range.len()))
250 })
251 });
252
253 if let Some((start, bytes)) = hit {
254 state.statistics.hits += 1;
255 state.touch(key, start);
256 return Ok(Some(bytes));
257 }
258
259 let covered = state.covered(key, &range);
260 if covered.is_empty() {
261 state.statistics.misses += 1;
262 } else {
263 state.statistics.partial_hits += 1;
264 for (start, _) in covered {
265 state.touch(key, start);
266 }
267 }
268 Ok(None)
269 }
270
271 pub fn missing_ranges(
277 &self,
278 key: &K,
279 range: Range<usize>,
280 ) -> Result<Vec<Range<usize>>, RangeError> {
281 validate_range(&range)?;
282 if range.is_empty() {
283 return Ok(Vec::new());
284 }
285
286 let state = self.inner.lock();
287 let covered = state.covered(key, &range);
288 Ok(missing_ranges(&range, &covered))
289 }
290
291 pub fn insert(
308 &self,
309 key: K,
310 range: Range<usize>,
311 bytes: Bytes,
312 ) -> Result<InsertOutcome, RangeError> {
313 validate_range(&range)?;
314 let expected = range.len();
315 if bytes.len() != expected {
316 return Err(RangeError::PayloadLengthMismatch {
317 range,
318 expected,
319 actual: bytes.len(),
320 });
321 }
322 if range.is_empty() {
323 return Ok(InsertOutcome::AlreadyCovered);
324 }
325
326 let mut state = self.inner.lock();
327 let containing = state.ranges.get(&key).and_then(|ranges| {
328 ranges
329 .range(..=range.start)
330 .next_back()
331 .filter(|(_, block)| range.end <= block.end)
332 });
333 if containing.is_some() {
334 return Ok(InsertOutcome::AlreadyCovered);
335 }
336
337 let mut merged_start = range.start;
338 let mut merged_end = range.end;
339 let mut affected = Vec::new();
340 if let Some(ranges) = state.ranges.get(&key) {
341 for (&start, block) in ranges {
342 if block.end < merged_start {
343 continue;
344 }
345 if start > merged_end {
346 break;
347 }
348 merged_start = merged_start.min(start);
349 merged_end = merged_end.max(block.end);
350 affected.push((start, block.end, block.bytes.clone()));
351 }
352 }
353
354 let merged_length = merged_end - merged_start;
355 match self.capacity {
356 CacheCapacity::Bounded(capacity) if merged_length > capacity.get() => {
357 state.statistics.admissions_rejected_too_large += 1;
358 return Ok(InsertOutcome::TooLarge);
359 }
360 CacheCapacity::Bounded(_) | CacheCapacity::Unbounded => {}
361 }
362
363 let merged_bytes =
364 if affected.is_empty() || (merged_start == range.start && merged_end == range.end) {
365 bytes
366 } else {
367 let mut merged = vec![0; merged_length];
368 for (start, end, cached) in &affected {
369 let offset = start - merged_start;
370 merged[offset..offset + (end - start)].copy_from_slice(cached);
371 }
372 let offset = range.start - merged_start;
373 merged[offset..offset + expected].copy_from_slice(&bytes);
374 Bytes::from(merged)
375 };
376
377 for (start, _, _) in affected {
378 state
379 .remove(&key, start)
380 .expect("affected range remains resident");
381 }
382
383 let access = state.take_access();
384 assert!(
385 state.lru.insert((access, key.clone(), merged_start)),
386 "new range has a unique LRU entry"
387 );
388 let previous = state.ranges.entry(key).or_default().insert(
389 merged_start,
390 CacheBlock {
391 end: merged_end,
392 bytes: merged_bytes,
393 last_access: access,
394 },
395 );
396 assert!(previous.is_none(), "merged range start must be vacant");
397 state.resident_bytes += merged_length;
398 state.statistics.insertions += 1;
399
400 if let CacheCapacity::Bounded(capacity) = self.capacity {
401 while state.resident_bytes > capacity.get() {
402 let (_, oldest_key, oldest_start) = state
403 .lru
404 .first()
405 .cloned()
406 .expect("resident bytes require an LRU entry");
407 state
408 .remove(&oldest_key, oldest_start)
409 .expect("LRU range remains resident");
410 state.statistics.evictions += 1;
411 }
412 }
413
414 Ok(InsertOutcome::Inserted)
415 }
416
417 #[must_use]
424 pub fn invalidate(&self, key: &K) -> Invalidation {
425 let mut state = self.inner.lock();
426 let starts = state
427 .ranges
428 .get(key)
429 .map(|ranges| ranges.keys().copied().collect::<Vec<_>>())
430 .unwrap_or_default();
431 let mut invalidation = Invalidation::default();
432 for start in starts {
433 let block = state
434 .remove(key, start)
435 .expect("invalidated range remains resident");
436 invalidation.ranges += 1;
437 invalidation.bytes += block.bytes.len();
438 }
439 invalidation
440 }
441
442 #[must_use]
444 pub fn clear(&self) -> Invalidation {
445 let mut state = self.inner.lock();
446 let invalidation = Invalidation {
447 ranges: state.lru.len(),
448 bytes: state.resident_bytes,
449 };
450 state.ranges.clear();
451 state.lru.clear();
452 state.resident_bytes = 0;
453 invalidation
454 }
455
456 #[must_use]
458 pub fn snapshot(&self) -> CacheSnapshot {
459 let state = self.inner.lock();
460 CacheSnapshot {
461 capacity: self.capacity,
462 resident_bytes: state.resident_bytes,
463 keys: state.ranges.len(),
464 ranges: state.lru.len(),
465 hits: state.statistics.hits,
466 partial_hits: state.statistics.partial_hits,
467 misses: state.statistics.misses,
468 insertions: state.statistics.insertions,
469 admissions_rejected_too_large: state.statistics.admissions_rejected_too_large,
470 evictions: state.statistics.evictions,
471 }
472 }
473
474 #[cfg(feature = "async")]
475 pub(crate) fn read_plan(&self, key: &K, range: Range<usize>) -> Result<ReadPlan, RangeError> {
476 validate_range(&range)?;
477 let mut state = self.inner.lock();
478 if range.is_empty() {
479 state.statistics.hits += 1;
480 return Ok(ReadPlan::Complete(Bytes::new()));
481 }
482
483 let hit = state.ranges.get(key).and_then(|ranges| {
484 ranges
485 .range(..=range.start)
486 .next_back()
487 .filter(|(_, block)| range.end <= block.end)
488 .map(|(&start, block)| {
489 let offset = range.start - start;
490 (start, block.bytes.slice(offset..offset + range.len()))
491 })
492 });
493 if let Some((start, bytes)) = hit {
494 state.statistics.hits += 1;
495 state.touch(key, start);
496 return Ok(ReadPlan::Complete(bytes));
497 }
498
499 let covered = state.covered(key, &range);
500 let missing = missing_ranges(&range, &covered);
501 let cached = covered
502 .iter()
503 .map(|(start, covered_range)| {
504 let block = state
505 .ranges
506 .get(key)
507 .and_then(|ranges| ranges.get(start))
508 .expect("covered range remains resident");
509 let offset = covered_range.start - start;
510 (
511 covered_range.clone(),
512 block.bytes.slice(offset..offset + covered_range.len()),
513 )
514 })
515 .collect();
516
517 if covered.is_empty() {
518 state.statistics.misses += 1;
519 } else {
520 state.statistics.partial_hits += 1;
521 for (start, _) in covered {
522 state.touch(key, start);
523 }
524 }
525 Ok(ReadPlan::Fetch { cached, missing })
526 }
527}
528
529fn validate_range(range: &Range<usize>) -> Result<(), RangeError> {
530 if range.start > range.end {
531 return Err(RangeError::ReversedRange {
532 start: range.start,
533 end: range.end,
534 });
535 }
536 Ok(())
537}
538
539fn missing_ranges(
540 requested: &Range<usize>,
541 covered: &[(usize, Range<usize>)],
542) -> Vec<Range<usize>> {
543 let mut missing = Vec::new();
544 let mut cursor = requested.start;
545 for (_, range) in covered {
546 if cursor < range.start {
547 missing.push(cursor..range.start);
548 }
549 cursor = cursor.max(range.end);
550 }
551 if cursor < requested.end {
552 missing.push(cursor..requested.end);
553 }
554 missing
555}
556
557#[cfg(feature = "async")]
558pub(crate) enum ReadPlan {
559 Complete(Bytes),
560 Fetch {
561 cached: Vec<(Range<usize>, Bytes)>,
562 missing: Vec<Range<usize>>,
563 },
564}