libmoost
|
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__