Skip to main content

fs_core/
caching_device.rs

1//! Small LRU read-cache decorator. Caches only block-aligned, block-sized
2//! reads; everything else passes through. Writes invalidate any overlapping
3//! cached entries.
4
5use crate::block::{BlockDevice, BlockRead};
6use crate::error::Result;
7use std::collections::VecDeque;
8use std::sync::{Arc, Mutex};
9
10/// LRU read-cache wrapper. Wraps any `BlockDevice` (writable or not).
11pub struct CachingDevice {
12    inner: Arc<dyn BlockDevice>,
13    block_size: u64,
14    state: Mutex<CacheState>,
15}
16
17struct CacheState {
18    /// Fixed-capacity LRU; head is most-recently used.
19    entries: VecDeque<(u64, Arc<Vec<u8>>)>,
20    capacity: usize,
21    hits: u64,
22    misses: u64,
23}
24
25impl CachingDevice {
26    pub fn new(inner: Arc<dyn BlockDevice>, block_size: u64, capacity: usize) -> Arc<Self> {
27        Arc::new(Self {
28            inner,
29            block_size,
30            state: Mutex::new(CacheState {
31                entries: VecDeque::with_capacity(capacity),
32                capacity,
33                hits: 0,
34                misses: 0,
35            }),
36        })
37    }
38
39    pub fn stats(&self) -> (u64, u64) {
40        let s = self.state.lock().unwrap();
41        (s.hits, s.misses)
42    }
43
44    pub fn invalidate_all(&self) {
45        let mut s = self.state.lock().unwrap();
46        s.entries.clear();
47    }
48
49    fn invalidate_range(state: &mut CacheState, start: u64, end: u64, block_size: u64) {
50        state.entries.retain(|(off, _)| {
51            let block_end = off.saturating_add(block_size);
52            *off >= end || block_end <= start
53        });
54    }
55}
56
57impl BlockRead for CachingDevice {
58    fn read_at(&self, offset: u64, buf: &mut [u8]) -> Result<()> {
59        let cacheable =
60            buf.len() as u64 == self.block_size && offset.is_multiple_of(self.block_size);
61        if !cacheable {
62            return self.inner.read_at(offset, buf);
63        }
64
65        {
66            let mut s = self.state.lock().unwrap();
67            if let Some(pos) = s.entries.iter().position(|(o, _)| *o == offset) {
68                let entry = s.entries.remove(pos).unwrap();
69                buf.copy_from_slice(&entry.1);
70                s.entries.push_front(entry);
71                s.hits += 1;
72                return Ok(());
73            }
74            s.misses += 1;
75        }
76
77        self.inner.read_at(offset, buf)?;
78        let data = Arc::new(buf.to_vec());
79        let mut s = self.state.lock().unwrap();
80        if s.entries.len() >= s.capacity {
81            s.entries.pop_back();
82        }
83        s.entries.push_front((offset, data));
84        Ok(())
85    }
86
87    fn size_bytes(&self) -> u64 {
88        self.inner.size_bytes()
89    }
90}
91
92impl BlockDevice for CachingDevice {
93    fn write_at(&self, offset: u64, buf: &[u8]) -> Result<()> {
94        let end = offset.saturating_add(buf.len() as u64);
95        {
96            let mut s = self.state.lock().unwrap();
97            let bs = self.block_size;
98            Self::invalidate_range(&mut s, offset, end, bs);
99        }
100        self.inner.write_at(offset, buf)
101    }
102
103    fn flush(&self) -> Result<()> {
104        self.inner.flush()
105    }
106
107    fn is_writable(&self) -> bool {
108        self.inner.is_writable()
109    }
110}