[go: up one dir, main page]

cached 0.8.1

Generic cache implementations and simplified function memoization
Documentation
#[macro_use] extern crate cached;

use std::collections::HashMap;
use std::hash::Hash;
use std::cmp::Eq;

use std::time::Duration;
use std::thread::sleep;

use cached::{Cached, UnboundCache, SizedCache};


/// cached shorthand, uses the default unbounded cache.
/// Equivalent to specifying `FIB: UnboundCache<(u32), u32> = UnboundCache::new();`
cached!{
    FIB;
    fn fib(n: u32) -> u32 = {
        if n == 0 || n == 1 { return n; }
        fib(n-1) + fib(n-2)
    }
}


/// Same as above, but preallocates some space.
/// Note that the cache key type is a tuple of function argument types.
cached!{
    FIB_SPECIFIC: UnboundCache<(u32), u32> = UnboundCache::with_capacity(50);
    fn fib_specific(n: u32) -> u32 = {
        if n == 0 || n == 1 { return n; }
        fib_specific(n-1) + fib_specific(n-2)
    }
}


/// Specify a specific cache type
/// Note that the cache key type is a tuple of function argument types.
cached!{
    SLOW: SizedCache<(u32, u32), u32> = SizedCache::with_size(100);
    fn slow(a: u32, b: u32) -> u32 = {
        sleep(Duration::new(2, 0));
        return a * b;
    }
}


/// Specify a specific cache type and an explicit key expression
/// Note that the cache key type is a `String` created from the borrow arguments
cached_key!{
    KEYED: SizedCache<String, usize> = SizedCache::with_size(100);
    Key = { format!("{}{}", a, b) };
    fn keyed(a: &str, b: &str) -> usize = {
        let size = a.len() + b.len();
        sleep(Duration::new(size as u64, 0));
        size
    }
}


/// Implement our own cache type
struct MyCache<K: Hash + Eq, V> {
    store: HashMap<K, V>,
}
impl <K: Hash + Eq, V> MyCache<K, V> {
    pub fn with_capacity(size: usize) -> MyCache<K, V> {
        MyCache { store: HashMap::with_capacity(size) }
    }
}
impl <K: Hash + Eq, V> Cached<K, V> for MyCache<K, V> {
    fn cache_get(&mut self, k: &K) -> Option<&V> {
        self.store.get(k)
    }
    fn cache_set(&mut self, k: K, v: V) {
        self.store.insert(k, v);
    }
    fn cache_remove(&mut self, k: &K) -> Option<V> {
        self.store.remove(k)
    }
    fn cache_clear(&mut self) { self.store.clear(); }
    fn cache_size(&self) -> usize {
        self.store.len()
    }
}


/// Specify our custom cache and supply an instance to use
cached!{
    CUSTOM: MyCache<(u32), ()> = MyCache::with_capacity(50);
    fn custom(n: u32) -> () = {
        if n == 0 { return; }
        custom(n-1)
    }
}


pub fn main() {
    println!("\n ** default cache **");
    fib(3);
    fib(3);
    {
        let cache = FIB.lock().unwrap();
        println!("hits: {:?}", cache.cache_hits());
        println!("misses: {:?}", cache.cache_misses());
        // make sure lock is dropped
    }
    fib(10);
    fib(10);

    println!("\n ** specific cache **");
    fib_specific(20);
    fib_specific(20);
    {
        let cache = FIB_SPECIFIC.lock().unwrap();
        println!("hits: {:?}", cache.cache_hits());
        println!("misses: {:?}", cache.cache_misses());
        // make sure lock is dropped
    }
    fib_specific(20);
    fib_specific(20);

    println!("\n ** custom cache **");
    custom(25);
    {
        let cache = CUSTOM.lock().unwrap();
        println!("hits: {:?}", cache.cache_hits());
        println!("misses: {:?}", cache.cache_misses());
        // make sure lock is dropped
    }

    println!("\n ** slow func **");
    println!(" - first run `slow(10)`");
    slow(10, 10);
    println!(" - second run `slow(10)`");
    slow(10, 10);
    {
        let cache = SLOW.lock().unwrap();
        println!("hits: {:?}", cache.cache_hits());
        println!("misses: {:?}", cache.cache_misses());
        // make sure the cache-lock is dropped
    }
}