mtp_mount/
sparse_cache.rs1use std::io::{self, Read as _, Seek as _, SeekFrom, Write as _};
13use std::ops::Range;
14
15#[derive(Debug)]
17pub struct SparseCache {
18 file: std::fs::File,
19 ranges: Vec<Range<u64>>,
21 total_size: u64,
22}
23
24impl SparseCache {
25 pub fn new(total_size: u64) -> io::Result<Self> {
30 let file = tempfile::tempfile()?;
31 file.set_len(total_size)?;
32 Ok(Self {
33 file,
34 ranges: Vec::new(),
35 total_size,
36 })
37 }
38
39 pub fn missing_ranges(&self, offset: u64, size: u64) -> Vec<Range<u64>> {
44 let end = offset.saturating_add(size).min(self.total_size);
45 if offset >= end {
46 return Vec::new();
47 }
48
49 let mut missing = Vec::new();
50 let mut cursor = offset;
51
52 for populated in &self.ranges {
53 if populated.end <= cursor {
54 continue;
55 }
56 if populated.start >= end {
57 break;
58 }
59 if populated.start > cursor {
60 missing.push(cursor..populated.start.min(end));
61 }
62 cursor = populated.end;
63 if cursor >= end {
64 break;
65 }
66 }
67
68 if cursor < end {
69 missing.push(cursor..end);
70 }
71
72 missing
73 }
74
75 pub fn write_at(&mut self, offset: u64, data: &[u8]) -> io::Result<()> {
77 if data.is_empty() {
78 return Ok(());
79 }
80 self.file.seek(SeekFrom::Start(offset))?;
81 self.file.write_all(data)?;
82
83 let new_range = offset..offset + data.len() as u64;
84 self.insert_range(new_range);
85 Ok(())
86 }
87
88 pub fn read_at(&mut self, offset: u64, size: u64) -> io::Result<Vec<u8>> {
94 if offset >= self.total_size {
95 return Ok(Vec::new());
96 }
97 let read_len = size.min(self.total_size - offset) as usize;
98 let mut buf = vec![0u8; read_len];
99 self.file.seek(SeekFrom::Start(offset))?;
100 self.file.read_exact(&mut buf)?;
101 Ok(buf)
102 }
103
104 fn insert_range(&mut self, new: Range<u64>) {
107 let mut start = new.start;
108 let mut end = new.end;
109
110 self.ranges.retain(|r| {
112 if r.end < start || r.start > end {
113 true
114 } else {
115 start = start.min(r.start);
116 end = end.max(r.end);
117 false
118 }
119 });
120
121 let pos = self
123 .ranges
124 .binary_search_by(|r| r.start.cmp(&start))
125 .unwrap_or_else(|p| p);
126 self.ranges.insert(pos, start..end);
127 }
128
129 #[cfg(test)]
130 pub fn populated_ranges(&self) -> &[Range<u64>] {
131 &self.ranges
132 }
133}
134
135#[cfg(test)]
136#[allow(clippy::single_range_in_vec_init)] mod tests {
138 use super::*;
139
140 #[test]
141 fn missing_ranges_empty_cache() {
142 let cache = SparseCache::new(1000).unwrap();
143 assert_eq!(cache.missing_ranges(0, 100), vec![0..100]);
144 assert_eq!(cache.missing_ranges(500, 100), vec![500..600]);
145 }
146
147 #[test]
148 fn missing_ranges_full_hit() {
149 let mut cache = SparseCache::new(1000).unwrap();
150 cache.write_at(0, &[0u8; 500]).unwrap();
151 assert_eq!(cache.missing_ranges(100, 200), Vec::<Range<u64>>::new());
152 assert_eq!(cache.missing_ranges(0, 500), Vec::<Range<u64>>::new());
153 }
154
155 #[test]
156 fn missing_ranges_partial_hit_at_start() {
157 let mut cache = SparseCache::new(1000).unwrap();
158 cache.write_at(0, &[0u8; 100]).unwrap();
160 assert_eq!(cache.missing_ranges(0, 200), vec![100..200]);
162 }
163
164 #[test]
165 fn missing_ranges_partial_hit_at_end() {
166 let mut cache = SparseCache::new(1000).unwrap();
167 cache.write_at(100, &[0u8; 100]).unwrap();
169 assert_eq!(cache.missing_ranges(0, 200), vec![0..100]);
171 }
172
173 #[test]
174 fn missing_ranges_gap_in_middle() {
175 let mut cache = SparseCache::new(1000).unwrap();
176 cache.write_at(0, &[0u8; 100]).unwrap();
177 cache.write_at(200, &[0u8; 100]).unwrap();
178 assert_eq!(cache.missing_ranges(0, 300), vec![100..200]);
180 }
181
182 #[test]
183 fn missing_ranges_multiple_gaps() {
184 let mut cache = SparseCache::new(1000).unwrap();
185 cache.write_at(100, &[0u8; 50]).unwrap();
186 cache.write_at(300, &[0u8; 50]).unwrap();
187 assert_eq!(
189 cache.missing_ranges(0, 400),
190 vec![0..100, 150..300, 350..400]
191 );
192 }
193
194 #[test]
195 fn missing_ranges_clips_to_total_size() {
196 let mut cache = SparseCache::new(500).unwrap();
197 assert_eq!(cache.missing_ranges(400, 1000), vec![400..500]);
199 cache.write_at(400, &[0u8; 100]).unwrap();
200 assert_eq!(cache.missing_ranges(400, 1000), Vec::<Range<u64>>::new());
201 }
202
203 #[test]
204 fn missing_ranges_offset_past_end() {
205 let cache = SparseCache::new(100).unwrap();
206 assert_eq!(cache.missing_ranges(200, 100), Vec::<Range<u64>>::new());
207 }
208
209 #[test]
210 fn adjacent_ranges_merge() {
211 let mut cache = SparseCache::new(1000).unwrap();
212 cache.write_at(0, &[0u8; 100]).unwrap();
213 cache.write_at(100, &[0u8; 100]).unwrap();
214 assert_eq!(cache.populated_ranges(), &[0..200]);
215 }
216
217 #[test]
218 fn overlapping_ranges_merge() {
219 let mut cache = SparseCache::new(1000).unwrap();
220 cache.write_at(0, &[0u8; 100]).unwrap();
221 cache.write_at(50, &[0u8; 100]).unwrap();
222 assert_eq!(cache.populated_ranges(), &[0..150]);
223 }
224
225 #[test]
226 fn disjoint_ranges_preserved() {
227 let mut cache = SparseCache::new(1000).unwrap();
228 cache.write_at(0, &[0u8; 100]).unwrap();
229 cache.write_at(500, &[0u8; 100]).unwrap();
230 assert_eq!(cache.populated_ranges(), &[0..100, 500..600]);
231 }
232
233 #[test]
234 fn insertion_sorted() {
235 let mut cache = SparseCache::new(1000).unwrap();
236 cache.write_at(500, &[0u8; 100]).unwrap();
237 cache.write_at(0, &[0u8; 100]).unwrap();
238 cache.write_at(300, &[0u8; 50]).unwrap();
239 assert_eq!(cache.populated_ranges(), &[0..100, 300..350, 500..600]);
240 }
241
242 #[test]
243 fn write_read_roundtrip() {
244 let mut cache = SparseCache::new(1000).unwrap();
245 let data: Vec<u8> = (0..200).map(|i| (i % 256) as u8).collect();
246 cache.write_at(100, &data).unwrap();
247 let read = cache.read_at(100, 200).unwrap();
248 assert_eq!(read, data);
249 }
250
251 #[test]
252 fn read_at_clips_to_total_size() {
253 let mut cache = SparseCache::new(150).unwrap();
254 cache.write_at(100, &[0u8; 50]).unwrap();
255 let read = cache.read_at(100, 200).unwrap();
257 assert_eq!(read.len(), 50);
258 }
259
260 #[test]
261 fn read_at_past_end_returns_empty() {
262 let mut cache = SparseCache::new(100).unwrap();
263 let read = cache.read_at(200, 50).unwrap();
264 assert_eq!(read, Vec::<u8>::new());
265 }
266
267 #[test]
268 fn three_way_merge() {
269 let mut cache = SparseCache::new(1000).unwrap();
271 cache.write_at(0, &[0u8; 100]).unwrap();
272 cache.write_at(200, &[0u8; 100]).unwrap();
273 cache.write_at(100, &[0u8; 100]).unwrap();
274 assert_eq!(cache.populated_ranges(), &[0..300]);
275 }
276}