Hydra 0.20
hydra.src/desktop/LoadCache.h
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 
 All Classes Namespaces Functions Variables