2012-02-06 12:54:41 +08:00
/** \file lru.h
Least - recently - used cache implementation
*/
# ifndef FISH_LRU_H
# define FISH_LRU_H
2015-07-25 23:14:25 +08:00
# include <assert.h>
2012-02-06 12:54:41 +08:00
# include <wchar.h>
# include <map>
# include <set>
# include <list>
# include "common.h"
/** A predicate to compare dereferenced pointers */
2012-11-19 08:30:30 +08:00
struct dereference_less_t
{
2012-02-06 12:54:41 +08:00
template < typename ptr_t >
2012-11-19 08:30:30 +08:00
bool operator ( ) ( ptr_t p1 , ptr_t p2 ) const
{
return * p1 < * p2 ;
}
2012-02-06 12:54:41 +08:00
} ;
2012-11-19 08:30:30 +08:00
class lru_node_t
{
2012-02-06 12:54:41 +08:00
template < class T > friend class lru_cache_t ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Our linked list pointer */
lru_node_t * prev , * next ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
public :
/** The key used to look up in the cache */
const wcstring key ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Constructor */
lru_node_t ( const wcstring & pkey ) : prev ( NULL ) , next ( NULL ) , key ( pkey ) { }
2012-11-18 18:23:22 +08:00
2014-09-24 22:37:32 +08:00
/** Virtual destructor that does nothing for classes that inherit lru_node_t */
virtual ~ lru_node_t ( ) { }
2012-02-06 12:54:41 +08:00
/** operator< for std::set */
2012-11-19 08:30:30 +08:00
bool operator < ( const lru_node_t & other ) const
{
return key < other . key ;
}
2012-02-06 12:54:41 +08:00
} ;
template < class node_type_t >
2012-11-19 08:30:30 +08:00
class lru_cache_t
{
private :
2012-02-06 12:54:41 +08:00
2015-03-24 03:31:22 +08:00
/** Max node count. This may be (transiently) exceeded by add_node_without_eviction, which is used from background threads. */
2012-02-06 12:54:41 +08:00
const size_t max_node_count ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Count of nodes */
size_t node_count ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** The set of nodes */
typedef std : : set < lru_node_t * , dereference_less_t > node_set_t ;
node_set_t node_set ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
void promote_node ( node_type_t * node )
{
2012-02-06 12:54:41 +08:00
/* We should never promote the mouth */
assert ( node ! = & mouth ) ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* First unhook us */
node - > prev - > next = node - > next ;
node - > next - > prev = node - > prev ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Put us after the mouth */
node - > next = mouth . next ;
node - > next - > prev = node ;
node - > prev = & mouth ;
mouth . next = node ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
void evict_node ( node_type_t * condemned_node )
{
2012-02-06 12:54:41 +08:00
/* We should never evict the mouth */
assert ( condemned_node ! = NULL & & condemned_node ! = & mouth ) ;
/* Remove it from the linked list */
condemned_node - > prev - > next = condemned_node - > next ;
condemned_node - > next - > prev = condemned_node - > prev ;
/* Remove us from the set */
node_set . erase ( condemned_node ) ;
node_count - - ;
/* Tell ourselves */
this - > node_was_evicted ( condemned_node ) ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
void evict_last_node ( void )
{
2012-02-06 12:54:41 +08:00
/* Simple */
evict_node ( ( node_type_t * ) mouth . prev ) ;
}
2012-11-18 18:23:22 +08:00
2012-12-12 05:18:40 +08:00
static lru_node_t * get_previous ( lru_node_t * node )
{
return node - > prev ;
}
2012-11-19 08:30:30 +08:00
protected :
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Head of the linked list */
lru_node_t mouth ;
/** Overridable callback for when a node is evicted */
virtual void node_was_evicted ( node_type_t * node ) { }
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
public :
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Constructor */
2012-11-19 08:30:30 +08:00
lru_cache_t ( size_t max_size = 1024 ) : max_node_count ( max_size ) , node_count ( 0 ) , mouth ( wcstring ( ) )
{
2012-02-06 12:54:41 +08:00
/* Hook up the mouth to itself: a one node circularly linked list! */
mouth . prev = mouth . next = & mouth ;
}
2012-11-18 18:23:22 +08:00
2012-05-05 09:37:12 +08:00
/** Note that we do not evict nodes in our destructor (even though they typically need to be deleted by their creator). */
virtual ~ lru_cache_t ( ) { }
2012-02-18 06:54:58 +08:00
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Returns the node for a given key, or NULL */
2012-11-19 08:30:30 +08:00
node_type_t * get_node ( const wcstring & key )
{
2012-02-06 12:54:41 +08:00
node_type_t * result = NULL ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Construct a fake node as our key */
lru_node_t node_key ( key ) ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Look for it in the set */
node_set_t : : iterator iter = node_set . find ( & node_key ) ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* If we found a node, promote and return it */
2012-11-19 08:30:30 +08:00
if ( iter ! = node_set . end ( ) )
{
2012-02-06 12:54:41 +08:00
result = static_cast < node_type_t * > ( * iter ) ;
promote_node ( result ) ;
}
return result ;
}
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Evicts the node for a given key, returning true if a node was evicted. */
2012-11-19 08:30:30 +08:00
bool evict_node ( const wcstring & key )
{
2012-02-06 12:54:41 +08:00
/* Construct a fake node as our key */
lru_node_t node_key ( key ) ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Look for it in the set */
node_set_t : : iterator iter = node_set . find ( & node_key ) ;
if ( iter = = node_set . end ( ) )
return false ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Evict the given node */
evict_node ( static_cast < node_type_t * > ( * iter ) ) ;
return true ;
}
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Adds a node under the given key. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */
2012-11-19 08:30:30 +08:00
bool add_node ( node_type_t * node )
{
2012-02-06 12:54:41 +08:00
/* Add our node without eviction */
if ( ! this - > add_node_without_eviction ( node ) )
return false ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Evict */
while ( node_count > max_node_count )
evict_last_node ( ) ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Success */
return true ;
}
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Adds a node under the given key without triggering eviction. Returns true if the node was added, false if the node was not because a node with that key is already in the set. */
2012-11-19 08:30:30 +08:00
bool add_node_without_eviction ( node_type_t * node )
{
2012-02-06 12:54:41 +08:00
assert ( node ! = NULL & & node ! = & mouth ) ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Try inserting; return false if it was already in the set */
if ( ! node_set . insert ( node ) . second )
return false ;
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/* Add the node after the mouth */
node - > next = mouth . next ;
node - > next - > prev = node ;
node - > prev = & mouth ;
mouth . next = node ;
2012-11-18 18:23:22 +08:00
2015-03-24 03:31:22 +08:00
/* Update the count. This may push us over the maximum node count. */
2012-02-06 12:54:41 +08:00
node_count + + ;
2015-03-24 03:31:22 +08:00
2012-02-06 12:54:41 +08:00
/* Success */
return true ;
}
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Counts nodes */
2012-11-19 08:30:30 +08:00
size_t size ( void )
{
2012-02-06 12:54:41 +08:00
return node_count ;
}
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Evicts all nodes */
2012-11-19 08:30:30 +08:00
void evict_all_nodes ( void )
{
while ( node_count > 0 )
{
2012-02-06 12:54:41 +08:00
evict_last_node ( ) ;
}
}
2012-11-18 18:23:22 +08:00
2012-02-06 12:54:41 +08:00
/** Iterator for walking nodes, from least recently used to most */
2012-11-19 08:30:30 +08:00
class iterator
{
2012-02-06 12:54:41 +08:00
lru_node_t * node ;
2012-11-19 08:30:30 +08:00
public :
2012-02-06 12:54:41 +08:00
iterator ( lru_node_t * val ) : node ( val ) { }
2012-11-19 08:30:30 +08:00
void operator + + ( )
{
2012-12-12 05:18:40 +08:00
node = lru_cache_t : : get_previous ( node ) ;
2012-11-19 08:30:30 +08:00
}
void operator + + ( int x )
{
2012-12-12 05:18:40 +08:00
node = lru_cache_t : : get_previous ( node ) ;
2012-11-19 08:30:30 +08:00
}
bool operator = = ( const iterator & other )
{
return node = = other . node ;
}
bool operator ! = ( const iterator & other )
{
return ! ( * this = = other ) ;
}
node_type_t * operator * ( )
{
return static_cast < node_type_t * > ( node ) ;
}
2012-02-06 12:54:41 +08:00
} ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
iterator begin ( )
{
return iterator ( mouth . prev ) ;
}
iterator end ( )
{
return iterator ( & mouth ) ;
}
2012-02-06 12:54:41 +08:00
} ;
# endif