00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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;
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
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
00270
00271
00272 };
00273
00274 }
00275
00276 #endif