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