libmoost
/home/mhx/git/github/libmoost/include/moost/container/lru.hpp
Go to the documentation of this file.
00001 /* vim:set ts=3 sw=3 sts=3 et: */
00028 #ifndef MOOST_CONTAINER_LRU_HPP__
00029 #define MOOST_CONTAINER_LRU_HPP__
00030 
00031 #include <list>
00032 #include <limits>
00033 
00034 #include <boost/functional/hash.hpp>
00035 #include <boost/type_traits/is_integral.hpp>
00036 #include <boost/function.hpp>
00037 
00038 #include "sparse_hash_map.hpp"
00039 
00040 namespace moost { namespace container {
00041 
00042    template <typename Key, bool>
00043    struct get_deleted_key
00044    {
00045       Key operator()() const
00046       { return Key(); } // generic version
00047    };
00048 
00049    template <typename Key>
00050    struct get_deleted_key<Key, true>
00051    {
00052       Key operator()() const
00053       { return (std::numeric_limits<Key>::max)(); } // specialized for integrals
00054    };
00055 
00063    template<class Key,
00064       typename Data,
00065       typename HashFcn = boost::hash<Key> >
00066    class lru
00067    {
00068    private:
00069       typedef std::list< std::pair< Key, Data > > lru_t;
00070 
00071    public:
00072       typedef typename lru_t::value_type value_type;
00073       typedef typename lru_t::value_type::first_type key_type;
00074       typedef typename lru_t::value_type::second_type mapped_type;
00075 
00076       typedef typename lru_t::iterator iterator;
00077       typedef typename lru_t::const_iterator const_iterator;
00078       typedef typename lru_t::reference reference;
00079       typedef typename lru_t::const_reference const_reference;
00080 
00081    private:
00082       typedef google::sparse_hash_map< key_type, iterator, HashFcn > hm_key_data;
00083       typedef boost::function<bool(key_type, const mapped_type & value)> evict_func_t;
00084 
00085       lru_t m_lru; // head = oldest, tail = newest
00086       hm_key_data       m_key_data;
00087       size_t            m_max_size;
00088 
00089    public:
00090 
00092       lru(size_t max_size = std::numeric_limits<size_t>::max()) : m_max_size(max_size)
00093       {
00094          m_key_data.set_deleted_key( get_deleted_key<key_type, boost::is_integral<key_type>::value >()() );
00095       }
00096 
00097       void set_deleted_key(const key_type & deleted_key)
00098       {
00099          m_key_data.set_deleted_key( deleted_key );
00100       }
00101 
00102       // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
00103       // stuff with keys
00104 
00106       bool get(const key_type & key, mapped_type & value, bool bbump = true)
00107       {
00108          iterator itr = find(key);
00109 
00110          if(itr == m_lru.end())
00111             return false;
00112 
00113          // first, move the iterator to the tail of the lru
00114          if(bbump)
00115             bump(itr);
00116 
00117          // now get the value
00118          value = itr->second;
00119          return true;
00120       }
00121 
00125       void put(const key_type & key, const mapped_type & value, evict_func_t evict_func = evict_func_t())
00126       {
00127          insert(key, value, evict_func);
00128       }
00129 
00131       void erase(const key_type & key)
00132       {
00133          iterator itr = find(key);
00134 
00135          if(itr != m_lru.end())
00136             erase(itr);
00137       }
00138 
00140       bool peek(const key_type & key, mapped_type & value)
00141       {
00142          return get(key, value, false);
00143       }
00144 
00145       // bump something to the top of the lru
00146       bool bump(const key_type & key)
00147       {
00148          iterator itr = find(key);
00149 
00150          if(itr == m_lru.end())
00151             return false;
00152 
00153          return bump(itr);
00154       }
00155 
00156       bool exists(const key_type & key) const
00157       {
00158          return find(key) != m_lru.end();
00159       }
00160 
00161       // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
00162       // stuff with iterators
00163 
00164       const_iterator find(const key_type & key) const
00165       {
00166          typename hm_key_data::const_iterator it_key_data = m_key_data.find(key);
00167          return it_key_data == m_key_data.end() ? m_lru.end() : it_key_data->second;
00168       }
00169 
00170       iterator find(const key_type & key)
00171       {
00172          typename hm_key_data::iterator it_key_data = m_key_data.find(key);
00173          return it_key_data == m_key_data.end() ? m_lru.end() : it_key_data->second;
00174       }
00175 
00176       reference front()
00177       {
00178          return m_lru.front();
00179       }
00180 
00181       const_reference front() const
00182       {
00183          return m_lru.front();
00184       }
00185 
00186       reference back()
00187       {
00188          return m_lru.back();
00189       }
00190 
00191       const_reference back() const
00192       {
00193          return m_lru.back();
00194       }
00195 
00196       std::pair<iterator, bool> insert(const key_type & key, const mapped_type & value, evict_func_t evict_func = evict_func_t())
00197       {
00198          if (m_max_size == 0)
00199             m_lru.end();
00200 
00201          typename hm_key_data::iterator it_key_data = m_key_data.find(key);
00202 
00203          bool const existed = (it_key_data != m_key_data.end());
00204 
00205          if (existed)
00206          {
00207             erase(it_key_data->second);
00208          }
00209 
00210          if (m_key_data.size() == m_max_size)
00211          {
00212             if(evict_func)
00213             {
00214                typename std::list< std::pair< key_type, mapped_type > >::iterator it;
00215                for (it = m_lru.begin(); it != m_lru.end(); ++it)
00216                {
00217                   if (evict_func(it->first, it->second))
00218                      break;
00219                }
00220 
00221                if (it == m_lru.end()) // we couldn't evict anything?  then we can't insert this element
00222                {
00223                   evict_func(key, value);
00224                   return std::make_pair(it, existed);
00225                }
00226 
00227                erase(it);
00228             }
00229             else
00230             {
00231                erase(m_lru.begin());
00232             }
00233          }
00234 
00235          // insert
00236          return std::make_pair(
00237             m_key_data[key] = m_lru.insert( m_lru.end(), std::make_pair(key, value) ),
00238             existed
00239          );
00240       }
00241 
00242       bool bump(iterator itr)
00243       {
00244          m_lru.splice(m_lru.end(), m_lru, itr);
00245          return true;
00246       }
00247 
00248       void erase(iterator it)
00249       {
00250          m_key_data.erase(it->first);
00251          m_lru.erase(it);
00252       }
00253 
00254       // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
00255       // operators
00256 
00257       mapped_type const & operator[](key_type const & key) const
00258       {
00259          iterator itr = find(key);
00260          if(itr == m_lru.end()) { throw std::runtime_error("key not found"); }
00261          return itr->second;
00262       }
00263 
00264       mapped_type & operator[](key_type const & key)
00265       {
00266          iterator itr = find(key);
00267          if(itr == m_lru.end()) { throw std::runtime_error("key not found"); }
00268          return itr->second;
00269       }
00270 
00271       // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
00272       // everything else
00273 
00275       void clear()
00276       {
00277           m_key_data.clear();
00278           m_lru.clear();
00279       }
00280 
00282       void purge()
00283       {
00284          clear();
00285 
00286          // We use Scott Meyers "swap trick" to purge memory. This is necessary
00287          // because the underlying structure of a hash map is a vector, which
00288          // doesn't give up its memory without a fight
00289          hm_key_data(m_key_data).swap(m_key_data);
00290       }
00291 
00293       size_t size() const
00294       {
00295          return m_key_data.size();
00296       }
00297 
00299       bool empty() const
00300       {
00301          return m_lru.empty();
00302       }
00303 
00305       size_t max_size() const
00306       {
00307          return m_max_size;
00308       }
00309 
00311       iterator begin()
00312       {
00313          return m_lru.begin();
00314       }
00315 
00317       const_iterator begin() const
00318       {
00319          return m_lru.begin();
00320       }
00321 
00323       iterator end()
00324       {
00325          return m_lru.end();
00326       }
00327 
00329       const_iterator end() const
00330       {
00331          return m_lru.end();
00332       }
00333 
00334    };
00335 
00336 }} // moost::container
00337 
00338 #endif // MOOST_CONTAINER_LRU_HPP__