Constificiation.
[gemrb.git] / gemrb / core / Cache.cpp
blob03afea93bb4757a2247ca21a896a7d5fdaed0c49
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "Cache.h"
23 // private inlines
24 inline unsigned int Cache::MyHashKey(const char* key) const
26 int nHash = tolower(key[0]);
27 for (int i=1;(i<KEYSIZE) && key[i];i++) {
28 nHash = (nHash << 5) ^ tolower(key[i]);
30 return nHash % m_nHashTableSize;
33 Cache::Cache(int nBlockSize, int nHashTableSize)
35 assert( nBlockSize > 0 );
36 assert( nHashTableSize > 16 );
38 m_pHashTable = NULL;
39 m_nHashTableSize = nHashTableSize; // default size
40 m_nCount = 0;
41 m_pFreeList = NULL;
42 m_pBlocks = NULL;
43 m_nBlockSize = nBlockSize;
46 void Cache::InitHashTable(unsigned int nHashSize, bool bAllocNow)
47 //Used to force allocation of a hash table or to override the default
48 //hash table size of (which is fairly small)
50 assert( m_nCount == 0 );
51 assert( nHashSize > 16 );
53 if (m_pHashTable != NULL) {
54 // free hash table
55 free( m_pHashTable);
56 m_pHashTable = NULL;
59 if (bAllocNow) {
60 m_pHashTable = (Cache::MyAssoc **) malloc( sizeof( Cache::MyAssoc * ) * nHashSize );
61 memset( m_pHashTable, 0, sizeof( Cache::MyAssoc * ) * nHashSize );
63 m_nHashTableSize = nHashSize;
66 void Cache::RemoveAll(ReleaseFun fun)
68 if (m_pHashTable) {
69 for (unsigned int nHash = 0; nHash < m_nHashTableSize; nHash++)
71 MyAssoc* pAssoc;
72 for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL;
73 pAssoc = pAssoc->pNext)
75 if (fun)
76 fun(pAssoc->data);
77 pAssoc->MyAssoc::~MyAssoc();
80 // free hash table
81 free( m_pHashTable );
82 m_pHashTable = NULL;
85 m_nCount = 0;
86 m_pFreeList = NULL;
88 // free memory blocks
89 MemBlock* p = m_pBlocks;
90 while (p != NULL) {
91 MemBlock* pNext = p->pNext;
92 free( p );
93 p = pNext;
96 m_pBlocks = NULL;
99 Cache::~Cache()
101 RemoveAll(NULL);
104 Cache::MyAssoc* Cache::NewAssoc()
106 if (m_pFreeList == NULL) {
107 // add another block
108 Cache::MemBlock* newBlock = ( Cache::MemBlock* ) malloc(m_nBlockSize * sizeof( Cache::MyAssoc ) + sizeof( Cache::MemBlock ));
109 assert( newBlock != NULL ); // we must have something
111 newBlock->pNext = m_pBlocks;
112 m_pBlocks = newBlock;
114 // chain them into free list
115 Cache::MyAssoc* pAssoc = ( Cache::MyAssoc* )
116 ( newBlock + 1 );
117 for (int i = 0; i < m_nBlockSize; i++) {
118 pAssoc->pNext = m_pFreeList;
119 m_pFreeList = pAssoc++;
123 Cache::MyAssoc* pAssoc = m_pFreeList;
124 m_pFreeList = m_pFreeList->pNext;
125 m_nCount++;
126 assert( m_nCount > 0 ); // make sure we don't overflow
127 #ifdef _DEBUG
128 pAssoc->key[0] = 0;
129 pAssoc->data = 0;
130 #endif
131 pAssoc->nRefCount=1;
132 return pAssoc;
135 void Cache::FreeAssoc(Cache::MyAssoc* pAssoc)
137 if(pAssoc->pNext) {
138 pAssoc->pNext->pPrev=pAssoc->pPrev;
140 *pAssoc->pPrev = pAssoc->pNext;
141 pAssoc->pNext = m_pFreeList;
142 m_pFreeList = pAssoc;
143 m_nCount--;
144 assert( m_nCount >= 0 ); // make sure we don't underflow
146 // if no more elements, cleanup completely
147 if (m_nCount == 0) {
148 RemoveAll(NULL);
152 Cache::MyAssoc *Cache::GetNextAssoc(Cache::MyAssoc *Position) const
154 if (m_pHashTable == NULL || m_nCount==0) {
155 return NULL;
158 Cache::MyAssoc* pAssocRet = (Cache::MyAssoc*)Position;
160 if (pAssocRet == NULL)
162 // find the first association
163 for (unsigned int nBucket = 0; nBucket < m_nHashTableSize; nBucket++)
164 if ((pAssocRet = m_pHashTable[nBucket]) != NULL)
165 break;
166 return pAssocRet;
168 Cache::MyAssoc* pAssocNext = pAssocRet->pNext;
169 if (pAssocNext == NULL)
171 // go to next bucket
172 for (unsigned int nBucket = MyHashKey(pAssocRet->key) + 1;
173 nBucket < m_nHashTableSize; nBucket++)
174 if ((pAssocNext = m_pHashTable[nBucket]) != NULL)
175 break;
178 return pAssocNext;
181 Cache::MyAssoc* Cache::GetAssocAt(const ieResRef key) const
182 // find association (or return NULL)
184 if (m_pHashTable == NULL) {
185 return NULL;
188 unsigned int nHash = MyHashKey( key );
190 // see if it exists
191 Cache::MyAssoc* pAssoc;
192 for (pAssoc = m_pHashTable[nHash];
193 pAssoc != NULL;
194 pAssoc = pAssoc->pNext) {
195 if (!strnicmp( pAssoc->key, key, KEYSIZE )) {
196 return pAssoc;
199 return NULL;
202 void *Cache::GetResource(const ieResRef key) const
204 Cache::MyAssoc* pAssoc = GetAssocAt( key );
205 if (pAssoc == NULL) {
206 return false;
207 } // not in map
209 pAssoc->nRefCount++;
210 return pAssoc->data;
213 //returns true if it was successful
214 bool Cache::SetAt(const ieResRef key, void *rValue)
216 int i;
218 if (m_pHashTable == NULL) {
219 InitHashTable( m_nHashTableSize );
222 Cache::MyAssoc* pAssoc=GetAssocAt( key );
224 if (pAssoc) {
225 //already exists, but we return true if it is the same
226 return (pAssoc->data==rValue);
229 // it doesn't exist, add a new Association
230 pAssoc = NewAssoc();
231 for (i=0;i<KEYSIZE && key[i];i++) {
232 pAssoc->key[i]=tolower(key[i]);
234 for (;i<KEYSIZE;i++) {
235 pAssoc->key[i]=0;
237 pAssoc->data=rValue;
238 // put into hash table
239 unsigned int nHash = MyHashKey(pAssoc->key);
240 pAssoc->pNext = m_pHashTable[nHash];
241 pAssoc->pPrev = &m_pHashTable[nHash];
242 if (pAssoc->pNext) {
243 pAssoc->pNext->pPrev = &pAssoc->pNext;
245 m_pHashTable[nHash] = pAssoc;
246 return true;
249 int Cache::RefCount(const ieResRef key) const
251 Cache::MyAssoc* pAssoc=GetAssocAt( key );
252 if (pAssoc) {
253 return pAssoc->nRefCount;
255 return -1;
258 int Cache::DecRef(void *data, const ieResRef key, bool remove)
260 Cache::MyAssoc* pAssoc;
262 if (key) {
263 pAssoc=GetAssocAt( key );
264 if (pAssoc && (pAssoc->data==data) ) {
265 if (!pAssoc->nRefCount) {
266 return -1;
268 --pAssoc->nRefCount;
269 if (remove && !pAssoc->nRefCount) {
270 FreeAssoc(pAssoc);
271 return 0;
273 return pAssoc->nRefCount;
275 return -1;
278 pAssoc=(Cache::MyAssoc *) GetNextAssoc(NULL);
280 while (pAssoc) {
281 if (pAssoc->data == data) {
282 if (!pAssoc->nRefCount) {
283 return -1;
285 --pAssoc->nRefCount;
286 if (remove && !pAssoc->nRefCount) {
287 FreeAssoc(pAssoc);
288 return 0;
290 return pAssoc->nRefCount;
292 pAssoc=GetNextAssoc(pAssoc);
294 return -1;
297 void Cache::Cleanup()
299 Cache::MyAssoc* pAssoc=(Cache::MyAssoc *) GetNextAssoc(NULL);
301 while (pAssoc)
303 Cache::MyAssoc* nextAssoc = GetNextAssoc(pAssoc);
304 if (pAssoc->nRefCount == 0) {
305 FreeAssoc(pAssoc);
307 pAssoc=nextAssoc;