/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! A simple LRU cache. use arrayvec::{Array, ArrayVec}; /// A LRU cache using a statically-sized array for storage. /// /// The most-recently-used entry is at index `head`. The entries form a linked list, linked to each /// other by indices within the `entries` array. After an entry is added to the array, its index /// never changes, so these links are never invalidated. pub struct LRUCache>> { entries: ArrayVec, /// Index of the first entry. If the cache is empty, ignore this field. head: u16, /// Index of the last entry. If the cache is empty, ignore this field. tail: u16, } /// An opaque token used as an index into an LRUCache. pub struct CacheIndex(u16); /// An entry in an LRUCache. pub struct Entry { val: T, /// Index of the previous entry. If this entry is the head, ignore this field. prev: u16, /// Index of the next entry. If this entry is the tail, ignore this field. next: u16, } impl>> LRUCache { /// Create an empty LRU cache. pub fn new() -> Self { let cache = LRUCache { entries: ArrayVec::new(), head: 0, tail: 0, }; assert!(cache.entries.capacity() < u16::max_value() as usize, "Capacity overflow"); cache } /// Returns the number of elements in the cache. pub fn num_entries(&self) -> usize { self.entries.len() } #[inline] /// Touch a given entry, putting it first in the list. pub fn touch(&mut self, idx: CacheIndex) { if idx.0 != self.head { self.remove(idx.0); self.push_front(idx.0); } } /// Returns the front entry in the list (most recently used). pub fn front(&self) -> Option<&T> { self.entries.get(self.head as usize).map(|e| &e.val) } /// Returns a mutable reference to the front entry in the list (most recently used). pub fn front_mut(&mut self) -> Option<&mut T> { self.entries.get_mut(self.head as usize).map(|e| &mut e.val) } /// Iterate over the contents of this cache, from more to less recently /// used. pub fn iter(&self) -> LRUCacheIterator { LRUCacheIterator { pos: self.head, done: self.entries.len() == 0, cache: self, } } /// Iterate mutably over the contents of this cache. pub fn iter_mut(&mut self) -> LRUCacheMutIterator { LRUCacheMutIterator { pos: self.head, done: self.entries.len() == 0, cache: self, } } /// Insert a given key in the cache. pub fn insert(&mut self, val: T) { let entry = Entry { val, prev: 0, next: 0 }; // If the cache is full, replace the oldest entry. Otherwise, add an entry. let new_head = if self.entries.len() == self.entries.capacity() { let i = self.pop_back(); self.entries[i as usize] = entry; i } else { self.entries.push(entry); self.entries.len() as u16 - 1 }; self.push_front(new_head); } /// Remove an from the linked list. /// /// Note: This only unlinks the entry from the list; it does not remove it from the array. fn remove(&mut self, i: u16) { let prev = self.entries[i as usize].prev; let next = self.entries[i as usize].next; if i == self.head { self.head = next; } else { self.entries[prev as usize].next = next; } if i == self.tail { self.tail = prev; } else { self.entries[next as usize].prev = prev; } } /// Insert a new entry at the head of the list. fn push_front(&mut self, i: u16) { if self.entries.len() == 1 { self.tail = i; } else { self.entries[i as usize].next = self.head; self.entries[self.head as usize].prev = i; } self.head = i; } /// Remove the last entry from the linked list. Returns the index of the removed entry. /// /// Note: This only unlinks the entry from the list; it does not remove it from the array. fn pop_back(&mut self) -> u16 { let old_tail = self.tail; let new_tail = self.entries[old_tail as usize].prev; self.tail = new_tail; old_tail } /// Evict all elements from the cache. pub fn evict_all(&mut self) { self.entries.clear(); } } /// Immutable iterator over values in an LRUCache, from most-recently-used to least-recently-used. pub struct LRUCacheIterator<'a, T: 'a, A: 'a + Array>> { cache: &'a LRUCache, pos: u16, done: bool, } impl<'a, T, A> Iterator for LRUCacheIterator<'a, T, A> where T: 'a, A: 'a + Array> { type Item = (CacheIndex, &'a T); fn next(&mut self) -> Option { if self.done { return None } let entry = &self.cache.entries[self.pos as usize]; let index = CacheIndex(self.pos); if self.pos == self.cache.tail { self.done = true; } self.pos = entry.next; Some((index, &entry.val)) } } /// Mutable iterator over values in an LRUCache, from most-recently-used to least-recently-used. pub struct LRUCacheMutIterator<'a, T: 'a, A: 'a + Array>> { cache: &'a mut LRUCache, pos: u16, done: bool, } impl<'a, T, A> Iterator for LRUCacheMutIterator<'a, T, A> where T: 'a, A: 'a + Array> { type Item = (CacheIndex, &'a mut T); fn next(&mut self) -> Option { if self.done { return None } // Use a raw pointer because the compiler doesn't know that subsequent calls can't alias. let entry = unsafe { &mut *(&mut self.cache.entries[self.pos as usize] as *mut Entry) }; let index = CacheIndex(self.pos); if self.pos == self.cache.tail { self.done = true; } self.pos = entry.next; Some((index, &mut entry.val)) } }