00001 /****************************************************************************** 00002 * 00003 * $Id: classlist.cpp,v 1.14 2001/03/19 19:27:39 root Exp $ 00004 * 00005 * Copyright (C) 1997-2008 by Dimitri van Heesch. 00006 * 00007 * Permission to use, copy, modify, and distribute this software and its 00008 * documentation under the terms of the GNU General Public License is hereby 00009 * granted. No representations are made about the suitability of this software 00010 * for any purpose. It is provided "as is" without express or implied warranty. 00011 * See the GNU General Public License for more details. 00012 * 00013 * Documents produced by Doxygen are derivative works derived from the 00014 * input used in their production; they are not affected by this license. 00015 * 00016 */ 00017 00018 #include <stdio.h> 00019 #include <assert.h> 00020 #include <qglobal.h> 00021 #include "objcache.h" 00022 00023 //---------------------------------------------------------------------- 00024 00025 #ifdef CACHE_STATS 00026 int ObjCache::misses = 0; 00027 int ObjCache::hits = 0; 00028 #endif 00029 00030 //---------------------------------------------------------------------- 00031 00032 ObjCache::ObjCache(unsigned int logSize) 00033 : m_head(-1), m_tail(-1), //m_numEntries(0), 00034 m_size(1<<logSize), m_freeHashNodes(0), m_freeCacheNodes(0), m_lastHandle(-1) 00035 { 00036 int i; 00037 m_cache = new CacheNode[m_size]; 00038 m_hash = new HashNode[m_size]; 00039 // add all items to list of free buckets 00040 for (i=0;i<m_size-1;i++) 00041 { 00042 m_hash[i].nextHash = i+1; 00043 m_cache[i].next = i+1; 00044 } 00045 } 00046 00047 ObjCache::~ObjCache() 00048 { 00049 delete[] m_cache; 00050 delete[] m_hash; 00051 } 00052 00053 int ObjCache::add(void *obj,void **victim) 00054 { 00055 *victim=0; 00056 00057 HashNode *hnode = hashFind(obj); 00058 //printf("hnode=%p\n",hnode); 00059 if (hnode) // move object to the front of the LRU list, since it is used 00060 // most recently 00061 { 00062 //printf("moveToFront=%d\n",hnode->index); 00063 moveToFront(hnode->index); 00064 #ifdef CACHE_STATS 00065 hits++; 00066 #endif 00067 } 00068 else // object not in the cache. 00069 { 00070 void *lruObj=0; 00071 if (m_freeCacheNodes!=-1) // cache not full -> add element to the cache 00072 { 00073 // remove element from free list 00074 int index = m_freeCacheNodes; 00075 m_freeCacheNodes = m_cache[index].next; 00076 00077 // add to head of the list 00078 if (m_tail==-1) 00079 { 00080 m_tail = index; 00081 } 00082 m_cache[index].prev = -1; 00083 m_cache[index].next = m_head; 00084 if (m_head!=-1) 00085 { 00086 m_cache[m_head].prev = index; 00087 } 00088 m_head = index; 00089 } 00090 else // cache full -> replace element in the cache 00091 { 00092 //printf("Cache full!\n"); 00093 lruObj = m_cache[m_tail].obj; 00094 hashRemove(lruObj); 00095 moveToFront(m_tail); // m_tail indexes the emptied element, which becomes m_head 00096 } 00097 //printf("numEntries=%d size=%d\n",m_numEntries,m_size); 00098 m_cache[m_head].obj = obj; 00099 hnode = hashInsert(obj); 00100 hnode->index = m_head; 00101 *victim = lruObj; 00102 #ifdef CACHE_STATS 00103 misses++; 00104 #endif 00105 } 00106 return m_head; 00107 } 00108 00109 void ObjCache::del(int index) 00110 { 00111 assert(index!=-1); 00112 assert(m_cache[index].obj!=0); 00113 hashRemove(m_cache[index].obj); 00114 moveToFront(index); 00115 m_head = m_cache[index].next; 00116 if (m_head==-1) 00117 m_tail=-1; 00118 else 00119 m_cache[m_head].prev=-1; 00120 m_cache[index].obj=0; 00121 m_cache[index].prev=-1; 00122 m_cache[index].next = m_freeCacheNodes; 00123 m_freeCacheNodes = index; 00124 } 00125 00126 #ifdef CACHE_DEBUG 00127 #define cache_debug_printf printf 00128 void ObjCache::printLRU() 00129 { 00130 cache_debug_printf("MRU->LRU: "); 00131 int index = m_head; 00132 while (index!=-1) 00133 { 00134 cache_debug_printf("%d=%p ",index,m_cache[index].obj); 00135 index = m_cache[index].next; 00136 } 00137 cache_debug_printf("\n"); 00138 00139 cache_debug_printf("LRU->MRU: "); 00140 index = m_tail; 00141 while (index!=-1) 00142 { 00143 cache_debug_printf("%d=%p ",index,m_cache[index].obj); 00144 index = m_cache[index].prev; 00145 } 00146 cache_debug_printf("\n"); 00147 } 00148 #endif 00149 00150 #ifdef CACHE_STATS 00151 #define cache_stats_printf printf 00152 void ObjCache::printStats() 00153 { 00154 cache_stats_printf("ObjCache: hits=%d misses=%d hit ratio=%f\n",hits,misses,hits*100.0/(hits+misses)); 00155 } 00156 #endif 00157 00158 void ObjCache::moveToFront(int index) 00159 { 00160 int prev,next; 00161 if (m_head!=index) 00162 { 00163 next = m_cache[index].next; 00164 prev = m_cache[index].prev; 00165 00166 // de-chain node at index 00167 m_cache[prev].next = next; 00168 if (next!=-1) m_cache[next].prev = prev; else m_tail = prev; 00169 00170 // add to head 00171 m_cache[index].prev = -1; 00172 m_cache[index].next = m_head; 00173 m_cache[m_head].prev = index; 00174 m_head = index; 00175 } 00176 } 00177 00178 unsigned int ObjCache::hash(void *addr) 00179 { 00180 static bool isPtr64 = sizeof(addr)==8; 00181 if (isPtr64) 00182 { 00183 uint64 key = (uint64)addr; 00184 // Thomas Wang's 64 bit Mix Function 00185 key += ~(key << 32); 00186 key ^= (key >> 22); 00187 key += ~(key << 13); 00188 key ^= (key >> 8); 00189 key += (key << 3); 00190 key ^= (key >> 15); 00191 key += ~(key << 27); 00192 key ^= (key >> 31); 00193 return key & (m_size-1); 00194 } 00195 else 00196 { 00197 // Thomas Wang's 32 bit Mix Function 00198 unsigned long key = (unsigned long)addr; 00199 key += ~(key << 15); 00200 key ^= (key >> 10); 00201 key += (key << 3); 00202 key ^= (key >> 6); 00203 key += ~(key << 11); 00204 key ^= (key >> 16); 00205 return key & (m_size-1); 00206 } 00207 } 00208 00209 ObjCache::HashNode *ObjCache::hashFind(void *obj) 00210 { 00211 HashNode *node = 0; 00212 int index = m_hash[hash(obj)].head; 00213 //printf("hashFind: obj=%p index=%d\n",obj,index); 00214 while (index!=-1 && 00215 m_hash[index].obj!=obj 00216 ) // search for right object in the list 00217 { 00218 index = m_hash[index].nextHash; 00219 } 00220 // found the obj at index, so it is in the cache! 00221 if (index!=-1) 00222 { 00223 node = &m_hash[index]; 00224 } 00225 return node; 00226 } 00227 00228 ObjCache::HashNode *ObjCache::hashInsert(void *obj) 00229 { 00230 int index = hash(obj); 00231 //printf("Inserting %p index=%d\n",obj,index); 00232 00233 // remove element from empty list 00234 int newElement = m_freeHashNodes; 00235 assert(newElement!=-1); 00236 m_freeHashNodes = m_hash[m_freeHashNodes].nextHash; 00237 00238 if (m_hash[index].head!=-1) // hash collision -> goto end of the list 00239 { 00240 index = m_hash[index].head; 00241 while (m_hash[index].nextHash!=-1) 00242 { 00243 index = m_hash[index].nextHash; 00244 } 00245 // add to end of the list 00246 m_hash[index].nextHash = newElement; 00247 } 00248 else // first element in the hash list 00249 { 00250 m_hash[index].head = newElement; 00251 } 00252 // add to the end of the list 00253 m_hash[newElement].nextHash = -1; 00254 m_hash[newElement].obj = obj; 00255 return &m_hash[newElement]; 00256 } 00257 00258 void ObjCache::hashRemove(void *obj) 00259 { 00260 int index = hash(obj); 00261 00262 // find element 00263 int curIndex = m_hash[index].head; 00264 int prevIndex=-1; 00265 while (m_hash[curIndex].obj!=obj) 00266 { 00267 prevIndex = curIndex; 00268 curIndex = m_hash[curIndex].nextHash; 00269 } 00270 00271 if (prevIndex==-1) // remove from start 00272 { 00273 m_hash[index].head = m_hash[curIndex].nextHash; 00274 } 00275 else // remove in the middle 00276 { 00277 m_hash[prevIndex].nextHash = m_hash[curIndex].nextHash; 00278 } 00279 00280 // add curIndex element to empty list 00281 m_hash[curIndex].nextHash = m_freeHashNodes; 00282 m_hash[curIndex].index = -1; 00283 m_hash[curIndex].obj = 0; 00284 m_freeHashNodes = curIndex; 00285 } 00286 00287 #ifdef CACHE_TEST 00288 int main() 00289 { 00290 int i; 00291 struct obj 00292 { 00293 obj() : handle(-1) {} 00294 int handle; 00295 }; 00296 obj *objs = new obj[100]; 00297 ObjCache c(3); 00298 for (i=0;i<32;i++) 00299 { 00300 int objId=(i%3)+(i>>2)*4; 00301 printf("------- use(%d=%p)--------\n",objId,&objs[objId]); 00302 #ifdef CACHE_DEBUG 00303 c.printLRU(); 00304 #endif 00305 obj *victim=0; 00306 if (objs[objId].handle==-1) 00307 { 00308 objs[objId].handle = c.add(&objs[objId],(void**)&victim); 00309 if (victim) victim->handle=-1; 00310 } 00311 else 00312 { 00313 c.use(objs[objId].handle); 00314 } 00315 printf("i=%d objId=%d using %p victim=%p\n",i,objId,&objs[objId],victim); 00316 } 00317 for (i=0;i<100;i++) 00318 { 00319 if (objs[i].handle!=-1) 00320 { 00321 printf("------ del objId=%d handle=%d ------\n",i,objs[i].handle); 00322 c.del(objs[i].handle); 00323 objs[i].handle=-1; 00324 #ifdef CACHE_DEBUG 00325 c.printLRU(); 00326 #endif 00327 } 00328 } 00329 c.printStats(); 00330 return 0; 00331 } 00332 #endif