lru-cache-macros

Build Status lru-cache-macros on docs.rs lru-cache-macros on crates.io

An attribute procedural macro to automatically cache the result of a function given a set of inputs.

Example:

```rust use lrucachemacros::lru_cache;

[lru_cache(20)]

fn fib(x: u32) -> u64 { println!("{:?}", x); if x <= 1 { 1 } else { fib(x - 1) + fib(x - 2) } }

assert_eq!(fib(19), 6765); ```

The above example only calls fib twenty times, with the values from 0 to 19. All intermediate results because of the recursion hit the cache.

Usage:

Simply place #[lru_cache([size])] above your function. The function must obey a few properties to use lru_cache:

The macro will use the LruCache at ::lru_cache::LruCache by default. This can be changed by setting the cache_type config variable as shown in the configuration section.

The LruCache type used must accept two generic parameters <Args, Return> and must support methods get_mut(&K) and insert(K, V). The lru-cache crate meets these requirements.

Currently, this crate only works on nightly rust. However, once the 2018 edition stabilizes as well as the procedural macro diagnostic interface, it should be able to run on stable.

Configuration:

The lru_cache macro can be configured by adding additional attributes under #[lru_cache(size)].

All configuration attributes take the form #[lru_config(...)]. The available attributes are:

Details

The created cache is stored as a static variable protected by a mutex unless the #[lru_config(thread_local)] configuration is added.

With the default settings, the fibonacci example will generate the following code:

```rust fn _lrubasefib(x: u32) -> u64 { if x <= 1 { 1 } else { fib(x - 1) + fib(x - 2) } } fn fib(x: u32) -> u64 { use lazystatic::lazy_static; use std::sync::Mutex;

lazy_static! {
    static ref cache: Mutex<::lru_cache::LruCache<(u32,), u64>> =
        Mutex::new(::lru_cache::LruCache::new(20usize));
}

let cloned_args = (x.clone(),);
let mut cache_unlocked = cache.lock().unwrap();
let stored_result = cache_unlocked.get_mut(&cloned_args);
if let Some(stored_result) = stored_result {
    return stored_result.clone();
};
drop(cache_unlocked);
let ret = __lru_base_fib(x);
let mut cache_unlocked = cache.lock().unwrap();
cache_unlocked.insert(cloned_args, ret.clone());
ret

}

```

Whereas, if you use the #[lru_config(thread_local)] the generated code will look like:

```rust fn _lrubasefib(x: u32) -> u64 { if x <= 1 { 1 } else { fib(x - 1) + fib(x - 2) } } fn fib(x: u32) -> u64 { use std::cell::UnsafeCell; use std::threadlocal;

thread_local!(
     static cache: UnsafeCell<::lru_cache::LruCache<(u32,), u64>> =
         UnsafeCell::new(::lru_cache::LruCache::new(20usize));
);

cache.with(|c|
    {
        let mut cache_ref = unsafe { &mut *c.get() };
        let cloned_args = (x.clone(),);
        let stored_result = cache_ref.get_mut(&cloned_args);
        if let Some(stored_result) = stored_result {
            stored_result.clone()
        } else {
            let ret = __lru_base_fib(x);
            cache_ref.insert(cloned_args, ret.clone());
            ret
        }
    })

} ```