Hoist the LRU cache into its own crate to share it with selectors.

This commit is contained in:
Bobby Holley 2017-09-20 18:38:20 -07:00
parent 5afb1b7dd2
commit 8b6c5988b5
9 changed files with 30 additions and 3 deletions

View file

@ -48,6 +48,7 @@ itertools = "0.5"
itoa = "0.3"
html5ever = {version = "0.19", optional = true}
lazy_static = "0.2"
lru_cache = { path = "../lru_cache" }
log = "0.3"
malloc_size_of = { path = "../malloc_size_of", optional=true }
malloc_size_of_derive = { path = "../malloc_size_of_derive", optional=true }

View file

@ -1,210 +0,0 @@
/* 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<T, A: Array<Item=Entry<T>>> {
entries: ArrayVec<A>,
/// 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<T> {
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<T, A: Array<Item=Entry<T>>> LRUCache<T, A> {
/// 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<T, A> {
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<T, A> {
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<Item=Entry<T>>> {
cache: &'a LRUCache<T, A>,
pos: u16,
done: bool,
}
impl<'a, T, A> Iterator for LRUCacheIterator<'a, T, A>
where T: 'a,
A: 'a + Array<Item=Entry<T>>
{
type Item = (CacheIndex, &'a T);
fn next(&mut self) -> Option<Self::Item> {
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<Item=Entry<T>>> {
cache: &'a mut LRUCache<T, A>,
pos: u16,
done: bool,
}
impl<'a, T, A> Iterator for LRUCacheMutIterator<'a, T, A>
where T: 'a,
A: 'a + Array<Item=Entry<T>>
{
type Item = (CacheIndex, &'a mut T);
fn next(&mut self) -> Option<Self::Item> {
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<T>)
};
let index = CacheIndex(self.pos);
if self.pos == self.cache.tail {
self.done = true;
}
self.pos = entry.next;
Some((index, &mut entry.val))
}
}

View file

@ -8,13 +8,13 @@
#[cfg(feature = "servo")] use animation::PropertyAnimation;
use app_units::Au;
use bloom::StyleBloom;
use cache::{Entry, LRUCache};
use data::{EagerPseudoStyles, ElementData};
use dom::{OpaqueNode, TNode, TElement, SendElement};
use euclid::ScaleFactor;
use euclid::Size2D;
use fnv::FnvHashMap;
use font_metrics::FontMetricsProvider;
use lru_cache::{Entry, LRUCache};
#[cfg(feature = "gecko")] use gecko_bindings::structs;
use parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB};
#[cfg(feature = "servo")] use parking_lot::RwLock;

View file

@ -59,6 +59,7 @@ extern crate itoa;
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate lru_cache;
#[cfg(feature = "gecko")] #[macro_use] extern crate malloc_size_of;
#[cfg(feature = "gecko")] #[macro_use] extern crate malloc_size_of_derive;
#[allow(unused_extern_crates)]
@ -102,7 +103,6 @@ pub mod applicable_declarations;
#[cfg(feature = "servo")] pub mod attr;
pub mod bezier;
pub mod bloom;
pub mod cache;
pub mod context;
pub mod counter_style;
pub mod custom_properties;

View file

@ -68,9 +68,9 @@ use Atom;
use applicable_declarations::ApplicableDeclarationBlock;
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
use bloom::StyleBloom;
use cache::{LRUCache, Entry};
use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
use dom::{TElement, SendElement};
use lru_cache::{LRUCache, Entry};
use matching::MatchMethods;
use owning_ref::OwningHandle;
use properties::ComputedValues;