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.
26 inline unsigned int Cache::MyHashKey(const char* key
) const
28 int nHash
= tolower(key
[0]);
29 for (int i
=1;(i
<KEYSIZE
) && key
[i
];i
++) {
30 nHash
= (nHash
<< 5) ^ tolower(key
[i
]);
32 return nHash
% m_nHashTableSize
;
35 Cache::Cache(int nBlockSize
, int nHashTableSize
)
37 assert( nBlockSize
> 0 );
38 assert( nHashTableSize
> 16 );
41 m_nHashTableSize
= nHashTableSize
; // default size
45 m_nBlockSize
= nBlockSize
;
48 void Cache::InitHashTable(unsigned int nHashSize
, bool bAllocNow
)
49 //Used to force allocation of a hash table or to override the default
50 //hash table size of (which is fairly small)
52 assert( m_nCount
== 0 );
53 assert( nHashSize
> 16 );
55 if (m_pHashTable
!= NULL
) {
62 m_pHashTable
= (Cache::MyAssoc
**) malloc( sizeof( Cache::MyAssoc
* ) * nHashSize
);
63 memset( m_pHashTable
, 0, sizeof( Cache::MyAssoc
* ) * nHashSize
);
65 m_nHashTableSize
= nHashSize
;
68 void Cache::RemoveAll(ReleaseFun fun
)
71 for (unsigned int nHash
= 0; nHash
< m_nHashTableSize
; nHash
++)
74 for (pAssoc
= m_pHashTable
[nHash
]; pAssoc
!= NULL
;
75 pAssoc
= pAssoc
->pNext
)
79 pAssoc
->MyAssoc::~MyAssoc();
91 MemBlock
* p
= m_pBlocks
;
93 MemBlock
* pNext
= p
->pNext
;
106 Cache::MyAssoc
* Cache::NewAssoc()
108 if (m_pFreeList
== NULL
) {
110 Cache::MemBlock
* newBlock
= ( Cache::MemBlock
* ) malloc(m_nBlockSize
* sizeof( Cache::MyAssoc
) + sizeof( Cache::MemBlock
));
111 assert( newBlock
!= NULL
); // we must have something
113 newBlock
->pNext
= m_pBlocks
;
114 m_pBlocks
= newBlock
;
116 // chain them into free list
117 Cache::MyAssoc
* pAssoc
= ( Cache::MyAssoc
* )
119 for (int i
= 0; i
< m_nBlockSize
; i
++) {
120 pAssoc
->pNext
= m_pFreeList
;
121 m_pFreeList
= pAssoc
++;
125 Cache::MyAssoc
* pAssoc
= m_pFreeList
;
126 m_pFreeList
= m_pFreeList
->pNext
;
128 assert( m_nCount
> 0 ); // make sure we don't overflow
137 void Cache::FreeAssoc(Cache::MyAssoc
* pAssoc
)
140 pAssoc
->pNext
->pPrev
=pAssoc
->pPrev
;
142 *pAssoc
->pPrev
= pAssoc
->pNext
;
143 pAssoc
->pNext
= m_pFreeList
;
144 m_pFreeList
= pAssoc
;
146 assert( m_nCount
>= 0 ); // make sure we don't underflow
148 // if no more elements, cleanup completely
154 Cache::MyAssoc
*Cache::GetNextAssoc(Cache::MyAssoc
*Position
) const
156 if (m_pHashTable
== NULL
|| m_nCount
==0) {
160 Cache::MyAssoc
* pAssocRet
= (Cache::MyAssoc
*)Position
;
162 if (pAssocRet
== NULL
)
164 // find the first association
165 for (unsigned int nBucket
= 0; nBucket
< m_nHashTableSize
; nBucket
++)
166 if ((pAssocRet
= m_pHashTable
[nBucket
]) != NULL
)
170 Cache::MyAssoc
* pAssocNext
= pAssocRet
->pNext
;
171 if (pAssocNext
== NULL
)
174 for (unsigned int nBucket
= MyHashKey(pAssocRet
->key
) + 1;
175 nBucket
< m_nHashTableSize
; nBucket
++)
176 if ((pAssocNext
= m_pHashTable
[nBucket
]) != NULL
)
183 Cache::MyAssoc
* Cache::GetAssocAt(const ieResRef key
) const
184 // find association (or return NULL)
186 if (m_pHashTable
== NULL
) {
190 unsigned int nHash
= MyHashKey( key
);
193 Cache::MyAssoc
* pAssoc
;
194 for (pAssoc
= m_pHashTable
[nHash
];
196 pAssoc
= pAssoc
->pNext
) {
197 if (!strnicmp( pAssoc
->key
, key
, KEYSIZE
)) {
204 void *Cache::GetResource(const ieResRef key
) const
206 Cache::MyAssoc
* pAssoc
= GetAssocAt( key
);
207 if (pAssoc
== NULL
) {
215 //returns true if it was successful
216 bool Cache::SetAt(const ieResRef key
, void *rValue
)
220 if (m_pHashTable
== NULL
) {
221 InitHashTable( m_nHashTableSize
);
224 Cache::MyAssoc
* pAssoc
=GetAssocAt( key
);
227 //already exists, but we return true if it is the same
228 return (pAssoc
->data
==rValue
);
231 // it doesn't exist, add a new Association
233 for (i
=0;i
<KEYSIZE
&& key
[i
];i
++) {
234 pAssoc
->key
[i
]=tolower(key
[i
]);
236 for (;i
<KEYSIZE
;i
++) {
240 // put into hash table
241 unsigned int nHash
= MyHashKey(pAssoc
->key
);
242 pAssoc
->pNext
= m_pHashTable
[nHash
];
243 pAssoc
->pPrev
= &m_pHashTable
[nHash
];
245 pAssoc
->pNext
->pPrev
= &pAssoc
->pNext
;
247 m_pHashTable
[nHash
] = pAssoc
;
251 int Cache::RefCount(const ieResRef key
) const
253 Cache::MyAssoc
* pAssoc
=GetAssocAt( key
);
255 return pAssoc
->nRefCount
;
260 int Cache::DecRef(void *data
, const ieResRef key
, bool remove
)
262 Cache::MyAssoc
* pAssoc
;
265 pAssoc
=GetAssocAt( key
);
266 if (pAssoc
&& (pAssoc
->data
==data
) ) {
267 if (!pAssoc
->nRefCount
) {
271 if (remove
&& !pAssoc
->nRefCount
) {
275 return pAssoc
->nRefCount
;
280 pAssoc
=(Cache::MyAssoc
*) GetNextAssoc(NULL
);
283 if (pAssoc
->data
== data
) {
284 if (!pAssoc
->nRefCount
) {
288 if (remove
&& !pAssoc
->nRefCount
) {
292 return pAssoc
->nRefCount
;
294 pAssoc
=GetNextAssoc(pAssoc
);
299 void Cache::Cleanup()
301 Cache::MyAssoc
* pAssoc
=(Cache::MyAssoc
*) GetNextAssoc(NULL
);
305 Cache::MyAssoc
* nextAssoc
= GetNextAssoc(pAssoc
);
306 if (pAssoc
->nRefCount
== 0) {