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