1 #ifndef _DEPOOLHASH_H 2 #define _DEPOOLHASH_H 3 /*------------------------------------------------------------------------- 4 * drawElements Memory Pool Library 5 * -------------------------------- 6 * 7 * Copyright 2014 The Android Open Source Project 8 * 9 * Licensed under the Apache License, Version 2.0 (the "License"); 10 * you may not use this file except in compliance with the License. 11 * You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, software 16 * distributed under the License is distributed on an "AS IS" BASIS, 17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 * See the License for the specific language governing permissions and 19 * limitations under the License. 20 * 21 *//*! 22 * \file 23 * \brief Memory pool hash class. 24 *//*--------------------------------------------------------------------*/ 25 26 #include "deDefs.h" 27 #include "deMemPool.h" 28 #include "dePoolArray.h" 29 #include "deInt32.h" 30 31 #include <string.h> /* memset() */ 32 33 enum 34 { 35 DE_HASH_ELEMENTS_PER_SLOT = 4 36 }; 37 38 DE_BEGIN_EXTERN_C 39 40 void dePoolHash_selfTest (void); 41 42 DE_END_EXTERN_C 43 44 /*--------------------------------------------------------------------*//*! 45 * \brief Declare a template pool hash class interface. 46 * \param TYPENAME Type name of the declared hash. 47 * \param KEYTYPE Type of the key. 48 * \param VALUETYPE Type of the value. 49 * 50 * This macro declares the interface for a hash. For the implementation of 51 * the hash, see DE_IMPLEMENT_POOL_HASH. Usually this macro is put into the 52 * header file and the implementation macro is put in some .c file. 53 * 54 * \todo [petri] Detailed description. 55 * 56 * The functions for operating the hash are: 57 * \todo [petri] Figure out how to comment these in Doxygen-style. 58 * 59 * \code 60 * Hash* Hash_create (deMemPool* pool); 61 * int Hash_getNumElements (const Hash* hash); 62 * Value* Hash_find (Hash* hash, Key key); 63 * deBool Hash_insert (Hash* hash, Key key, Value value); 64 * void Hash_delete (Hash* hash, Key key); 65 * \endcode 66 *//*--------------------------------------------------------------------*/ 67 #define DE_DECLARE_POOL_HASH(TYPENAME, KEYTYPE, VALUETYPE) \ 68 \ 69 typedef struct TYPENAME##Slot_s TYPENAME##Slot; \ 70 \ 71 struct TYPENAME##Slot_s \ 72 { \ 73 int numUsed; \ 74 TYPENAME##Slot* nextSlot; \ 75 KEYTYPE keys[DE_HASH_ELEMENTS_PER_SLOT]; \ 76 VALUETYPE values[DE_HASH_ELEMENTS_PER_SLOT]; \ 77 }; \ 78 \ 79 typedef struct TYPENAME##_s \ 80 { \ 81 deMemPool* pool; \ 82 int numElements; \ 83 \ 84 int slotTableSize; \ 85 TYPENAME##Slot** slotTable; \ 86 TYPENAME##Slot* slotFreeList; \ 87 } TYPENAME; /* NOLINT(TYPENAME) */ \ 88 \ 89 typedef struct TYPENAME##Iter_s \ 90 { \ 91 const TYPENAME* hash; \ 92 int curSlotIndex; \ 93 const TYPENAME##Slot* curSlot; \ 94 int curElemIndex; \ 95 } TYPENAME##Iter; \ 96 \ 97 TYPENAME* TYPENAME##_create (deMemPool* pool); \ 98 void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) hash); \ 99 deBool TYPENAME##_reserve (DE_PTR_TYPE(TYPENAME) hash, int capacity); \ 100 VALUETYPE* TYPENAME##_find (const TYPENAME* hash, KEYTYPE key); \ 101 deBool TYPENAME##_insert (DE_PTR_TYPE(TYPENAME) hash, KEYTYPE key, VALUETYPE value); \ 102 void TYPENAME##_delete (DE_PTR_TYPE(TYPENAME) hash, KEYTYPE key); \ 103 \ 104 DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* hash) DE_UNUSED_FUNCTION; \ 105 DE_INLINE void TYPENAME##Iter_init (const TYPENAME* hash, TYPENAME##Iter* iter) DE_UNUSED_FUNCTION; \ 106 DE_INLINE deBool TYPENAME##Iter_hasItem (const TYPENAME##Iter* iter) DE_UNUSED_FUNCTION; \ 107 DE_INLINE void TYPENAME##Iter_next (TYPENAME##Iter* iter) DE_UNUSED_FUNCTION; \ 108 DE_INLINE KEYTYPE TYPENAME##Iter_getKey (const TYPENAME##Iter* iter) DE_UNUSED_FUNCTION; \ 109 DE_INLINE VALUETYPE TYPENAME##Iter_getValue (const TYPENAME##Iter* iter) DE_UNUSED_FUNCTION; \ 110 \ 111 DE_INLINE int TYPENAME##_getNumElements (const TYPENAME* hash) \ 112 { \ 113 return hash->numElements; \ 114 } \ 115 \ 116 DE_INLINE void TYPENAME##Iter_init (const TYPENAME* hash, TYPENAME##Iter* iter) \ 117 { \ 118 iter->hash = hash; \ 119 iter->curSlotIndex = 0; \ 120 iter->curSlot = DE_NULL; \ 121 iter->curElemIndex = 0; \ 122 if (TYPENAME##_getNumElements(hash) > 0) \ 123 { \ 124 int slotTableSize = hash->slotTableSize; \ 125 int slotNdx = 0; \ 126 while (slotNdx < slotTableSize) \ 127 { \ 128 if (hash->slotTable[slotNdx]) \ 129 break; \ 130 slotNdx++; \ 131 } \ 132 DE_ASSERT(slotNdx < slotTableSize); \ 133 iter->curSlotIndex = slotNdx; \ 134 iter->curSlot = hash->slotTable[slotNdx]; \ 135 DE_ASSERT(iter->curSlot); \ 136 } \ 137 } \ 138 \ 139 DE_INLINE deBool TYPENAME##Iter_hasItem (const TYPENAME##Iter* iter) \ 140 { \ 141 return (iter->curSlot != DE_NULL); \ 142 } \ 143 \ 144 DE_INLINE void TYPENAME##Iter_next (TYPENAME##Iter* iter) \ 145 { \ 146 DE_ASSERT(TYPENAME##Iter_hasItem(iter)); \ 147 if (++iter->curElemIndex == iter->curSlot->numUsed) \ 148 { \ 149 iter->curElemIndex = 0; \ 150 if (iter->curSlot->nextSlot) \ 151 { \ 152 iter->curSlot = iter->curSlot->nextSlot; \ 153 } \ 154 else \ 155 { \ 156 const TYPENAME* hash = iter->hash; \ 157 int curSlotIndex = iter->curSlotIndex; \ 158 int slotTableSize = hash->slotTableSize; \ 159 while (++curSlotIndex < slotTableSize) \ 160 { \ 161 if (hash->slotTable[curSlotIndex]) \ 162 break; \ 163 } \ 164 iter->curSlotIndex = curSlotIndex; \ 165 if (curSlotIndex < slotTableSize) \ 166 iter->curSlot = hash->slotTable[curSlotIndex]; \ 167 else \ 168 iter->curSlot = DE_NULL; \ 169 } \ 170 } \ 171 } \ 172 \ 173 DE_INLINE KEYTYPE TYPENAME##Iter_getKey (const TYPENAME##Iter* iter) \ 174 { \ 175 DE_ASSERT(TYPENAME##Iter_hasItem(iter)); \ 176 return iter->curSlot->keys[iter->curElemIndex]; \ 177 } \ 178 \ 179 DE_INLINE VALUETYPE TYPENAME##Iter_getValue (const TYPENAME##Iter* iter) \ 180 { \ 181 DE_ASSERT(TYPENAME##Iter_hasItem(iter)); \ 182 return iter->curSlot->values[iter->curElemIndex]; \ 183 } \ 184 \ 185 struct TYPENAME##Unused_s { int unused; } 186 187 /*--------------------------------------------------------------------*//*! 188 * \brief Implement a template pool hash class. 189 * \param TYPENAME Type name of the declared hash. 190 * \param KEYTYPE Type of the key. 191 * \param VALUETYPE Type of the value. 192 * \param HASHFUNC Function used for hashing the key. 193 * \param CMPFUNC Function used for exact matching of the keys. 194 * 195 * This macro has implements the hash declared with DE_DECLARE_POOL_HASH. 196 * Usually this macro should be used from a .c file, since the macro expands 197 * into multiple functions. The TYPENAME, KEYTYPE, and VALUETYPE parameters 198 * must match those of the declare macro. 199 *//*--------------------------------------------------------------------*/ 200 #define DE_IMPLEMENT_POOL_HASH(TYPENAME, KEYTYPE, VALUETYPE, HASHFUNC, CMPFUNC) \ 201 \ 202 TYPENAME* TYPENAME##_create (deMemPool* pool) \ 203 { \ 204 /* Alloc struct. */ \ 205 DE_PTR_TYPE(TYPENAME) hash = DE_POOL_NEW(pool, TYPENAME); \ 206 if (!hash) \ 207 return DE_NULL; \ 208 \ 209 memset(hash, 0, sizeof(TYPENAME)); \ 210 hash->pool = pool; \ 211 \ 212 return hash; \ 213 } \ 214 \ 215 void TYPENAME##_reset (DE_PTR_TYPE(TYPENAME) hash) \ 216 { \ 217 int slotNdx; \ 218 for (slotNdx = 0; slotNdx < hash->slotTableSize; slotNdx++) \ 219 { \ 220 TYPENAME##Slot* slot = hash->slotTable[slotNdx]; \ 221 while (slot) \ 222 { \ 223 TYPENAME##Slot* nextSlot = slot->nextSlot; \ 224 slot->nextSlot = hash->slotFreeList; \ 225 hash->slotFreeList = slot; \ 226 slot->numUsed = 0; \ 227 slot = nextSlot; \ 228 } \ 229 hash->slotTable[slotNdx] = DE_NULL; \ 230 } \ 231 hash->numElements = 0; \ 232 } \ 233 \ 234 TYPENAME##Slot* TYPENAME##_allocSlot (DE_PTR_TYPE(TYPENAME) hash) \ 235 { \ 236 TYPENAME##Slot* slot; \ 237 if (hash->slotFreeList) \ 238 { \ 239 slot = hash->slotFreeList; \ 240 hash->slotFreeList = hash->slotFreeList->nextSlot; \ 241 } \ 242 else \ 243 slot = (TYPENAME##Slot*)deMemPool_alloc(hash->pool, sizeof(TYPENAME##Slot) * DE_HASH_ELEMENTS_PER_SLOT); \ 244 \ 245 if (slot) \ 246 { \ 247 slot->nextSlot = DE_NULL; \ 248 slot->numUsed = 0; \ 249 } \ 250 \ 251 return slot; \ 252 } \ 253 \ 254 deBool TYPENAME##_rehash (DE_PTR_TYPE(TYPENAME) hash, int newSlotTableSize) \ 255 { \ 256 DE_ASSERT(deIsPowerOfTwo32(newSlotTableSize) && newSlotTableSize > 0); \ 257 if (newSlotTableSize > hash->slotTableSize) \ 258 { \ 259 TYPENAME##Slot** oldSlotTable = hash->slotTable; \ 260 TYPENAME##Slot** newSlotTable = (TYPENAME##Slot**)deMemPool_alloc(hash->pool, sizeof(TYPENAME##Slot*) * (size_t)newSlotTableSize); \ 261 int oldSlotTableSize = hash->slotTableSize; \ 262 int slotNdx; \ 263 \ 264 if (!newSlotTable) \ 265 return DE_FALSE; \ 266 \ 267 for (slotNdx = 0; slotNdx < oldSlotTableSize; slotNdx++) \ 268 newSlotTable[slotNdx] = oldSlotTable[slotNdx]; \ 269 \ 270 for (slotNdx = oldSlotTableSize; slotNdx < newSlotTableSize; slotNdx++) \ 271 newSlotTable[slotNdx] = DE_NULL; \ 272 \ 273 hash->slotTableSize = newSlotTableSize; \ 274 hash->slotTable = newSlotTable; \ 275 \ 276 for (slotNdx = 0; slotNdx < oldSlotTableSize; slotNdx++) \ 277 { \ 278 TYPENAME##Slot* slot = oldSlotTable[slotNdx]; \ 279 newSlotTable[slotNdx] = DE_NULL; \ 280 while (slot) \ 281 { \ 282 int elemNdx; \ 283 for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \ 284 { \ 285 hash->numElements--; \ 286 if (!TYPENAME##_insert(hash, slot->keys[elemNdx], slot->values[elemNdx])) \ 287 return DE_FALSE; \ 288 } \ 289 slot = slot->nextSlot; \ 290 } \ 291 } \ 292 } \ 293 \ 294 return DE_TRUE; \ 295 } \ 296 \ 297 VALUETYPE* TYPENAME##_find (const TYPENAME* hash, KEYTYPE key) \ 298 { \ 299 if (hash->numElements > 0) \ 300 { \ 301 int slotNdx = (int)(HASHFUNC(key) & (deUint32)(hash->slotTableSize - 1)); \ 302 TYPENAME##Slot* slot = hash->slotTable[slotNdx]; \ 303 DE_ASSERT(deInBounds32(slotNdx, 0, hash->slotTableSize)); \ 304 \ 305 while (slot) \ 306 { \ 307 int elemNdx; \ 308 for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \ 309 { \ 310 if (CMPFUNC(slot->keys[elemNdx], key)) \ 311 return &slot->values[elemNdx]; \ 312 } \ 313 slot = slot->nextSlot; \ 314 } \ 315 } \ 316 \ 317 return DE_NULL; \ 318 } \ 319 \ 320 deBool TYPENAME##_insert (DE_PTR_TYPE(TYPENAME) hash, KEYTYPE key, VALUETYPE value) \ 321 { \ 322 int slotNdx; \ 323 TYPENAME##Slot* slot; \ 324 \ 325 DE_ASSERT(!TYPENAME##_find(hash, key)); \ 326 \ 327 if ((hash->numElements + 1) >= hash->slotTableSize * DE_HASH_ELEMENTS_PER_SLOT) \ 328 if (!TYPENAME##_rehash(hash, deMax32(4, 2*hash->slotTableSize))) \ 329 return DE_FALSE; \ 330 \ 331 slotNdx = (int)(HASHFUNC(key) & (deUint32)(hash->slotTableSize - 1)); \ 332 DE_ASSERT(slotNdx >= 0 && slotNdx < hash->slotTableSize); \ 333 slot = hash->slotTable[slotNdx]; \ 334 \ 335 if (!slot) \ 336 { \ 337 slot = TYPENAME##_allocSlot(hash); \ 338 if (!slot) return DE_FALSE; \ 339 hash->slotTable[slotNdx] = slot; \ 340 } \ 341 \ 342 for (;;) \ 343 { \ 344 if (slot->numUsed == DE_HASH_ELEMENTS_PER_SLOT) \ 345 { \ 346 if (slot->nextSlot) \ 347 slot = slot->nextSlot; \ 348 else \ 349 { \ 350 TYPENAME##Slot* nextSlot = TYPENAME##_allocSlot(hash); \ 351 if (!nextSlot) return DE_FALSE; \ 352 slot->nextSlot = nextSlot; \ 353 slot = nextSlot; \ 354 } \ 355 } \ 356 else \ 357 { \ 358 slot->keys[slot->numUsed] = key; \ 359 slot->values[slot->numUsed] = value; \ 360 slot->numUsed++; \ 361 hash->numElements++; \ 362 return DE_TRUE; \ 363 } \ 364 } \ 365 } \ 366 \ 367 void TYPENAME##_delete (DE_PTR_TYPE(TYPENAME) hash, KEYTYPE key) \ 368 { \ 369 int slotNdx; \ 370 TYPENAME##Slot* slot; \ 371 TYPENAME##Slot* prevSlot = DE_NULL; \ 372 \ 373 DE_ASSERT(hash->numElements > 0); \ 374 slotNdx = (int)(HASHFUNC(key) & (deUint32)(hash->slotTableSize - 1)); \ 375 DE_ASSERT(slotNdx >= 0 && slotNdx < hash->slotTableSize); \ 376 slot = hash->slotTable[slotNdx]; \ 377 DE_ASSERT(slot); \ 378 \ 379 for (;;) \ 380 { \ 381 int elemNdx; \ 382 DE_ASSERT(slot->numUsed > 0); \ 383 for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \ 384 { \ 385 if (CMPFUNC(slot->keys[elemNdx], key)) \ 386 { \ 387 TYPENAME##Slot* lastSlot = slot; \ 388 while (lastSlot->nextSlot) \ 389 { \ 390 prevSlot = lastSlot; \ 391 lastSlot = lastSlot->nextSlot; \ 392 } \ 393 \ 394 slot->keys[elemNdx] = lastSlot->keys[lastSlot->numUsed-1]; \ 395 slot->values[elemNdx] = lastSlot->values[lastSlot->numUsed-1]; \ 396 lastSlot->numUsed--; \ 397 \ 398 if (lastSlot->numUsed == 0) \ 399 { \ 400 if (prevSlot) \ 401 prevSlot->nextSlot = DE_NULL; \ 402 else \ 403 hash->slotTable[slotNdx] = DE_NULL; \ 404 \ 405 lastSlot->nextSlot = hash->slotFreeList; \ 406 hash->slotFreeList = lastSlot; \ 407 } \ 408 \ 409 hash->numElements--; \ 410 return; \ 411 } \ 412 } \ 413 \ 414 prevSlot = slot; \ 415 slot = slot->nextSlot; \ 416 DE_ASSERT(slot); \ 417 } \ 418 } \ 419 struct TYPENAME##Unused2_s { int unused; } 420 421 /* Copy-to-array templates. */ 422 423 #define DE_DECLARE_POOL_HASH_TO_ARRAY(HASHTYPENAME, KEYARRAYTYPENAME, VALUEARRAYTYPENAME) \ 424 deBool HASHTYPENAME##_copyToArray(const HASHTYPENAME* set, DE_PTR_TYPE(KEYARRAYTYPENAME) keyArray, DE_PTR_TYPE(VALUEARRAYTYPENAME) valueArray); \ 425 struct HASHTYPENAME##_##KEYARRAYTYPENAME##_##VALUEARRAYTYPENAME##_declare_unused { int unused; } 426 427 #define DE_IMPLEMENT_POOL_HASH_TO_ARRAY(HASHTYPENAME, KEYARRAYTYPENAME, VALUEARRAYTYPENAME) \ 428 deBool HASHTYPENAME##_copyToArray(const HASHTYPENAME* hash, DE_PTR_TYPE(KEYARRAYTYPENAME) keyArray, DE_PTR_TYPE(VALUEARRAYTYPENAME) valueArray) \ 429 { \ 430 int numElements = hash->numElements; \ 431 int arrayNdx = 0; \ 432 int slotNdx; \ 433 \ 434 if ((keyArray && !KEYARRAYTYPENAME##_setSize(keyArray, numElements)) || \ 435 (valueArray && !VALUEARRAYTYPENAME##_setSize(valueArray, numElements))) \ 436 return DE_FALSE; \ 437 \ 438 for (slotNdx = 0; slotNdx < hash->slotTableSize; slotNdx++) \ 439 { \ 440 const HASHTYPENAME##Slot* slot = hash->slotTable[slotNdx]; \ 441 while (slot) \ 442 { \ 443 int elemNdx; \ 444 for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \ 445 { \ 446 if (keyArray) \ 447 KEYARRAYTYPENAME##_set(keyArray, arrayNdx, slot->keys[elemNdx]); \ 448 if (valueArray) \ 449 VALUEARRAYTYPENAME##_set(valueArray, arrayNdx, slot->values[elemNdx]); \ 450 arrayNdx++; \ 451 } \ 452 slot = slot->nextSlot; \ 453 } \ 454 } \ 455 DE_ASSERT(arrayNdx == numElements); \ 456 return DE_TRUE; \ 457 } \ 458 struct HASHTYPENAME##_##KEYARRAYTYPENAME##_##VALUEARRAYTYPENAME##_implement_unused { int unused; } 459 460 #endif /* _DEPOOLHASH_H */ 461