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.
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 );
39 m_nHashTableSize
= nHashTableSize
; // default size
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
) {
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
)
69 for (unsigned int nHash
= 0; nHash
< m_nHashTableSize
; nHash
++)
72 for (pAssoc
= m_pHashTable
[nHash
]; pAssoc
!= NULL
;
73 pAssoc
= pAssoc
->pNext
)
77 pAssoc
->MyAssoc::~MyAssoc();
89 MemBlock
* p
= m_pBlocks
;
91 MemBlock
* pNext
= p
->pNext
;
104 Cache::MyAssoc
* Cache::NewAssoc()
106 if (m_pFreeList
== NULL
) {
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
* )
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
;
126 assert( m_nCount
> 0 ); // make sure we don't overflow
135 void Cache::FreeAssoc(Cache::MyAssoc
* pAssoc
)
138 pAssoc
->pNext
->pPrev
=pAssoc
->pPrev
;
140 *pAssoc
->pPrev
= pAssoc
->pNext
;
141 pAssoc
->pNext
= m_pFreeList
;
142 m_pFreeList
= pAssoc
;
144 assert( m_nCount
>= 0 ); // make sure we don't underflow
146 // if no more elements, cleanup completely
152 Cache::MyAssoc
*Cache::GetNextAssoc(Cache::MyAssoc
*Position
) const
154 if (m_pHashTable
== NULL
|| m_nCount
==0) {
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
)
168 Cache::MyAssoc
* pAssocNext
= pAssocRet
->pNext
;
169 if (pAssocNext
== NULL
)
172 for (unsigned int nBucket
= MyHashKey(pAssocRet
->key
) + 1;
173 nBucket
< m_nHashTableSize
; nBucket
++)
174 if ((pAssocNext
= m_pHashTable
[nBucket
]) != NULL
)
181 Cache::MyAssoc
* Cache::GetAssocAt(const ieResRef key
) const
182 // find association (or return NULL)
184 if (m_pHashTable
== NULL
) {
188 unsigned int nHash
= MyHashKey( key
);
191 Cache::MyAssoc
* pAssoc
;
192 for (pAssoc
= m_pHashTable
[nHash
];
194 pAssoc
= pAssoc
->pNext
) {
195 if (!strnicmp( pAssoc
->key
, key
, KEYSIZE
)) {
202 void *Cache::GetResource(const ieResRef key
) const
204 Cache::MyAssoc
* pAssoc
= GetAssocAt( key
);
205 if (pAssoc
== NULL
) {
213 //returns true if it was successful
214 bool Cache::SetAt(const ieResRef key
, void *rValue
)
218 if (m_pHashTable
== NULL
) {
219 InitHashTable( m_nHashTableSize
);
222 Cache::MyAssoc
* pAssoc
=GetAssocAt( key
);
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
231 for (i
=0;i
<KEYSIZE
&& key
[i
];i
++) {
232 pAssoc
->key
[i
]=tolower(key
[i
]);
234 for (;i
<KEYSIZE
;i
++) {
238 // put into hash table
239 unsigned int nHash
= MyHashKey(pAssoc
->key
);
240 pAssoc
->pNext
= m_pHashTable
[nHash
];
241 pAssoc
->pPrev
= &m_pHashTable
[nHash
];
243 pAssoc
->pNext
->pPrev
= &pAssoc
->pNext
;
245 m_pHashTable
[nHash
] = pAssoc
;
249 int Cache::RefCount(const ieResRef key
) const
251 Cache::MyAssoc
* pAssoc
=GetAssocAt( key
);
253 return pAssoc
->nRefCount
;
258 int Cache::DecRef(void *data
, const ieResRef key
, bool remove
)
260 Cache::MyAssoc
* pAssoc
;
263 pAssoc
=GetAssocAt( key
);
264 if (pAssoc
&& (pAssoc
->data
==data
) ) {
265 if (!pAssoc
->nRefCount
) {
269 if (remove
&& !pAssoc
->nRefCount
) {
273 return pAssoc
->nRefCount
;
278 pAssoc
=(Cache::MyAssoc
*) GetNextAssoc(NULL
);
281 if (pAssoc
->data
== data
) {
282 if (!pAssoc
->nRefCount
) {
286 if (remove
&& !pAssoc
->nRefCount
) {
290 return pAssoc
->nRefCount
;
292 pAssoc
=GetNextAssoc(pAssoc
);
297 void Cache::Cleanup()
299 Cache::MyAssoc
* pAssoc
=(Cache::MyAssoc
*) GetNextAssoc(NULL
);
303 Cache::MyAssoc
* nextAssoc
= GetNextAssoc(pAssoc
);
304 if (pAssoc
->nRefCount
== 0) {