chess/cache_table.rs
1#[derive(Copy, Clone, PartialEq, PartialOrd)]
2struct CacheTableEntry<T: Copy + Clone + PartialEq + PartialOrd> {
3 hash: u64,
4 entry: T,
5}
6
7/// Store a cache of entries, each with an associated hash.
8pub struct CacheTable<T: Copy + Clone + PartialEq + PartialOrd> {
9 table: Box<[CacheTableEntry<T>]>,
10 mask: usize,
11}
12
13impl<T: Copy + Clone + PartialEq + PartialOrd> CacheTable<T> {
14 /// Create a new `CacheTable` with each associated entry initialized with a hash of '0'
15 /// Note: You must pass in a size where only 1 bit is set. (AKA: 2, 4, 8, 16, 1024, 65536,
16 /// etc.)
17 /// Panics when size is invalid.
18 #[inline]
19 pub fn new(size: usize, default: T) -> CacheTable<T> {
20 if size.count_ones() != 1 {
21 panic!("You cannot create a CacheTable with a non-binary number.");
22 }
23 let values = vec![
24 CacheTableEntry {
25 hash: 0,
26 entry: default
27 };
28 size
29 ];
30 CacheTable {
31 table: values.into_boxed_slice(),
32 mask: size - 1,
33 }
34 }
35
36 /// Get a particular entry with the hash specified
37 #[inline]
38 pub fn get(&self, hash: u64) -> Option<T> {
39 let entry = unsafe { *self.table.get_unchecked((hash as usize) & self.mask) };
40 if entry.hash == hash {
41 Some(entry.entry)
42 } else {
43 None
44 }
45 }
46
47 /// Add (or overwrite) an entry with the associated hash
48 #[inline]
49 pub fn add(&mut self, hash: u64, entry: T) {
50 let e = unsafe { self.table.get_unchecked_mut((hash as usize) & self.mask) };
51 *e = CacheTableEntry {
52 hash: hash,
53 entry: entry,
54 };
55 }
56
57 /// Replace an entry in the hash table with a user-specified replacement policy specified by
58 /// `replace`. The `replace` closure is called with the previous entry occupying the hash table
59 /// slot, and returns true or false to specify whether the entry should be replaced. Note that
60 /// the previous entry may not have the same hash, but merely be the default initialization or
61 /// a hash collision with `hash`.
62 ///
63 /// ```
64 /// use chess::CacheTable;
65 ///
66 /// # fn main() {
67 ///
68 /// let mut table: CacheTable<char> = CacheTable::new(256, 'a');
69 ///
70 /// assert_eq!(table.get(5), None);
71 /// // Note that 'a' is the default initialization value.
72 /// table.replace_if(5, 'b', |old_entry| old_entry != 'a');
73 /// assert_eq!(table.get(5), None);
74 /// table.replace_if(5, 'c', |old_entry| old_entry == 'a');
75 /// assert_eq!(table.get(5), Some('c'));
76 /// table.replace_if(5, 'd', |old_entry| old_entry == 'c');
77 /// assert_eq!(table.get(5), Some('d'));
78 ///
79 /// # }
80 /// ```
81 #[inline(always)]
82 pub fn replace_if<F: Fn(T) -> bool>(&mut self, hash: u64, entry: T, replace: F) {
83 let e = unsafe { self.table.get_unchecked_mut((hash as usize) & self.mask) };
84 if replace(e.entry) {
85 *e = CacheTableEntry {
86 hash: hash,
87 entry: entry,
88 };
89 }
90 }
91}