FXLRUCache.h

Go to the documentation of this file.
00001 /********************************************************************************
00002 *                                                                               *
00003 *                    A Generic Least Recently Used Cache                        *
00004 *                                                                               *
00005 *********************************************************************************
00006 *        Copyright (C) 2003 by Niall Douglas.   All Rights Reserved.            *
00007 *       NOTE THAT I DO NOT PERMIT ANY OF MY CODE TO BE PROMOTED TO THE GPL      *
00008 *********************************************************************************
00009 * This code is free software; you can redistribute it and/or modify it under    *
00010 * the terms of the GNU Library General Public License v2.1 as published by the  *
00011 * Free Software Foundation EXCEPT that clause 3 does not apply ie; you may not  *
00012 * "upgrade" this code to the GPL without my prior written permission.           *
00013 * Please consult the file "License_Addendum2.txt" accompanying this file.       *
00014 *                                                                               *
00015 * This code is distributed in the hope that it will be useful,                  *
00016 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
00017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                          *
00018 *********************************************************************************
00019 * $Id:                                                                          *
00020 ********************************************************************************/
00021 
00022 #ifndef FXLRUCACHE_H
00023 #define FXLRUCACHE_H
00024 
00025 #include "FXProcess.h"
00026 #include "qdictbase.h"
00027 #include <list>
00028 
00029 namespace FX {
00030 
00035 namespace FXLRUCacheImpl
00036 {
00037     template<typename Type, typename TypeAsParam, bool isValue> struct getPtr
00038     {
00039         Type item;
00040         getPtr(TypeAsParam _item) : item(_item) { }
00041         Type itemPtr() throw() { return item; }
00042     };
00043     template<typename Type, typename TypeAsParam> struct getPtr<Type, TypeAsParam, true>
00044     {
00045         Type item;
00046         getPtr(TypeAsParam _item) : item(_item) { }
00047         Type *itemPtr() throw() { return &item; }
00048     };
00049 }
00050 
00079 template<class dictbase, class type> class FXLRUCache : protected dictbase
00080 {
00081     template<class cache> friend class FXLRUCacheIterator;
00082 public:
00083     typedef typename dictbase::KeyType KeyType;
00084     typedef typename dictbase::ItemType FundamentalDictType;
00085 protected:
00086     typedef FundamentalDictType *DictType;
00087     typedef typename Generic::select<Generic::sameType<type, Generic::NullType>::value, DictType, type>::value Type;
00088     typedef Generic::TraitsBasic<Type> TypeTraits;
00089     typedef typename Generic::TraitsBasic<KeyType>::asROParam KeyTypeAsParam;
00090     typedef typename TypeTraits::asROParam TypeAsParam;
00091     typedef typename Generic::leastIndir<Type>::value *TypeAsReturn;
00092     FXuint topmax, maximum, cost;
00093     bool amDynamic;
00094     typedef FXLRUCacheImpl::getPtr<Type, TypeAsParam, TypeTraits::isValue> CacheItemBase;
00095     struct CacheItem : public CacheItemBase
00096     {
00097         FXint cost;
00098         KeyType key;
00099         typename std::list<CacheItem>::iterator myit;   // Points to this
00100         CacheItem(FXint _cost, KeyTypeAsParam _key, TypeAsParam _item)
00101             : CacheItemBase(_item), cost(_cost), key(_key) { }
00102     };
00103     mutable std::list<CacheItem> cache;
00104     mutable struct stats_t
00105     {
00106         FXuint inserted, removed, flushed, hits, misses;
00107         stats_t() : inserted(0), removed(0), flushed(0), hits(0), misses(0) { }
00108     } stats;
00113     virtual void purgeLFU()
00114     {
00115         while(cost>maximum)
00116         {
00117             CacheItem *ci=&cache.back();
00118             remove(ci->key);
00119             --stats.removed; ++stats.flushed;
00120         }
00121     }
00122 private:
00123     void dynMax()
00124     {
00125         maximum=amDynamic ? topmax>>FXProcess::memoryFull() : topmax;
00126         if(maximum<cost)
00127             purgeLFU();
00128     }
00129 public:
00131     FXLRUCache(FXuint maxCost=100, FXuint size=13, bool autodel=false)
00132         : dictbase(size), topmax(maxCost), maximum(maxCost), cost(0), amDynamic(true)
00133     {
00134         dynMax();
00135         setAutoDelete(autodel);
00136     }
00137     using dictbase::autoDelete;
00138     void setAutoDelete(bool a)
00139     {
00140         if(!TypeTraits::isValue) dictbase::setAutoDelete(a);
00141     }
00142     using dictbase::count;
00143     using dictbase::isEmpty;
00144     using dictbase::size;
00145     using dictbase::clear;
00146     using dictbase::resize;
00147     using dictbase::spread;
00148 
00150     bool dynamic() const throw() { return amDynamic; }
00152     void setDynamic(bool v) const throw() { amDynamic=v; }
00154     FXuint maxCost() const throw() { return maximum; }
00156     void setMaxCost(FXuint newmax)
00157     {
00158         topmax=newmax;
00159         dynMax();
00160     }
00162     FXuint totalCost() const throw() { return cost; }
00167     void cacheStats(float *hitrate, float *churn) const throw()
00168     {
00169         FXuint lookups=stats.hits+stats.misses;
00170         if(!lookups) lookups=1;
00171         if(hitrate)
00172             *hitrate=100.0*stats.hits/lookups;
00173         if(churn)
00174             *churn=100.0*stats.flushed/lookups;
00175     }
00177     void statistics() const
00178     {
00179 #ifdef DEBUG
00180         float full, slotsspread, avrgkeysperslot, _spread, hitrate, churn;
00181         spread(&full, &slotsspread, &avrgkeysperslot, &_spread);
00182         cacheStats(&hitrate, &churn);
00183         fxmessage("Dictionary size=%d, items=%d, full=%f%%, slot spread=%f%%, avrg keys per slot=%f, overall spread=%f%%\n"
00184                   "Cache hit rate=%d%%, churn=%d%%\n", size(), count(), full, slotsspread, avrgkeysperslot, _spread, hitrate, churn);
00185 #endif
00186     }
00187 
00193     bool insert(KeyTypeAsParam k, TypeAsParam d, FXint itemcost=1)
00194     {
00195         if(cost>maximum) return false;
00196         FXEXCEPTION_STL1 {
00197             cache.push_front(CacheItem(itemcost, k, d));
00198             cache.front().myit=cache.begin();
00199         } FXEXCEPTION_STL2;
00200         dictbase::insert(k, (DictType) &cache.front());
00201         cost+=itemcost; ++stats.inserted;
00202         dynMax();
00203         return true;
00204     }
00206     bool remove(KeyTypeAsParam k)
00207     {
00208         bool ret=dictbase::remove(k);
00209         ++stats.removed;
00210         return ret;
00211     }
00214     TypeAsReturn take(KeyTypeAsParam k)
00215     {
00216         CacheItem *ci=(CacheItem *) dictbase::take(k);
00217         ++stats.removed;
00218         cost-=ci->cost;
00219         TypeAsReturn ret=TypeTraits::isValue ? 0 : ci->itemPtr();
00220         cache.erase(ci->myit);
00221         return ret;
00222     }
00226     TypeAsReturn find(KeyTypeAsParam k, bool tofront=true) const
00227     {
00228         CacheItem *ci=(CacheItem *) dictbase::find(k);
00229         if(!ci)
00230         {
00231             ++stats.misses;
00232             return 0;
00233         }
00234         ++stats.hits;
00235         if(tofront)
00236         {
00237             cache.splice(cache.begin(), cache, ci->myit);
00238             // ci->myit=cache.begin();
00239         }
00240         return ci->itemPtr();
00241     }
00243     TypeAsReturn operator[](KeyTypeAsParam k) const { return find(k); }
00244 
00245 protected:
00246     virtual void deleteItem(DictType d)
00247     {
00248         CacheItem *ci=(CacheItem *) d;
00249         FXint itemcost=ci->cost;
00250         if(!TypeTraits::isValue)
00251             dictbase::deleteItem(ci->itemPtr());
00252         cache.erase(ci->myit);
00253         cost-=itemcost;
00254     }
00255 };
00256 
00264 template<class cache> class FXLRUCacheIterator
00265     : public QDictBaseIterator<typename cache::KeyType, typename cache::FundamentalDictType>
00266 {
00267 public:
00268     FXLRUCacheIterator(const cache &d) : QDictBaseIterator<typename cache::KeyType, typename cache::FundamentalDictType>(d) { }
00269     /* NOTE TO SELF: Relies on the compiler putting FXLRUCacheImpl::getPtr at the
00270     front of CacheItem (undefined behaviour). Likely to be fine on almost any
00271     compiler as it's not virtual and base classes go first */
00272 };
00273 
00274 } // namespace
00275 
00276 #endif

(C) 2002-2009 Niall Douglas. Some parts (C) to assorted authors.
Generated on Fri Nov 20 18:31:23 2009 for TnFOX by doxygen v1.4.7