Simplify disk cache
This commit is contained in:
@@ -1,14 +1,10 @@
|
||||
use std::{cmp, ptr};
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use syscall::error::Result;
|
||||
|
||||
use BLOCK_SIZE;
|
||||
use disk::Disk;
|
||||
|
||||
use self::lru_cache::LruCache;
|
||||
|
||||
mod linked_hash_map;
|
||||
mod lru_cache;
|
||||
|
||||
fn copy_memory(src: &[u8], dest: &mut [u8]) -> usize {
|
||||
let len = cmp::min(src.len(), dest.len());
|
||||
unsafe { ptr::copy(src.as_ptr(), dest.as_mut_ptr(), len) };
|
||||
@@ -17,16 +13,30 @@ fn copy_memory(src: &[u8], dest: &mut [u8]) -> usize {
|
||||
|
||||
pub struct DiskCache<T> {
|
||||
inner: T,
|
||||
cache: LruCache<u64, [u8; BLOCK_SIZE as usize]>,
|
||||
cache: BTreeMap<u64, [u8; BLOCK_SIZE as usize]>,
|
||||
order: VecDeque<u64>,
|
||||
size: usize,
|
||||
}
|
||||
|
||||
impl<T: Disk> DiskCache<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
DiskCache {
|
||||
inner: inner,
|
||||
cache: LruCache::new((256 * 1024 * 1024 / BLOCK_SIZE) as usize) // 256 MB cache
|
||||
cache: BTreeMap::new(),
|
||||
order: VecDeque::new(),
|
||||
size: 65536, // 256 MB cache
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, i: u64, data: [u8; BLOCK_SIZE as usize]) {
|
||||
while self.order.len() >= self.size {
|
||||
let removed = self.order.pop_front().unwrap();
|
||||
self.cache.remove(&removed);
|
||||
}
|
||||
|
||||
self.cache.insert(i, data);
|
||||
self.order.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Disk> Disk for DiskCache<T> {
|
||||
@@ -63,7 +73,7 @@ impl<T: Disk> Disk for DiskCache<T> {
|
||||
|
||||
let mut cache_buf = [0; BLOCK_SIZE as usize];
|
||||
read += copy_memory(buffer_slice, &mut cache_buf);
|
||||
self.cache.insert(block_i, cache_buf);
|
||||
self.insert(block_i, cache_buf);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +95,7 @@ impl<T: Disk> Disk for DiskCache<T> {
|
||||
|
||||
let mut cache_buf = [0; BLOCK_SIZE as usize];
|
||||
written += copy_memory(buffer_slice, &mut cache_buf);
|
||||
self.cache.insert(block_i, cache_buf);
|
||||
self.insert(block_i, cache_buf);
|
||||
}
|
||||
|
||||
Ok(written)
|
||||
Vendored
-732
@@ -1,732 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A HashMap wrapper that holds key-value pairs in insertion order.
|
||||
|
||||
#![forbid(missing_docs)]
|
||||
#![cfg_attr(feature = "nightly", feature(hashmap_public_hasher))]
|
||||
#![cfg_attr(all(feature = "nightly", test), feature(test))]
|
||||
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::{self, HashMap};
|
||||
use std::fmt;
|
||||
use std::hash::{BuildHasher, Hash, Hasher};
|
||||
use std::iter;
|
||||
use std::marker;
|
||||
use std::mem;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::ptr;
|
||||
|
||||
struct KeyRef<K> { k: *const K }
|
||||
|
||||
struct LinkedHashMapEntry<K, V> {
|
||||
next: *mut LinkedHashMapEntry<K, V>,
|
||||
prev: *mut LinkedHashMapEntry<K, V>,
|
||||
key: K,
|
||||
value: V,
|
||||
}
|
||||
|
||||
/// A linked hash map.
|
||||
pub struct LinkedHashMap<K, V, S = hash_map::RandomState> {
|
||||
map: HashMap<KeyRef<K>, Box<LinkedHashMapEntry<K, V>>, S>,
|
||||
head: *mut LinkedHashMapEntry<K, V>,
|
||||
free: *mut LinkedHashMapEntry<K, V>,
|
||||
}
|
||||
|
||||
impl<K: Hash> Hash for KeyRef<K> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
unsafe { (*self.k).hash(state) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: PartialEq> PartialEq for KeyRef<K> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe{ (*self.k).eq(&*other.k) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Eq> Eq for KeyRef<K> {}
|
||||
|
||||
// This type exists only to support borrowing `KeyRef`s, which cannot be borrowed to `Q` directly
|
||||
// due to conflicting implementations of `Borrow`. The layout of `&Qey<Q>` must be identical to
|
||||
// `&Q` in order to support transmuting in the `Qey::from_ref` method.
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct Qey<Q: ?Sized>(Q);
|
||||
|
||||
impl<Q: ?Sized> Qey<Q> {
|
||||
fn from_ref(q: &Q) -> &Self { unsafe { mem::transmute(q) } }
|
||||
}
|
||||
|
||||
impl<K, Q: ?Sized> Borrow<Qey<Q>> for KeyRef<K> where K: Borrow<Q> {
|
||||
fn borrow(&self) -> &Qey<Q> {
|
||||
Qey::from_ref(unsafe { (*self.k).borrow() })
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> LinkedHashMapEntry<K, V> {
|
||||
fn new(k: K, v: V) -> Self {
|
||||
LinkedHashMapEntry {
|
||||
key: k,
|
||||
value: v,
|
||||
next: ptr::null_mut(),
|
||||
prev: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn drop_empty_entry_box<K, V>(the_box: *mut LinkedHashMapEntry<K, V>) {
|
||||
// Prevent compiler from trying to drop the un-initialized key and values in the node.
|
||||
let LinkedHashMapEntry { key, value, .. } = *Box::from_raw(the_box);
|
||||
mem::forget(key);
|
||||
mem::forget(value);
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V> LinkedHashMap<K, V> {
|
||||
/// Creates a linked hash map.
|
||||
pub fn new() -> Self { Self::with_map(HashMap::new()) }
|
||||
|
||||
/// Creates an empty linked hash map with the given initial capacity.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self::with_map(HashMap::with_capacity(capacity))
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> LinkedHashMap<K, V, S> {
|
||||
fn clear_free_list(&mut self) {
|
||||
unsafe {
|
||||
let mut free = self.free;
|
||||
while ! free.is_null() {
|
||||
let next_free = (*free).next;
|
||||
drop_empty_entry_box(free);
|
||||
free = next_free;
|
||||
}
|
||||
self.free = ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V, S: BuildHasher> LinkedHashMap<K, V, S> {
|
||||
fn with_map(map: HashMap<KeyRef<K>, Box<LinkedHashMapEntry<K, V>>, S>) -> Self {
|
||||
LinkedHashMap {
|
||||
map: map,
|
||||
head: ptr::null_mut(),
|
||||
free: ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an empty linked hash map with the given initial hash state.
|
||||
pub fn with_hash_state(hash_state: S) -> Self {
|
||||
Self::with_map(HashMap::with_hasher(hash_state))
|
||||
}
|
||||
|
||||
/// Creates an empty linked hash map with the given initial capacity and hash state.
|
||||
pub fn with_capacity_and_hash_state(capacity: usize, hash_state: S) -> Self {
|
||||
Self::with_map(HashMap::with_capacity_and_hasher(capacity, hash_state))
|
||||
}
|
||||
|
||||
/// Reserves capacity for at least `additional` more elements to be inserted into the map. The
|
||||
/// map may reserve more space to avoid frequent allocations.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the new allocation size overflows `usize.`
|
||||
pub fn reserve(&mut self, additional: usize) { self.map.reserve(additional); }
|
||||
|
||||
/// Shrinks the capacity of the map as much as possible. It will drop down as much as possible
|
||||
/// while maintaining the internal rules and possibly leaving some space in accordance with the
|
||||
/// resize policy.
|
||||
pub fn shrink_to_fit(&mut self) {
|
||||
self.map.shrink_to_fit();
|
||||
self.clear_free_list();
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map. If the key already existed, the old value is
|
||||
/// returned.
|
||||
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
||||
if self.head.is_null() {
|
||||
// allocate the guard node if not present
|
||||
unsafe {
|
||||
self.head = Box::into_raw(Box::new(mem::uninitialized()));
|
||||
(*self.head).next = self.head;
|
||||
(*self.head).prev = self.head;
|
||||
}
|
||||
}
|
||||
let (node_ptr, node_opt, old_val) = match self.map.get_mut(&KeyRef{k: &k}) {
|
||||
Some(node) => {
|
||||
let old_val = mem::replace(&mut node.value, v);
|
||||
let node_ptr: *mut LinkedHashMapEntry<K, V> = &mut **node;
|
||||
(node_ptr, None, Some(old_val))
|
||||
}
|
||||
None => {
|
||||
let mut node = if self.free.is_null() {
|
||||
Box::new(LinkedHashMapEntry::new(k, v))
|
||||
} else {
|
||||
// use a recycled box
|
||||
unsafe {
|
||||
let free = self.free;
|
||||
self.free = (*free).next;
|
||||
ptr::write(free, LinkedHashMapEntry::new(k, v));
|
||||
Box::from_raw(free)
|
||||
}
|
||||
};
|
||||
let node_ptr: *mut LinkedHashMapEntry<K, V> = &mut *node;
|
||||
(node_ptr, Some(node), None)
|
||||
}
|
||||
};
|
||||
match node_opt {
|
||||
None => {
|
||||
// Existing node, just update LRU position
|
||||
self.detach(node_ptr);
|
||||
self.attach(node_ptr);
|
||||
}
|
||||
Some(node) => {
|
||||
let keyref = unsafe { &(*node_ptr).key };
|
||||
self.map.insert(KeyRef{k: keyref}, node);
|
||||
self.attach(node_ptr);
|
||||
}
|
||||
}
|
||||
old_val
|
||||
}
|
||||
|
||||
/// Checks if the map contains the given key.
|
||||
pub fn contains_key<Q: ?Sized>(&self, k: &Q) -> bool where K: Borrow<Q>, Q: Eq + Hash {
|
||||
self.map.contains_key(Qey::from_ref(k))
|
||||
}
|
||||
|
||||
/// Returns the value corresponding to the key in the map.
|
||||
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where K: Borrow<Q>, Q: Eq + Hash {
|
||||
self.map.get(Qey::from_ref(k)).map(|e| &e.value)
|
||||
}
|
||||
|
||||
/// Returns the mutable reference corresponding to the key in the map.
|
||||
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> where K: Borrow<Q>, Q: Eq + Hash {
|
||||
self.map.get_mut(Qey::from_ref(k)).map(|e| &mut e.value)
|
||||
}
|
||||
|
||||
/// Returns the value corresponding to the key in the map.
|
||||
///
|
||||
/// If value is found, it is moved to the end of the list.
|
||||
/// This operation can be used in implemenation of LRU cache.
|
||||
pub fn get_refresh<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> where K: Borrow<Q>, Q: Eq + Hash {
|
||||
let (value, node_ptr_opt) = match self.map.get_mut(Qey::from_ref(k)) {
|
||||
None => (None, None),
|
||||
Some(node) => {
|
||||
let node_ptr: *mut LinkedHashMapEntry<K, V> = &mut **node;
|
||||
(Some(unsafe { &mut(*node_ptr).value }), Some(node_ptr))
|
||||
}
|
||||
};
|
||||
if let Some(node_ptr) = node_ptr_opt {
|
||||
self.detach(node_ptr);
|
||||
self.attach(node_ptr);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Removes and returns the value corresponding to the key from the map.
|
||||
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V> where K: Borrow<Q>, Q: Eq + Hash {
|
||||
let removed = self.map.remove(Qey::from_ref(k));
|
||||
removed.map(|mut node| {
|
||||
let node_ptr: *mut LinkedHashMapEntry<K,V> = &mut *node;
|
||||
self.detach(node_ptr);
|
||||
unsafe {
|
||||
// add to free list
|
||||
(*node_ptr).next = self.free;
|
||||
self.free = node_ptr;
|
||||
// forget the box but drop the key and return the value
|
||||
mem::forget(node);
|
||||
drop(ptr::read(&(*node_ptr).key));
|
||||
ptr::read(&(*node_ptr).value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the maximum number of key-value pairs the map can hold without reallocating.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.map.capacity()
|
||||
}
|
||||
|
||||
/// Removes the first entry.
|
||||
///
|
||||
/// Can be used in implementation of LRU cache.
|
||||
#[inline]
|
||||
pub fn pop_front(&mut self) -> Option<(K, V)> {
|
||||
if self.len() > 0 {
|
||||
let lru = unsafe { (*self.head).prev };
|
||||
self.detach(lru);
|
||||
return self.map
|
||||
.remove(&KeyRef{k: unsafe { &(*lru).key }})
|
||||
.map(|e| { let e = *e; (e.key, e.value) })
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Gets the first entry.
|
||||
#[inline]
|
||||
pub fn front(&self) -> Option<(&K, &V)> {
|
||||
if self.len() > 0 {
|
||||
let lru = unsafe { (*self.head).prev };
|
||||
return self.map.get(&KeyRef{k: unsafe { &(*lru).key }})
|
||||
.map(|e| (&e.key, &e.value))
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Removes the last entry.
|
||||
#[inline]
|
||||
pub fn pop_back(&mut self) -> Option<(K, V)> {
|
||||
if self.len() > 0 {
|
||||
let mru = unsafe { (*self.head).next };
|
||||
self.detach(mru);
|
||||
return self.map
|
||||
.remove(&KeyRef{k: unsafe { &(*mru).key }})
|
||||
.map(|e| { let e = *e; (e.key, e.value) })
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Gets the last entry.
|
||||
#[inline]
|
||||
pub fn back(&mut self) -> Option<(&K, &V)> {
|
||||
if self.len() > 0 {
|
||||
let mru = unsafe { (*self.head).next };
|
||||
return self.map.get(&KeyRef{k: unsafe { &(*mru).key }})
|
||||
.map(|e| (&e.key, &e.value))
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the number of key-value pairs in the map.
|
||||
pub fn len(&self) -> usize { self.map.len() }
|
||||
|
||||
/// Returns whether the map is currently empty.
|
||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||
|
||||
/// Clears the map of all key-value pairs.
|
||||
pub fn clear(&mut self) {
|
||||
self.map.clear();
|
||||
// update the guard node if present
|
||||
if ! self.head.is_null() {
|
||||
unsafe {
|
||||
(*self.head).prev = self.head;
|
||||
(*self.head).next = self.head;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a double-ended iterator visiting all key-value pairs in order of insertion.
|
||||
/// Iterator element type is `(&'a K, &'a V)`
|
||||
pub fn iter(&self) -> Iter<K, V> {
|
||||
let head = if ! self.head.is_null() {
|
||||
unsafe { (*self.head).prev }
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
};
|
||||
Iter {
|
||||
head: head,
|
||||
tail: self.head,
|
||||
remaining: self.len(),
|
||||
marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a double-ended iterator visiting all key-value pairs in order of insertion.
|
||||
/// Iterator element type is `(&'a K, &'a mut V)`
|
||||
pub fn iter_mut(&mut self) -> IterMut<K, V> {
|
||||
let head = if ! self.head.is_null() {
|
||||
unsafe { (*self.head).prev }
|
||||
} else {
|
||||
ptr::null_mut()
|
||||
};
|
||||
IterMut {
|
||||
head: head,
|
||||
tail: self.head,
|
||||
remaining: self.len(),
|
||||
marker: marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a double-ended iterator visiting all key in order of insertion.
|
||||
pub fn keys<'a>(&'a self) -> Keys<'a, K, V> {
|
||||
fn first<A, B>((a, _): (A, B)) -> A { a }
|
||||
let first: fn((&'a K, &'a V)) -> &'a K = first; // coerce to fn ptr
|
||||
|
||||
Keys { inner: self.iter().map(first) }
|
||||
}
|
||||
|
||||
/// Returns a double-ended iterator visiting all values in order of insertion.
|
||||
pub fn values<'a>(&'a self) -> Values<'a, K, V> {
|
||||
fn second<A, B>((_, b): (A, B)) -> B { b }
|
||||
let second: fn((&'a K, &'a V)) -> &'a V = second; // coerce to fn ptr
|
||||
|
||||
Values { inner: self.iter().map(second) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V, S, Q: ?Sized> Index<&'a Q> for LinkedHashMap<K, V, S>
|
||||
where K: Hash + Eq + Borrow<Q>, S: BuildHasher, Q: Eq + Hash
|
||||
{
|
||||
type Output = V;
|
||||
|
||||
fn index(&self, index: &'a Q) -> &V {
|
||||
self.get(index).expect("no entry found for key")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V, S, Q: ?Sized> IndexMut<&'a Q> for LinkedHashMap<K, V, S>
|
||||
where K: Hash + Eq + Borrow<Q>, S: BuildHasher, Q: Eq + Hash
|
||||
{
|
||||
fn index_mut(&mut self, index: &'a Q) -> &mut V {
|
||||
self.get_mut(index).expect("no entry found for key")
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V, S: BuildHasher> LinkedHashMap<K, V, S> {
|
||||
#[inline]
|
||||
fn detach(&mut self, node: *mut LinkedHashMapEntry<K, V>) {
|
||||
unsafe {
|
||||
(*(*node).prev).next = (*node).next;
|
||||
(*(*node).next).prev = (*node).prev;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn attach(&mut self, node: *mut LinkedHashMapEntry<K, V>) {
|
||||
unsafe {
|
||||
(*node).next = (*self.head).next;
|
||||
(*node).prev = self.head;
|
||||
(*self.head).next = node;
|
||||
(*(*node).next).prev = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
impl<K: Hash + Eq + Clone, V: Clone> Clone for LinkedHashMap<K, V> {
|
||||
fn clone(&self) -> Self {
|
||||
self.iter().map(|(k, v)| (k.clone(), v.clone())).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
impl<K: Hash + Eq + Clone, V: Clone, S: BuildHasher + Clone> Clone for LinkedHashMap<K, V, S> {
|
||||
fn clone(&self) -> Self {
|
||||
let mut map = Self::with_hash_state(self.map.hasher().clone());
|
||||
map.extend(self.iter().map(|(k, v)| (k.clone(), v.clone())));
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V, S: BuildHasher + Default> Default for LinkedHashMap<K, V, S> {
|
||||
fn default() -> Self { LinkedHashMap::with_hash_state(Default::default()) }
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V, S: BuildHasher> Extend<(K, V)> for LinkedHashMap<K, V, S> {
|
||||
fn extend<T: IntoIterator<Item=(K, V)>>(&mut self, iter: T) {
|
||||
for (k, v) in iter {
|
||||
self.insert(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V, S> Extend<(&'a K, &'a V)> for LinkedHashMap<K, V, S>
|
||||
where K: 'a + Hash + Eq + Copy, V: 'a + Copy, S: BuildHasher,
|
||||
{
|
||||
fn extend<I: IntoIterator<Item = (&'a K, &'a V)>>(&mut self, iter: I) {
|
||||
for (&k, &v) in iter {
|
||||
self.insert(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V, S: BuildHasher + Default> iter::FromIterator<(K, V)> for LinkedHashMap<K, V, S> {
|
||||
fn from_iter<I: IntoIterator<Item=(K, V)>>(iter: I) -> Self {
|
||||
let iter = iter.into_iter();
|
||||
let mut map = Self::with_capacity_and_hash_state(iter.size_hint().0, Default::default());
|
||||
map.extend(iter);
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: fmt::Debug + Hash + Eq, B: fmt::Debug, S: BuildHasher> fmt::Debug for LinkedHashMap<A, B, S> {
|
||||
/// Returns a string that lists the key-value pairs in insertion order.
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_map().entries(self).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V: PartialEq, S: BuildHasher> PartialEq for LinkedHashMap<K, V, S> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.len() == other.len() && self.iter().eq(other)
|
||||
}
|
||||
|
||||
fn ne(&self, other: &Self) -> bool {
|
||||
self.len() != other.len() || self.iter().ne(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V: Eq, S: BuildHasher> Eq for LinkedHashMap<K, V, S> {}
|
||||
|
||||
impl<K: Hash + Eq + PartialOrd, V: PartialOrd, S: BuildHasher> PartialOrd for LinkedHashMap<K, V, S> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.iter().partial_cmp(other)
|
||||
}
|
||||
|
||||
fn lt(&self, other: &Self) -> bool {
|
||||
self.iter().lt(other)
|
||||
}
|
||||
|
||||
fn le(&self, other: &Self) -> bool {
|
||||
self.iter().le(other)
|
||||
}
|
||||
|
||||
fn ge(&self, other: &Self) -> bool {
|
||||
self.iter().ge(other)
|
||||
}
|
||||
|
||||
fn gt(&self, other: &Self) -> bool {
|
||||
self.iter().gt(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq + Ord, V: Ord, S: BuildHasher> Ord for LinkedHashMap<K, V, S> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.iter().cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V: Hash, S: BuildHasher> Hash for LinkedHashMap<K, V, S> {
|
||||
fn hash<H: Hasher>(&self, h: &mut H) { for e in self.iter() { e.hash(h); } }
|
||||
}
|
||||
|
||||
unsafe impl<K: Send, V: Send, S: Send> Send for LinkedHashMap<K, V, S> {}
|
||||
|
||||
unsafe impl<K: Sync, V: Sync, S: Sync> Sync for LinkedHashMap<K, V, S> {}
|
||||
|
||||
impl<K, V, S> Drop for LinkedHashMap<K, V, S> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if ! self.head.is_null() {
|
||||
drop_empty_entry_box(self.head);
|
||||
}
|
||||
self.clear_free_list();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An insertion-order iterator over a `LinkedHashMap`'s entries, with immutable references to the
|
||||
/// values.
|
||||
pub struct Iter<'a, K: 'a, V: 'a> {
|
||||
head: *const LinkedHashMapEntry<K, V>,
|
||||
tail: *const LinkedHashMapEntry<K, V>,
|
||||
remaining: usize,
|
||||
marker: marker::PhantomData<(&'a K, &'a V)>,
|
||||
}
|
||||
|
||||
/// An insertion-order iterator over a `LinkedHashMap`'s entries, with mutable references to the
|
||||
/// values.
|
||||
pub struct IterMut<'a, K: 'a, V: 'a> {
|
||||
head: *mut LinkedHashMapEntry<K, V>,
|
||||
tail: *mut LinkedHashMapEntry<K, V>,
|
||||
remaining: usize,
|
||||
marker: marker::PhantomData<(&'a K, &'a mut V)>,
|
||||
}
|
||||
|
||||
unsafe impl<'a, K, V> Send for Iter<'a, K, V> where K: Send, V: Send {}
|
||||
|
||||
unsafe impl<'a, K, V> Send for IterMut<'a, K, V> where K: Send, V: Send {}
|
||||
|
||||
unsafe impl<'a, K, V> Sync for Iter<'a, K, V> where K: Sync, V: Sync {}
|
||||
|
||||
unsafe impl<'a, K, V> Sync for IterMut<'a, K, V> where K: Sync, V: Sync {}
|
||||
|
||||
impl<'a, K, V> Clone for Iter<'a, K, V> {
|
||||
fn clone(&self) -> Self { Iter { ..*self } }
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for Iter<'a, K, V> {
|
||||
type Item = (&'a K, &'a V);
|
||||
|
||||
fn next(&mut self) -> Option<(&'a K, &'a V)> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
self.remaining -= 1;
|
||||
unsafe {
|
||||
let r = Some((&(*self.head).key, &(*self.head).value));
|
||||
self.head = (*self.head).prev;
|
||||
r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.remaining, Some(self.remaining))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
|
||||
type Item = (&'a K, &'a mut V);
|
||||
|
||||
fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
self.remaining -= 1;
|
||||
unsafe {
|
||||
let r = Some((&(*self.head).key, &mut (*self.head).value));
|
||||
self.head = (*self.head).prev;
|
||||
r
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.remaining, Some(self.remaining))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> {
|
||||
fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
self.remaining -= 1;
|
||||
unsafe {
|
||||
self.tail = (*self.tail).next;
|
||||
let r = Some((&(*self.tail).key, &(*self.tail).value));
|
||||
r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> {
|
||||
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
|
||||
if self.head == self.tail {
|
||||
None
|
||||
} else {
|
||||
self.remaining -= 1;
|
||||
unsafe {
|
||||
self.tail = (*self.tail).next;
|
||||
let r = Some((&(*self.tail).key, &mut (*self.tail).value));
|
||||
r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {
|
||||
fn len(&self) -> usize { self.remaining }
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {
|
||||
fn len(&self) -> usize { self.remaining }
|
||||
}
|
||||
|
||||
/// An insertion-order iterator over a `LinkedHashMap`'s keys.
|
||||
pub struct Keys<'a, K: 'a, V: 'a> {
|
||||
inner: iter::Map<Iter<'a, K, V>, fn((&'a K, &'a V)) -> &'a K>
|
||||
}
|
||||
|
||||
impl<'a, K, V> Clone for Keys<'a, K, V> {
|
||||
fn clone(&self) -> Self { Keys { inner: self.inner.clone() } }
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for Keys<'a, K, V> {
|
||||
type Item = &'a K;
|
||||
|
||||
#[inline] fn next(&mut self) -> Option<(&'a K)> { self.inner.next() }
|
||||
#[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
|
||||
}
|
||||
|
||||
impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> {
|
||||
#[inline] fn next_back(&mut self) -> Option<(&'a K)> { self.inner.next_back() }
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> {
|
||||
fn len(&self) -> usize { self.inner.len() }
|
||||
}
|
||||
|
||||
/// An insertion-order iterator over a `LinkedHashMap`'s values.
|
||||
pub struct Values<'a, K: 'a, V: 'a> {
|
||||
inner: iter::Map<Iter<'a, K, V>, fn((&'a K, &'a V)) -> &'a V>
|
||||
}
|
||||
|
||||
impl<'a, K, V> Clone for Values<'a, K, V> {
|
||||
fn clone(&self) -> Self { Values { inner: self.inner.clone() } }
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for Values<'a, K, V> {
|
||||
type Item = &'a V;
|
||||
|
||||
#[inline] fn next(&mut self) -> Option<(&'a V)> { self.inner.next() }
|
||||
#[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() }
|
||||
}
|
||||
|
||||
impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> {
|
||||
#[inline] fn next_back(&mut self) -> Option<(&'a V)> { self.inner.next_back() }
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> {
|
||||
fn len(&self) -> usize { self.inner.len() }
|
||||
}
|
||||
|
||||
impl<'a, K: Hash + Eq, V, S: BuildHasher> IntoIterator for &'a LinkedHashMap<K, V, S> {
|
||||
type Item = (&'a K, &'a V);
|
||||
type IntoIter = Iter<'a, K, V>;
|
||||
fn into_iter(self) -> Iter<'a, K, V> { self.iter() }
|
||||
}
|
||||
|
||||
impl<'a, K: Hash + Eq, V, S: BuildHasher> IntoIterator for &'a mut LinkedHashMap<K, V, S> {
|
||||
type Item = (&'a K, &'a mut V);
|
||||
type IntoIter = IterMut<'a, K, V>;
|
||||
fn into_iter(self) -> IterMut<'a, K, V> { self.iter_mut() }
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "nightly", test))]
|
||||
mod bench {
|
||||
extern crate test;
|
||||
|
||||
use super::LinkedHashMap;
|
||||
|
||||
#[bench]
|
||||
fn not_recycled_cycling(b: &mut test::Bencher) {
|
||||
let mut hash_map = LinkedHashMap::with_capacity(1000);
|
||||
for i in 0usize..1000 {
|
||||
hash_map.insert(i, i);
|
||||
}
|
||||
b.iter(|| {
|
||||
for i in 0usize..1000 {
|
||||
hash_map.remove(&i);
|
||||
}
|
||||
hash_map.clear_free_list();
|
||||
for i in 0usize..1000 {
|
||||
hash_map.insert(i, i);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn recycled_cycling(b: &mut test::Bencher) {
|
||||
let mut hash_map = LinkedHashMap::with_capacity(1000);
|
||||
for i in 0usize..1000 {
|
||||
hash_map.insert(i, i);
|
||||
}
|
||||
b.iter(|| {
|
||||
for i in 0usize..1000 {
|
||||
hash_map.remove(&i);
|
||||
}
|
||||
for i in 0usize..1000 {
|
||||
hash_map.insert(i, i);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Vendored
-327
@@ -1,327 +0,0 @@
|
||||
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A cache that holds a limited number of key-value pairs. When the
|
||||
//! capacity of the cache is exceeded, the least-recently-used
|
||||
//! (where "used" means a look-up or putting the pair into the cache)
|
||||
//! pair is automatically removed.
|
||||
|
||||
use std::collections::hash_map::RandomState;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, BuildHasher};
|
||||
use std::borrow::Borrow;
|
||||
|
||||
use super::linked_hash_map::{self, LinkedHashMap};
|
||||
|
||||
// FIXME(conventions): implement indexing?
|
||||
|
||||
/// An LRU cache.
|
||||
pub struct LruCache<K, V, S = RandomState> where K: Eq + Hash, S: BuildHasher {
|
||||
map: LinkedHashMap<K, V, S>,
|
||||
max_size: usize,
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V> LruCache<K, V> {
|
||||
/// Creates an empty cache that can hold at most `capacity` items.
|
||||
pub fn new(capacity: usize) -> LruCache<K, V> {
|
||||
LruCache {
|
||||
map: LinkedHashMap::new(),
|
||||
max_size: capacity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V, S> LruCache<K, V, S> where K: Eq + Hash, S: BuildHasher {
|
||||
/// Creates an empty cache that can hold at most `capacity` items with the given hash state.
|
||||
pub fn with_hash_state(capacity: usize, hash_state: S) -> LruCache<K, V, S> {
|
||||
LruCache { map: LinkedHashMap::with_hash_state(hash_state), max_size: capacity }
|
||||
}
|
||||
|
||||
/// Checks if the map contains the given key.
|
||||
pub fn contains_key<Q: ?Sized>(&mut self, key: &Q) -> bool
|
||||
where K: Borrow<Q>,
|
||||
Q: Hash + Eq
|
||||
{
|
||||
self.get_mut(key).is_some()
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the cache. If the key already existed, the old value is
|
||||
/// returned.
|
||||
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
|
||||
let old_val = self.map.insert(k, v);
|
||||
if self.len() > self.capacity() {
|
||||
self.remove_lru();
|
||||
}
|
||||
old_val
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the value corresponding to the given key in the cache, if
|
||||
/// any.
|
||||
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
|
||||
where K: Borrow<Q>,
|
||||
Q: Hash + Eq
|
||||
{
|
||||
self.map.get_refresh(k)
|
||||
}
|
||||
|
||||
/// Removes the given key from the cache and returns its corresponding value.
|
||||
pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
|
||||
where K: Borrow<Q>,
|
||||
Q: Hash + Eq
|
||||
{
|
||||
self.map.remove(k)
|
||||
}
|
||||
|
||||
/// Returns the maximum number of key-value pairs the cache can hold.
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.max_size
|
||||
}
|
||||
|
||||
/// Sets the number of key-value pairs the cache can hold. Removes
|
||||
/// least-recently-used key-value pairs if necessary.
|
||||
pub fn set_capacity(&mut self, capacity: usize) {
|
||||
for _ in capacity..self.len() {
|
||||
self.remove_lru();
|
||||
}
|
||||
self.max_size = capacity;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn remove_lru(&mut self) -> Option<(K, V)> {
|
||||
self.map.pop_front()
|
||||
}
|
||||
|
||||
/// Returns the number of key-value pairs in the cache.
|
||||
pub fn len(&self) -> usize { self.map.len() }
|
||||
|
||||
/// Returns `true` if the cache contains no key-value pairs.
|
||||
pub fn is_empty(&self) -> bool { self.map.is_empty() }
|
||||
|
||||
/// Removes all key-value pairs from the cache.
|
||||
pub fn clear(&mut self) { self.map.clear(); }
|
||||
|
||||
/// Returns an iterator over the cache's key-value pairs in least- to most-recently-used order.
|
||||
///
|
||||
/// Accessing the cache through the iterator does _not_ affect the cache's LRU state.
|
||||
pub fn iter(&self) -> Iter<K, V> { Iter(self.map.iter()) }
|
||||
|
||||
/// Returns an iterator over the cache's key-value pairs in least- to most-recently-used order,
|
||||
/// with mutable references to the values.
|
||||
///
|
||||
/// Accessing the cache through the iterator does _not_ affect the cache's LRU state.
|
||||
pub fn iter_mut(&mut self) -> IterMut<K, V> { IterMut(self.map.iter_mut()) }
|
||||
}
|
||||
|
||||
impl<K: Hash + Eq, V, S: BuildHasher> Extend<(K, V)> for LruCache<K, V, S> {
|
||||
fn extend<T: IntoIterator<Item=(K, V)>>(&mut self, iter: T) {
|
||||
for (k, v) in iter {
|
||||
self.insert(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: fmt::Debug + Hash + Eq, B: fmt::Debug, S: BuildHasher> fmt::Debug for LruCache<A, B, S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_map().entries(self.iter().rev()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V, S> IntoIterator for &'a LruCache<K, V, S> where K: Eq + Hash, S: BuildHasher {
|
||||
type Item = (&'a K, &'a V);
|
||||
type IntoIter = Iter<'a, K, V>;
|
||||
fn into_iter(self) -> Iter<'a, K, V> { self.iter() }
|
||||
}
|
||||
|
||||
impl<'a, K, V, S> IntoIterator for &'a mut LruCache<K, V, S> where K: Eq + Hash, S: BuildHasher {
|
||||
type Item = (&'a K, &'a mut V);
|
||||
type IntoIter = IterMut<'a, K, V>;
|
||||
fn into_iter(self) -> IterMut<'a, K, V> { self.iter_mut() }
|
||||
}
|
||||
|
||||
impl<K, V> Clone for LruCache<K, V> where K: Clone + Eq + Hash, V: Clone {
|
||||
fn clone(&self) -> LruCache<K, V> { LruCache { map: self.map.clone(), ..*self } }
|
||||
}
|
||||
|
||||
/// An iterator over a cache's key-value pairs in least- to most-recently-used order.
|
||||
///
|
||||
/// Accessing a cache through the iterator does _not_ affect the cache's LRU state.
|
||||
pub struct Iter<'a, K: 'a, V: 'a>(linked_hash_map::Iter<'a, K, V>);
|
||||
|
||||
impl<'a, K, V> Clone for Iter<'a, K, V> {
|
||||
fn clone(&self) -> Iter<'a, K, V> { Iter(self.0.clone()) }
|
||||
}
|
||||
|
||||
impl<'a, K, V> Iterator for Iter<'a, K, V> {
|
||||
type Item = (&'a K, &'a V);
|
||||
fn next(&mut self) -> Option<(&'a K, &'a V)> { self.0.next() }
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
|
||||
}
|
||||
|
||||
impl<'a, K, V> DoubleEndedIterator for Iter<'a, K, V> {
|
||||
fn next_back(&mut self) -> Option<(&'a K, &'a V)> { self.0.next_back() }
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> {
|
||||
fn len(&self) -> usize { self.0.len() }
|
||||
}
|
||||
|
||||
/// An iterator over a cache's key-value pairs in least- to most-recently-used order with mutable
|
||||
/// references to the values.
|
||||
///
|
||||
/// Accessing a cache through the iterator does _not_ affect the cache's LRU state.
|
||||
pub struct IterMut<'a, K: 'a, V: 'a>(linked_hash_map::IterMut<'a, K, V>);
|
||||
|
||||
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
|
||||
type Item = (&'a K, &'a mut V);
|
||||
fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.0.next() }
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
|
||||
}
|
||||
|
||||
impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> {
|
||||
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { self.0.next_back() }
|
||||
}
|
||||
|
||||
impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {
|
||||
fn len(&self) -> usize { self.0.len() }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::LruCache;
|
||||
|
||||
#[test]
|
||||
fn test_put_and_get() {
|
||||
let mut cache = LruCache::new(2);
|
||||
cache.insert(1, 10);
|
||||
cache.insert(2, 20);
|
||||
assert_eq!(cache.get_mut(&1), Some(&mut 10));
|
||||
assert_eq!(cache.get_mut(&2), Some(&mut 20));
|
||||
assert_eq!(cache.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_put_update() {
|
||||
let mut cache = LruCache::new(1);
|
||||
cache.insert("1", 10);
|
||||
cache.insert("1", 19);
|
||||
assert_eq!(cache.get_mut("1"), Some(&mut 19));
|
||||
assert_eq!(cache.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains_key() {
|
||||
let mut cache = LruCache::new(1);
|
||||
cache.insert("1", 10);
|
||||
assert_eq!(cache.contains_key("1"), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_expire_lru() {
|
||||
let mut cache = LruCache::new(2);
|
||||
cache.insert("foo1", "bar1");
|
||||
cache.insert("foo2", "bar2");
|
||||
cache.insert("foo3", "bar3");
|
||||
assert!(cache.get_mut("foo1").is_none());
|
||||
cache.insert("foo2", "bar2update");
|
||||
cache.insert("foo4", "bar4");
|
||||
assert!(cache.get_mut("foo3").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pop() {
|
||||
let mut cache = LruCache::new(2);
|
||||
cache.insert(1, 10);
|
||||
cache.insert(2, 20);
|
||||
assert_eq!(cache.len(), 2);
|
||||
let opt1 = cache.remove(&1);
|
||||
assert!(opt1.is_some());
|
||||
assert_eq!(opt1.unwrap(), 10);
|
||||
assert!(cache.get_mut(&1).is_none());
|
||||
assert_eq!(cache.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_change_capacity() {
|
||||
let mut cache = LruCache::new(2);
|
||||
assert_eq!(cache.capacity(), 2);
|
||||
cache.insert(1, 10);
|
||||
cache.insert(2, 20);
|
||||
cache.set_capacity(1);
|
||||
assert!(cache.get_mut(&1).is_none());
|
||||
assert_eq!(cache.capacity(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let mut cache = LruCache::new(3);
|
||||
cache.insert(1, 10);
|
||||
cache.insert(2, 20);
|
||||
cache.insert(3, 30);
|
||||
assert_eq!(format!("{:?}", cache), "{3: 30, 2: 20, 1: 10}");
|
||||
cache.insert(2, 22);
|
||||
assert_eq!(format!("{:?}", cache), "{2: 22, 3: 30, 1: 10}");
|
||||
cache.insert(6, 60);
|
||||
assert_eq!(format!("{:?}", cache), "{6: 60, 2: 22, 3: 30}");
|
||||
cache.get_mut(&3);
|
||||
assert_eq!(format!("{:?}", cache), "{3: 30, 6: 60, 2: 22}");
|
||||
cache.set_capacity(2);
|
||||
assert_eq!(format!("{:?}", cache), "{3: 30, 6: 60}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_remove() {
|
||||
let mut cache = LruCache::new(3);
|
||||
cache.insert(1, 10);
|
||||
cache.insert(2, 20);
|
||||
cache.insert(3, 30);
|
||||
cache.insert(4, 40);
|
||||
cache.insert(5, 50);
|
||||
cache.remove(&3);
|
||||
cache.remove(&4);
|
||||
assert!(cache.get_mut(&3).is_none());
|
||||
assert!(cache.get_mut(&4).is_none());
|
||||
cache.insert(6, 60);
|
||||
cache.insert(7, 70);
|
||||
cache.insert(8, 80);
|
||||
assert!(cache.get_mut(&5).is_none());
|
||||
assert_eq!(cache.get_mut(&6), Some(&mut 60));
|
||||
assert_eq!(cache.get_mut(&7), Some(&mut 70));
|
||||
assert_eq!(cache.get_mut(&8), Some(&mut 80));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear() {
|
||||
let mut cache = LruCache::new(2);
|
||||
cache.insert(1, 10);
|
||||
cache.insert(2, 20);
|
||||
cache.clear();
|
||||
assert!(cache.get_mut(&1).is_none());
|
||||
assert!(cache.get_mut(&2).is_none());
|
||||
assert_eq!(format!("{:?}", cache), "{}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iter() {
|
||||
let mut cache = LruCache::new(3);
|
||||
cache.insert(1, 10);
|
||||
cache.insert(2, 20);
|
||||
cache.insert(3, 30);
|
||||
cache.insert(4, 40);
|
||||
cache.insert(5, 50);
|
||||
assert_eq!(cache.iter().collect::<Vec<_>>(),
|
||||
[(&3, &30), (&4, &40), (&5, &50)]);
|
||||
assert_eq!(cache.iter_mut().collect::<Vec<_>>(),
|
||||
[(&3, &mut 30), (&4, &mut 40), (&5, &mut 50)]);
|
||||
assert_eq!(cache.iter().rev().collect::<Vec<_>>(),
|
||||
[(&5, &50), (&4, &40), (&3, &30)]);
|
||||
assert_eq!(cache.iter_mut().rev().collect::<Vec<_>>(),
|
||||
[(&5, &mut 50), (&4, &mut 40), (&3, &mut 30)]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user