Hydra 0.20
|
00001 00002 /* 00003 * Copyright (c) 2009 Aleksander B. Demko 00004 * This source code is distributed under the MIT license. 00005 * See the accompanying file LICENSE.MIT.txt for details. 00006 */ 00007 00008 #ifndef __INCLUDED_HYDRADESKTOP_LOADCACHE_H__ 00009 #define __INCLUDED_HYDRADESKTOP_LOADCACHE_H__ 00010 00011 00012 #include <assert.h> 00013 00014 #include <list> 00015 #include <map> 00016 00017 #include <QString> 00018 #include <QDebug> 00019 00020 #include <hydra/TR1.h> 00021 00022 namespace desktop 00023 { 00024 template <class T, class KEY> 00025 class LoadCacheEntry; 00026 00027 template <class T, class KEY> 00028 class LoadCacheBase; 00029 template <class T, class LOADER, class KEY> 00030 class LoadCache; 00031 00032 template <class T, class KEY> 00033 class cache_ptr; 00034 } 00035 00036 // internal class 00037 template <class T, class KEY> 00038 class desktop::LoadCacheEntry 00039 { 00040 public: 00041 std::shared_ptr<T> ptr; 00042 bool isdel; 00043 00044 typename std::list< 00045 typename std::map<KEY,LoadCacheEntry<T,KEY> >::iterator 00046 >::iterator list_iterator; 00047 00048 public: 00049 LoadCacheEntry(void) : isdel(false) { } 00050 }; 00051 00052 /** 00053 * The LoaderCache foundation. 00054 * 00055 * This version doesn't have the automatic loader type stuff. 00056 * 00057 * @author Aleksander Demko 00058 */ 00059 template <class T, class KEY = QString> 00060 class desktop::LoadCacheBase 00061 { 00062 public: 00063 typedef T data_type; 00064 typedef KEY key_type; 00065 typedef desktop::cache_ptr<T,KEY> cache_ptr; 00066 00067 public: 00068 explicit LoadCacheBase(size_t maxhold); 00069 00070 //~LoadCacheBase() { qDebug() << __FUNCTION__; } 00071 00072 bool containsItem(const KEY &key) const; 00073 00074 void insertItem(const KEY &key, const std::shared_ptr<T> &item); 00075 00076 // asserts if the item is not in the cache already 00077 desktop::cache_ptr<T,KEY> getItem(const KEY &key); 00078 00079 protected: 00080 friend class desktop::cache_ptr<T,KEY>; 00081 00082 size_t dm_maxhold, dm_curhold; 00083 00084 std::map<KEY, LoadCacheEntry<T,KEY> > dm_keymap; 00085 std::list< 00086 typename std::map<KEY,LoadCacheEntry<T,KEY> >::iterator 00087 > dm_dellist; 00088 }; 00089 00090 /** 00091 * A LoadCache is a map that is able to manage a group of objects (T) 00092 * keyed by a key KEY, with the given demand LOADER class. 00093 * 00094 * This class will make sure all currently locked (via cache_ptr) 00095 * objects are in memory, aswell as possible some extras (within the 00096 * size maxhold as passed to the constructor 00097 * 00098 * @author Aleksander Demko 00099 */ 00100 template <class T, class LOADER, class KEY = QString> 00101 class desktop::LoadCache : public LoadCacheBase<T,KEY> 00102 { 00103 public: 00104 explicit LoadCache(size_t maxhold, const LOADER &loader = LOADER()) : LoadCacheBase<T,KEY>(maxhold), dm_loader(loader) { } 00105 00106 desktop::cache_ptr<T,KEY> getItem(const KEY &key); 00107 00108 protected: 00109 LOADER dm_loader; 00110 }; 00111 00112 00113 template <class T, class KEY = QString> 00114 class desktop::cache_ptr 00115 { 00116 public: 00117 cache_ptr(void); 00118 cache_ptr(LoadCacheBase<T,KEY> *cache, typename std::map<KEY,LoadCacheEntry<T,KEY> >::iterator &ii); 00119 cache_ptr(const cache_ptr<T,KEY> &rhs); 00120 ~cache_ptr() { cleanup(); } 00121 00122 cache_ptr<T,KEY> & operator = (const cache_ptr<T,KEY> &rhs); 00123 00124 T* get(void) const { return dm_ptr.get(); } 00125 T& ref(void) const { return *dm_ptr; } 00126 T* operator ->(void) const { return dm_ptr.get(); } 00127 T & operator *(void) const { return *dm_ptr; } 00128 00129 bool operator == (const cache_ptr<T,KEY> &rhs) const { return dm_ptr == rhs.dm_ptr; } 00130 bool operator != (const cache_ptr<T,KEY> &rhs) const { return dm_ptr != rhs.dm_ptr; } 00131 bool operator < (const cache_ptr<T,KEY> &rhs) const { return dm_ptr < rhs.dm_ptr; } 00132 00133 private: 00134 void cleanup(void); 00135 00136 private: 00137 LoadCacheBase<T,KEY> *dm_cache; 00138 typename std::map<KEY,LoadCacheEntry<T,KEY> >::iterator dm_iterator; 00139 std::shared_ptr<T> dm_ptr; 00140 }; 00141 00142 template <class T, class KEY> 00143 desktop::LoadCacheBase<T,KEY>::LoadCacheBase(size_t maxhold) 00144 : dm_maxhold(maxhold), dm_curhold(0) 00145 { 00146 } 00147 00148 template <class T, class KEY> 00149 bool desktop::LoadCacheBase<T,KEY>::containsItem(const KEY &key) const 00150 { 00151 return dm_keymap.count(key) > 0; 00152 } 00153 00154 template <class T, class KEY> 00155 void desktop::LoadCacheBase<T,KEY>::insertItem(const KEY &key, const std::shared_ptr<T> &item) 00156 { 00157 assert(!containsItem(key)); 00158 00159 dm_keymap[key].ptr = item; 00160 } 00161 00162 template <class T, class KEY> 00163 desktop::cache_ptr<T,KEY> desktop::LoadCacheBase<T,KEY>::getItem(const KEY &key) 00164 { 00165 typename std::map<KEY, LoadCacheEntry<T,KEY> >::iterator ii(LoadCacheBase<T,KEY>::dm_keymap.find(key)); 00166 00167 assert(ii != (LoadCacheBase<T,KEY>::dm_keymap.end()) ); // need the parens... gcc bug? 00168 assert(ii->second.ptr.get()); 00169 00170 return desktop::cache_ptr<T,KEY>(this, ii); 00171 } 00172 00173 template <class T, class LOADER, class KEY> 00174 desktop::cache_ptr<T,KEY> desktop::LoadCache<T,LOADER,KEY>::getItem(const KEY &key) 00175 { 00176 typename std::map<KEY, LoadCacheEntry<T,KEY> >::iterator ii(LoadCacheBase<T,KEY>::dm_keymap.find(key)); 00177 00178 // load if it need be 00179 if (ii == LoadCacheBase<T,KEY>::dm_keymap.end()) { 00180 // load it 00181 LoadCacheBase<T,KEY>::dm_keymap[key].ptr = dm_loader(key); 00182 ii = LoadCacheBase<T,KEY>::dm_keymap.find(key); 00183 } 00184 assert(ii != (LoadCacheBase<T,KEY>::dm_keymap.end()) ); // need the parens... gcc bug? 00185 assert(ii->second.ptr.get()); 00186 00187 return desktop::cache_ptr<T,KEY>(this, ii); 00188 } 00189 00190 template <class T, class KEY> 00191 desktop::cache_ptr<T,KEY>::cache_ptr(void) 00192 : dm_cache(0) 00193 { 00194 //qDebug() << "cache_ptr(void)"; 00195 } 00196 00197 template <class T, class KEY> 00198 desktop::cache_ptr<T,KEY>::cache_ptr(LoadCacheBase<T,KEY> *cache, typename std::map<KEY,LoadCacheEntry<T,KEY> >::iterator &ii) 00199 : dm_cache(cache), dm_iterator(ii) 00200 { 00201 //qDebug() << "cache_ptr(init)"; 00202 // extraction from cache 00203 00204 assert(cache); 00205 00206 if (dm_iterator->second.isdel) { 00207 // save it from deletion 00208 dm_cache->dm_curhold--; 00209 dm_cache->dm_dellist.erase(dm_iterator->second.list_iterator); 00210 dm_iterator->second.isdel = false; 00211 } 00212 00213 dm_ptr = dm_iterator->second.ptr; 00214 00215 assert(!dm_iterator->second.isdel); 00216 00217 assert(dm_ptr.get()); 00218 assert(!dm_ptr.unique()); 00219 } 00220 00221 template <class T, class KEY> 00222 desktop::cache_ptr<T,KEY>::cache_ptr(const cache_ptr<T,KEY> &rhs) 00223 : dm_cache(rhs.dm_cache), dm_iterator(rhs.dm_iterator), dm_ptr(rhs.dm_ptr) 00224 { 00225 if (dm_cache) { 00226 assert(dm_ptr.get()); 00227 assert(!dm_ptr.unique()); 00228 assert(!dm_iterator->second.isdel); 00229 } 00230 //qDebug() << "cache_ptr(copy)"; 00231 } 00232 00233 /*template <class T, class KEY> 00234 desktop::cache_ptr<T>::~cache_ptr() 00235 { 00236 //qDebug() << "cache_ptr(dtor)"; 00237 cleanup(); 00238 }*/ 00239 00240 template <class T, class KEY> 00241 desktop::cache_ptr<T,KEY> & desktop::cache_ptr<T,KEY>::operator = (const desktop::cache_ptr<T,KEY> &rhs) 00242 { 00243 //qDebug() << "cache_ptr(assignment)"; 00244 // check if we are already a copy 00245 if (dm_cache == rhs.dm_cache && (!dm_cache || dm_iterator == rhs.dm_iterator)) 00246 return *this; 00247 00248 cleanup(); 00249 00250 dm_cache = rhs.dm_cache; 00251 dm_iterator = rhs.dm_iterator; 00252 dm_ptr = rhs.dm_ptr; 00253 00254 if (dm_cache) { 00255 assert(dm_ptr.get()); 00256 assert(!dm_ptr.unique()); 00257 assert(!dm_iterator->second.isdel); 00258 } 00259 00260 return *this; 00261 } 00262 00263 template <class T, class KEY> 00264 void desktop::cache_ptr<T,KEY>::cleanup(void) 00265 { 00266 if (!dm_cache) 00267 return; 00268 00269 //qDebug() << "cache_ptr(cleanup) enter, curhold=" << dm_cache->dm_curhold << " maxhold=" << dm_cache->dm_maxhold; 00270 00271 assert(dm_ptr.get()); 00272 assert(!dm_ptr.unique()); 00273 dm_ptr.reset(); 00274 00275 //assert(dm_cache->dm_dellist.size() == dm_cache->dm_curhold); 00276 00277 //qDebug() << __FUNCTION__ << dm_iterator->second.ptr.get() << "pre_del"; 00278 assert(!dm_iterator->second.isdel); 00279 if (dm_iterator->second.ptr.unique()) { 00280 // add it to the delete queue, as noone else points to it anymore 00281 //qDebug() << __FUNCTION__ << dm_iterator->second.ptr.get() << "ISDEL = true"; 00282 dm_iterator->second.list_iterator = dm_cache->dm_dellist.insert(dm_cache->dm_dellist.end(), dm_iterator); 00283 dm_iterator->second.isdel = true; 00284 dm_cache->dm_curhold++; 00285 00286 // prune the del list as needed 00287 while (dm_cache->dm_curhold > dm_cache->dm_maxhold) { 00288 // delete the front of the list and its map node 00289 //qDebug() << __FUNCTION__ << dm_cache->dm_dellist.front()->second.ptr.get() << "... purged!"; 00290 dm_cache->dm_keymap.erase( dm_cache->dm_dellist.front() ); 00291 dm_cache->dm_dellist.pop_front(); 00292 dm_cache->dm_curhold--; 00293 //qDebug() << "cache_ptr(cleanup) just NUKED one, curhold=" << dm_cache->dm_curhold << " maxhold=" << dm_cache->dm_maxhold; 00294 } 00295 } 00296 00297 dm_cache = 0; 00298 } 00299 00300 /*TEST CODE 00301 00302 #include <desktop/LoadCache.h> 00303 class Data 00304 { 00305 public: 00306 Data(const QString &d) : dm_data("DATA" + d) { qDebug() << __FUNCTION__ << dm_data; } 00307 ~Data() { qDebug() << __FUNCTION__ << dm_data; } 00308 private: 00309 QString dm_data; 00310 }; 00311 class ImageLoader 00312 { 00313 public: 00314 std::shared_ptr<Data> operator()(const QString &fullfilename) { 00315 return std::shared_ptr<Data>(new Data(fullfilename)); 00316 } 00317 }; 00318 using namespace desktop; 00319 void foo(void) 00320 { 00321 //std::list< std::list<int>::iterator > iterator_list; 00322 typedef LoadCache<Data, ImageLoader> cache_type; 00323 cache_type cache(3); 00324 cache_ptr<Data> holder; 00325 00326 { 00327 cache_ptr<Data> p1(cache.getItem("s1")); 00328 cache_ptr<Data> p2(cache.getItem("s2")); 00329 cache_ptr<Data> p3(cache.getItem("s3")); 00330 cache_ptr<Data> p4(cache.getItem("s4")); 00331 cache_ptr<Data> p5(cache.getItem("s5")); 00332 cache_ptr<Data> p6(cache.getItem("s6")); 00333 cache_ptr<Data> p7(cache.getItem("s7")); 00334 cache_ptr<Data> p8(cache.getItem("s8")); 00335 00336 holder = p8; 00337 } 00338 00339 00340 qDebug() << "exiting"; 00341 } 00342 */ 00343 #endif 00344