sieve_cache/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::borrow::Borrow;
4use std::hash::Hash;
5use std::{collections::HashMap, ptr::NonNull};
6
7struct Node<K: Eq + Hash + Clone, V> {
8    key: K,
9    value: V,
10    prev: Option<NonNull<Node<K, V>>>,
11    next: Option<NonNull<Node<K, V>>>,
12    visited: bool,
13}
14
15impl<K: Eq + Hash + Clone, V> Node<K, V> {
16    fn new(key: K, value: V) -> Self {
17        Self {
18            key,
19            value,
20            prev: None,
21            next: None,
22            visited: false,
23        }
24    }
25}
26
27/// A cache based on the SIEVE eviction algorithm.
28pub struct SieveCache<K: Eq + Hash + Clone, V> {
29    map: HashMap<K, Box<Node<K, V>>>,
30    head: Option<NonNull<Node<K, V>>>,
31    tail: Option<NonNull<Node<K, V>>>,
32    hand: Option<NonNull<Node<K, V>>>,
33    capacity: usize,
34    len: usize,
35}
36
37unsafe impl<K: Eq + Hash + Clone, V> Send for SieveCache<K, V> {}
38
39impl<K: Eq + Hash + Clone, V> SieveCache<K, V> {
40    /// Create a new cache with the given capacity.
41    pub fn new(capacity: usize) -> Result<Self, &'static str> {
42        if capacity == 0 {
43            return Err("capacity must be greater than 0");
44        }
45        Ok(Self {
46            map: HashMap::with_capacity(capacity),
47            head: None,
48            tail: None,
49            hand: None,
50            capacity,
51            len: 0,
52        })
53    }
54
55    /// Return the capacity of the cache.
56    #[inline]
57    pub fn capacity(&self) -> usize {
58        self.capacity
59    }
60
61    /// Return the number of cached values.
62    #[inline]
63    pub fn len(&self) -> usize {
64        self.len
65    }
66
67    /// Return `true` when no values are currently cached.
68    #[inline]
69    pub fn is_empty(&self) -> bool {
70        self.len() == 0
71    }
72
73    /// Return `true` if there is a value in the cache mapped to by `key`.
74    #[inline]
75    pub fn contains_key<Q>(&mut self, key: &Q) -> bool
76    where
77        Q: Hash + Eq + ?Sized,
78        K: Borrow<Q>,
79    {
80        self.map.contains_key(key)
81    }
82
83    /// Get an immutable reference to the value in the cache mapped to by `key`.
84    ///
85    /// If no value exists for `key`, this returns `None`.
86    pub fn get<Q>(&mut self, key: &Q) -> Option<&V>
87    where
88        Q: Hash + Eq + ?Sized,
89        K: Borrow<Q>,
90    {
91        let node_ = self.map.get_mut(key)?;
92        node_.visited = true;
93        Some(&node_.value)
94    }
95
96    /// Get a mutable reference to the value in the cache mapped to by `key`.
97    ///
98    /// If no value exists for `key`, this returns `None`.
99    pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut V>
100    where
101        Q: Hash + Eq + ?Sized,
102        K: Borrow<Q>,
103    {
104        let node_ = self.map.get_mut(key)?;
105        node_.visited = true;
106        Some(&mut node_.value)
107    }
108
109    /// Map `key` to `value` in the cache, possibly evicting old entries.
110    ///
111    /// This method returns `true` when this is a new entry, and `false` if an existing entry was
112    /// updated.
113    pub fn insert(&mut self, key: K, value: V) -> bool {
114        let node = self.map.get_mut(&key);
115        if let Some(node_) = node {
116            node_.visited = true;
117            node_.value = value;
118            return false;
119        }
120        if self.len >= self.capacity {
121            self.evict();
122        }
123        let node = Box::new(Node::new(key.clone(), value));
124        self.add_node(NonNull::from(node.as_ref()));
125        debug_assert!(!node.visited);
126        self.map.insert(key, node);
127        debug_assert!(self.len < self.capacity);
128        self.len += 1;
129        true
130    }
131
132    /// Remove the cache entry mapped to by `key`.
133    ///
134    /// This method returns the value removed from the cache. If `key` did not map to any value,
135    /// then this returns `None`.
136    pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
137    where
138        K: Borrow<Q>,
139        Q: Eq + Hash + ?Sized,
140    {
141        let node_ = self.map.get_mut(key)?;
142        let node__ = NonNull::from(node_.as_ref());
143        if self.hand == Some(node__) {
144            self.hand = node_.as_ref().prev;
145        }
146        let value = self.map.remove(key).map(|node| node.value);
147        self.remove_node(node__);
148        debug_assert!(self.len > 0);
149        self.len -= 1;
150        value
151    }
152
153    fn add_node(&mut self, mut node: NonNull<Node<K, V>>) {
154        unsafe {
155            node.as_mut().next = self.head;
156            node.as_mut().prev = None;
157            if let Some(mut head) = self.head {
158                head.as_mut().prev = Some(node);
159            }
160        }
161        self.head = Some(node);
162        if self.tail.is_none() {
163            self.tail = self.head;
164        }
165    }
166
167    fn remove_node(&mut self, node: NonNull<Node<K, V>>) {
168        unsafe {
169            if let Some(mut prev) = node.as_ref().prev {
170                prev.as_mut().next = node.as_ref().next;
171            } else {
172                self.head = node.as_ref().next;
173            }
174            if let Some(mut next) = node.as_ref().next {
175                next.as_mut().prev = node.as_ref().prev;
176            } else {
177                self.tail = node.as_ref().prev;
178            }
179        }
180    }
181
182    fn evict(&mut self) {
183        let mut node = self.hand.or(self.tail);
184        while node.is_some() {
185            let mut node_ = node.unwrap();
186            unsafe {
187                if !node_.as_ref().visited {
188                    break;
189                }
190                node_.as_mut().visited = false;
191                if node_.as_ref().prev.is_some() {
192                    node = node_.as_ref().prev;
193                } else {
194                    node = self.tail;
195                }
196            }
197        }
198        if let Some(node_) = node {
199            unsafe {
200                self.hand = node_.as_ref().prev;
201                self.map.remove(&node_.as_ref().key);
202            }
203            self.remove_node(node_);
204            debug_assert!(self.len > 0);
205            self.len -= 1;
206        }
207    }
208}
209
210#[test]
211fn test() {
212    let mut cache = SieveCache::new(3).unwrap();
213    assert!(cache.insert("foo".to_string(), "foocontent".to_string()));
214    assert!(cache.insert("bar".to_string(), "barcontent".to_string()));
215    cache.remove("bar");
216    assert!(cache.insert("bar2".to_string(), "bar2content".to_string()));
217    assert!(cache.insert("bar3".to_string(), "bar3content".to_string()));
218    assert_eq!(cache.get("foo"), Some(&"foocontent".to_string()));
219    assert_eq!(cache.get("bar"), None);
220    assert_eq!(cache.get("bar2"), Some(&"bar2content".to_string()));
221    assert_eq!(cache.get("bar3"), Some(&"bar3content".to_string()));
222}
223
224#[test]
225fn test_visited_flag_update() {
226    let mut cache = SieveCache::new(2).unwrap();
227    cache.insert("key1".to_string(), "value1".to_string());
228    cache.insert("key2".to_string(), "value2".to_string());
229    // update `key1` entry.
230    cache.insert("key1".to_string(), "updated".to_string());
231    // new entry is added.
232    cache.insert("key3".to_string(), "value3".to_string());
233    assert_eq!(cache.get("key1"), Some(&"updated".to_string()));
234}