xref: /aosp_15_r20/external/crosvm/disk/src/qcow/vec_cache.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2018 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::hash_map::IterMut;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::HashMap;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::io;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::ops::Index;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::ops::IndexMut;
10*bb4ee6a4SAndroid Build Coastguard Worker use std::slice::SliceIndex;
11*bb4ee6a4SAndroid Build Coastguard Worker 
12*bb4ee6a4SAndroid Build Coastguard Worker /// Trait that allows for checking if an implementor is dirty. Useful for types that are cached so
13*bb4ee6a4SAndroid Build Coastguard Worker /// it can be checked if they need to be committed to disk.
14*bb4ee6a4SAndroid Build Coastguard Worker pub trait Cacheable {
15*bb4ee6a4SAndroid Build Coastguard Worker     /// Used to check if the item needs to be written out or if it can be discarded.
dirty(&self) -> bool16*bb4ee6a4SAndroid Build Coastguard Worker     fn dirty(&self) -> bool;
17*bb4ee6a4SAndroid Build Coastguard Worker }
18*bb4ee6a4SAndroid Build Coastguard Worker 
19*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)]
20*bb4ee6a4SAndroid Build Coastguard Worker /// Represents a vector that implements the `Cacheable` trait so it can be held in a cache.
21*bb4ee6a4SAndroid Build Coastguard Worker pub struct VecCache<T: 'static + Copy + Default> {
22*bb4ee6a4SAndroid Build Coastguard Worker     vec: Box<[T]>,
23*bb4ee6a4SAndroid Build Coastguard Worker     dirty: bool,
24*bb4ee6a4SAndroid Build Coastguard Worker }
25*bb4ee6a4SAndroid Build Coastguard Worker 
26*bb4ee6a4SAndroid Build Coastguard Worker impl<T: 'static + Copy + Default> VecCache<T> {
27*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates a `VecCache` that can hold `count` elements.
new(count: usize) -> VecCache<T>28*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(count: usize) -> VecCache<T> {
29*bb4ee6a4SAndroid Build Coastguard Worker         VecCache {
30*bb4ee6a4SAndroid Build Coastguard Worker             vec: vec![Default::default(); count].into_boxed_slice(),
31*bb4ee6a4SAndroid Build Coastguard Worker             dirty: true,
32*bb4ee6a4SAndroid Build Coastguard Worker         }
33*bb4ee6a4SAndroid Build Coastguard Worker     }
34*bb4ee6a4SAndroid Build Coastguard Worker 
35*bb4ee6a4SAndroid Build Coastguard Worker     /// Creates a `VecCache` from the passed in `vec`.
from_vec(vec: Vec<T>) -> VecCache<T>36*bb4ee6a4SAndroid Build Coastguard Worker     pub fn from_vec(vec: Vec<T>) -> VecCache<T> {
37*bb4ee6a4SAndroid Build Coastguard Worker         VecCache {
38*bb4ee6a4SAndroid Build Coastguard Worker             vec: vec.into_boxed_slice(),
39*bb4ee6a4SAndroid Build Coastguard Worker             dirty: false,
40*bb4ee6a4SAndroid Build Coastguard Worker         }
41*bb4ee6a4SAndroid Build Coastguard Worker     }
42*bb4ee6a4SAndroid Build Coastguard Worker 
get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output> where I: SliceIndex<[T]>,43*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
44*bb4ee6a4SAndroid Build Coastguard Worker     where
45*bb4ee6a4SAndroid Build Coastguard Worker         I: SliceIndex<[T]>,
46*bb4ee6a4SAndroid Build Coastguard Worker     {
47*bb4ee6a4SAndroid Build Coastguard Worker         self.vec.get(index)
48*bb4ee6a4SAndroid Build Coastguard Worker     }
49*bb4ee6a4SAndroid Build Coastguard Worker 
50*bb4ee6a4SAndroid Build Coastguard Worker     /// Gets a reference to the underlying vector.
get_values(&self) -> &[T]51*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get_values(&self) -> &[T] {
52*bb4ee6a4SAndroid Build Coastguard Worker         &self.vec
53*bb4ee6a4SAndroid Build Coastguard Worker     }
54*bb4ee6a4SAndroid Build Coastguard Worker 
55*bb4ee6a4SAndroid Build Coastguard Worker     /// Mark this cache element as clean.
mark_clean(&mut self)56*bb4ee6a4SAndroid Build Coastguard Worker     pub fn mark_clean(&mut self) {
57*bb4ee6a4SAndroid Build Coastguard Worker         self.dirty = false;
58*bb4ee6a4SAndroid Build Coastguard Worker     }
59*bb4ee6a4SAndroid Build Coastguard Worker 
60*bb4ee6a4SAndroid Build Coastguard Worker     /// Returns the number of elements in the vector.
len(&self) -> usize61*bb4ee6a4SAndroid Build Coastguard Worker     pub fn len(&self) -> usize {
62*bb4ee6a4SAndroid Build Coastguard Worker         self.vec.len()
63*bb4ee6a4SAndroid Build Coastguard Worker     }
64*bb4ee6a4SAndroid Build Coastguard Worker }
65*bb4ee6a4SAndroid Build Coastguard Worker 
66*bb4ee6a4SAndroid Build Coastguard Worker impl<T: 'static + Copy + Default> Cacheable for VecCache<T> {
dirty(&self) -> bool67*bb4ee6a4SAndroid Build Coastguard Worker     fn dirty(&self) -> bool {
68*bb4ee6a4SAndroid Build Coastguard Worker         self.dirty
69*bb4ee6a4SAndroid Build Coastguard Worker     }
70*bb4ee6a4SAndroid Build Coastguard Worker }
71*bb4ee6a4SAndroid Build Coastguard Worker 
72*bb4ee6a4SAndroid Build Coastguard Worker impl<T: 'static + Copy + Default> Index<usize> for VecCache<T> {
73*bb4ee6a4SAndroid Build Coastguard Worker     type Output = T;
74*bb4ee6a4SAndroid Build Coastguard Worker 
index(&self, index: usize) -> &T75*bb4ee6a4SAndroid Build Coastguard Worker     fn index(&self, index: usize) -> &T {
76*bb4ee6a4SAndroid Build Coastguard Worker         self.vec.index(index)
77*bb4ee6a4SAndroid Build Coastguard Worker     }
78*bb4ee6a4SAndroid Build Coastguard Worker }
79*bb4ee6a4SAndroid Build Coastguard Worker 
80*bb4ee6a4SAndroid Build Coastguard Worker impl<T: 'static + Copy + Default> IndexMut<usize> for VecCache<T> {
index_mut(&mut self, index: usize) -> &mut T81*bb4ee6a4SAndroid Build Coastguard Worker     fn index_mut(&mut self, index: usize) -> &mut T {
82*bb4ee6a4SAndroid Build Coastguard Worker         self.dirty = true;
83*bb4ee6a4SAndroid Build Coastguard Worker         self.vec.index_mut(index)
84*bb4ee6a4SAndroid Build Coastguard Worker     }
85*bb4ee6a4SAndroid Build Coastguard Worker }
86*bb4ee6a4SAndroid Build Coastguard Worker 
87*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)]
88*bb4ee6a4SAndroid Build Coastguard Worker pub struct CacheMap<T: Cacheable> {
89*bb4ee6a4SAndroid Build Coastguard Worker     capacity: usize,
90*bb4ee6a4SAndroid Build Coastguard Worker     map: HashMap<usize, T>,
91*bb4ee6a4SAndroid Build Coastguard Worker }
92*bb4ee6a4SAndroid Build Coastguard Worker 
93*bb4ee6a4SAndroid Build Coastguard Worker impl<T: Cacheable> CacheMap<T> {
new(capacity: usize) -> Self94*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(capacity: usize) -> Self {
95*bb4ee6a4SAndroid Build Coastguard Worker         CacheMap {
96*bb4ee6a4SAndroid Build Coastguard Worker             capacity,
97*bb4ee6a4SAndroid Build Coastguard Worker             map: HashMap::with_capacity(capacity),
98*bb4ee6a4SAndroid Build Coastguard Worker         }
99*bb4ee6a4SAndroid Build Coastguard Worker     }
100*bb4ee6a4SAndroid Build Coastguard Worker 
contains_key(&self, key: &usize) -> bool101*bb4ee6a4SAndroid Build Coastguard Worker     pub fn contains_key(&self, key: &usize) -> bool {
102*bb4ee6a4SAndroid Build Coastguard Worker         self.map.contains_key(key)
103*bb4ee6a4SAndroid Build Coastguard Worker     }
104*bb4ee6a4SAndroid Build Coastguard Worker 
get(&self, index: &usize) -> Option<&T>105*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get(&self, index: &usize) -> Option<&T> {
106*bb4ee6a4SAndroid Build Coastguard Worker         self.map.get(index)
107*bb4ee6a4SAndroid Build Coastguard Worker     }
108*bb4ee6a4SAndroid Build Coastguard Worker 
get_mut(&mut self, index: &usize) -> Option<&mut T>109*bb4ee6a4SAndroid Build Coastguard Worker     pub fn get_mut(&mut self, index: &usize) -> Option<&mut T> {
110*bb4ee6a4SAndroid Build Coastguard Worker         self.map.get_mut(index)
111*bb4ee6a4SAndroid Build Coastguard Worker     }
112*bb4ee6a4SAndroid Build Coastguard Worker 
iter_mut(&mut self) -> IterMut<usize, T>113*bb4ee6a4SAndroid Build Coastguard Worker     pub fn iter_mut(&mut self) -> IterMut<usize, T> {
114*bb4ee6a4SAndroid Build Coastguard Worker         self.map.iter_mut()
115*bb4ee6a4SAndroid Build Coastguard Worker     }
116*bb4ee6a4SAndroid Build Coastguard Worker 
117*bb4ee6a4SAndroid Build Coastguard Worker     // Check if the refblock cache is full and we need to evict.
insert<F>(&mut self, index: usize, block: T, write_callback: F) -> io::Result<()> where F: FnOnce(usize, T) -> io::Result<()>,118*bb4ee6a4SAndroid Build Coastguard Worker     pub fn insert<F>(&mut self, index: usize, block: T, write_callback: F) -> io::Result<()>
119*bb4ee6a4SAndroid Build Coastguard Worker     where
120*bb4ee6a4SAndroid Build Coastguard Worker         F: FnOnce(usize, T) -> io::Result<()>,
121*bb4ee6a4SAndroid Build Coastguard Worker     {
122*bb4ee6a4SAndroid Build Coastguard Worker         if self.map.len() == self.capacity {
123*bb4ee6a4SAndroid Build Coastguard Worker             // TODO(dgreid) - smarter eviction strategy.
124*bb4ee6a4SAndroid Build Coastguard Worker             let to_evict = *self.map.iter().next().unwrap().0;
125*bb4ee6a4SAndroid Build Coastguard Worker             if let Some(evicted) = self.map.remove(&to_evict) {
126*bb4ee6a4SAndroid Build Coastguard Worker                 if evicted.dirty() {
127*bb4ee6a4SAndroid Build Coastguard Worker                     write_callback(to_evict, evicted)?;
128*bb4ee6a4SAndroid Build Coastguard Worker                 }
129*bb4ee6a4SAndroid Build Coastguard Worker             }
130*bb4ee6a4SAndroid Build Coastguard Worker         }
131*bb4ee6a4SAndroid Build Coastguard Worker         self.map.insert(index, block);
132*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
133*bb4ee6a4SAndroid Build Coastguard Worker     }
134*bb4ee6a4SAndroid Build Coastguard Worker }
135*bb4ee6a4SAndroid Build Coastguard Worker 
136*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
137*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
138*bb4ee6a4SAndroid Build Coastguard Worker     use super::*;
139*bb4ee6a4SAndroid Build Coastguard Worker 
140*bb4ee6a4SAndroid Build Coastguard Worker     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
141*bb4ee6a4SAndroid Build Coastguard Worker     struct NumCache(pub u64);
142*bb4ee6a4SAndroid Build Coastguard Worker     impl Cacheable for NumCache {
dirty(&self) -> bool143*bb4ee6a4SAndroid Build Coastguard Worker         fn dirty(&self) -> bool {
144*bb4ee6a4SAndroid Build Coastguard Worker             true
145*bb4ee6a4SAndroid Build Coastguard Worker         }
146*bb4ee6a4SAndroid Build Coastguard Worker     }
147*bb4ee6a4SAndroid Build Coastguard Worker 
148*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
evicts_when_full()149*bb4ee6a4SAndroid Build Coastguard Worker     fn evicts_when_full() {
150*bb4ee6a4SAndroid Build Coastguard Worker         let mut cache = CacheMap::<NumCache>::new(3);
151*bb4ee6a4SAndroid Build Coastguard Worker         let mut evicted = None;
152*bb4ee6a4SAndroid Build Coastguard Worker         cache
153*bb4ee6a4SAndroid Build Coastguard Worker             .insert(0, NumCache(5), |index, _| {
154*bb4ee6a4SAndroid Build Coastguard Worker                 evicted = Some(index);
155*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(())
156*bb4ee6a4SAndroid Build Coastguard Worker             })
157*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
158*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(evicted, None);
159*bb4ee6a4SAndroid Build Coastguard Worker         cache
160*bb4ee6a4SAndroid Build Coastguard Worker             .insert(1, NumCache(6), |index, _| {
161*bb4ee6a4SAndroid Build Coastguard Worker                 evicted = Some(index);
162*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(())
163*bb4ee6a4SAndroid Build Coastguard Worker             })
164*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
165*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(evicted, None);
166*bb4ee6a4SAndroid Build Coastguard Worker         cache
167*bb4ee6a4SAndroid Build Coastguard Worker             .insert(2, NumCache(7), |index, _| {
168*bb4ee6a4SAndroid Build Coastguard Worker                 evicted = Some(index);
169*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(())
170*bb4ee6a4SAndroid Build Coastguard Worker             })
171*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
172*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(evicted, None);
173*bb4ee6a4SAndroid Build Coastguard Worker         cache
174*bb4ee6a4SAndroid Build Coastguard Worker             .insert(3, NumCache(8), |index, _| {
175*bb4ee6a4SAndroid Build Coastguard Worker                 evicted = Some(index);
176*bb4ee6a4SAndroid Build Coastguard Worker                 Ok(())
177*bb4ee6a4SAndroid Build Coastguard Worker             })
178*bb4ee6a4SAndroid Build Coastguard Worker             .unwrap();
179*bb4ee6a4SAndroid Build Coastguard Worker         assert!(evicted.is_some());
180*bb4ee6a4SAndroid Build Coastguard Worker 
181*bb4ee6a4SAndroid Build Coastguard Worker         // Check that three of the four items inserted are still there and that the most recently
182*bb4ee6a4SAndroid Build Coastguard Worker         // inserted is one of them.
183*bb4ee6a4SAndroid Build Coastguard Worker         let num_items = (0..=3).filter(|k| cache.contains_key(k)).count();
184*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(num_items, 3);
185*bb4ee6a4SAndroid Build Coastguard Worker         assert!(cache.contains_key(&3));
186*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(cache.get(&3), Some(&NumCache(8)));
187*bb4ee6a4SAndroid Build Coastguard Worker     }
188*bb4ee6a4SAndroid Build Coastguard Worker }
189