ocean.util.container.cache.LRUCache

(L)east (R)ecently (U)sed Cache class, Caches data items according to their access time and discards item that were least recently accessed.

Cache of raw data (ubyte[] / void[]) items of either fixed or variable length. The cache is initialised with a fixed capacity (the number of items that can be stored). When the cache reaches its full capacity, any newly added items will replace older items in the cache. The cache keeps track of the last time each item was written or read, and will replace the oldest items first.

The basic Cache template is used to store raw data. A second template exists which takes a type as its parameter. This implements a thin wrapper around the basic Cache, allowing the transparent storage of (no-op) serialized values of the specified type.

More...

Members

Classes

LRUCache
class LRUCache(T, bool TrackCreateTimes = false)

Data cache class template. Stores items of raw data, either of fixed or dynamic size.

Detailed Description

Note that with variable value length anything stored in the cache is invisible to the garbage collector because the internal value data type of the cache is ubyte[]. So if you store references (objects, pointers, slices to dynamic arrays) in the cache with variable value length, make sure your application keeps a reference to it. Otherwise the object referenced to may be garbage collected and attempting to use it after getting it from the cache will make your program go to HELL.

When a cache element is removed explicitly (by calling remove()), the value of the removed element is kept in the cache in a spare location. If required it is possible to erase the value by overriding Cache.replaceRemovedItem(), see the description of this method for an example.

When a cache element is removed automatically if the cache is full and a new element is added, Cache.whenCacheItemDropped(size_t index) will be called. If required it is possible to be notified of this occurrence by overriding Cache.whenCacheItemDropped method.

Cache.getOrCreate() return a reference to a record in the cache. Cache.getAndRefresh() return a reference to a record in the cache or returns null otherwise. For fixed length values the record value reference is a slice to the record value.

N.B. only get methods with refresh in their name update the recency of an item. If you use a plain get() this won't happen and the item may get evicted sooner than you might think.

Usage example:

import ocean.util.container.LRUCache;

// Create a fixed-size cache which can store 2 items, each of length 3.
auto cache = new LRUCache!(char[3])(2);

// Add an item using getOrCreate(). getOrCreate() returns a pointer to
// void[] array with length 3 which references the value in the cache.

hash_t key = 0x12345678;

char[3] val = "abc";

*cache.getOrCreate(key) = val[];

// Obtain an item using getAndRefresh(). If found, getAndRefresh()
// returns a pointer to a value slice just like getOrCreate() or null
// if not found.

char[] val_got = cache.getAndRefresh(key);

if (val_got !is null)
{
    // val_got contains the value that corresponds to key.
    // The value in the cache can be modified in-place by setting array
    // elements or copying to the whole array:

    (cast(char[])val_got)[2] = '!'; // Set the third value byte to '!'.

    (cast(char[])val_got)[] = "def"; // Set the value to "def".
}
else
{
    // not found
}

For variable length arrays it is a pointer to the Cache.Value struct which encapsulates the value, which is void[], providing access to the value via operator overloading:

- opAssign sets the value array instance to an input array slice (overwriting the previous array instance), - opSliceAssign treats the value array as an allocated buffer and copies the content of the an input array slice into the value array, - opSlice returns the value array.

opSliceAssign reuses an existing buffer and is therefore memory-friendly as long as opAssign is not used with the same value instance.

Rule of thumb: For each cache instance with variable value size use either opAssign or opSliceAssign with the values, never both.

Usage Example 1: Store string slices in a cache using Value.opAssign.

auto cache = new LRUCache!(char[])(100);

char[] str1 = "Hello World",
       str2 = "Eggs and Spam";

{
    auto val = cache.getRefreshOrCreate(4711);

    // Store a slice to str1 in the array using (*val).opAssign.

    *val = str1;
}

{
    auto val = cache.getAndRefresh(4711);

    // (*val)[] ((*val).opSlice) now returns a slice to str1.

    // Replace this value with a slice to str2 using (*val).opAssign.

    *val = str2;
}

Usage Example 2: Store copies of strings in a cache using Value.opSliceAssign.

auto cache = new LRUCache!(char[])(100);

char[] str1 = "Hello World",
       str2 = "Eggs and Spam";

{
    auto val = cache.getRefreshOrCreate(4711);

    // Allocate a value array buffer with str1.length and copy the
    // content of str1 into that value buffer.

    (*val)[] = str1;
}

{
    auto val = cache.getAndRefresh(4711);

    // (*val)[] ((*val).opSlice) now returns the value array buffer
    // which contains a copy of the content of str1.

    // Use (*val)[] = str2 ((*val).opSliceAssign(x)) to resize the value
    // array buffer to str2.length and copy the content of str2 into it.

    (*val)[] = str2;
}

For special situations it is possible to obtain a pointer to the value array. One such situation is when the value array needs to be modified by an external function which doesn't know about the cache.

void setValue ( ref void[] value )
{
    value = "Hello World!";
}

auto cache = new LRUCache!(char[])(100);

auto val = cache.getRefreshOrCreate(4711);

void[]* val_array = cast (void[]*) (*val);

setValue(*val_array);

Link with: -Llibebtree.a

TODO: Extend the cache by making values visible to the GC by default and provide GC hiding as an option.

Meta

License

Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. Alternatively, this file may be distributed under the terms of the Tango 3-Clause BSD License (see LICENSE_BSD.txt for details).