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 * bool 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 bool TYPENAME##_reserve(DE_PTR_TYPE(TYPENAME) hash, int capacity); \ 100 VALUETYPE *TYPENAME##_find(const TYPENAME *hash, KEYTYPE key); \ 101 bool 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 bool 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 bool 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 \ 186 { \ 187 int unused; \ 188 } 189 190 /*--------------------------------------------------------------------*//*! 191 * \brief Implement a template pool hash class. 192 * \param TYPENAME Type name of the declared hash. 193 * \param KEYTYPE Type of the key. 194 * \param VALUETYPE Type of the value. 195 * \param HASHFUNC Function used for hashing the key. 196 * \param CMPFUNC Function used for exact matching of the keys. 197 * 198 * This macro has implements the hash declared with DE_DECLARE_POOL_HASH. 199 * Usually this macro should be used from a .c file, since the macro expands 200 * into multiple functions. The TYPENAME, KEYTYPE, and VALUETYPE parameters 201 * must match those of the declare macro. 202 *//*--------------------------------------------------------------------*/ 203 #define DE_IMPLEMENT_POOL_HASH(TYPENAME, KEYTYPE, VALUETYPE, HASHFUNC, CMPFUNC) \ 204 \ 205 TYPENAME *TYPENAME##_create(deMemPool *pool) \ 206 { \ 207 /* Alloc struct. */ \ 208 DE_PTR_TYPE(TYPENAME) hash = DE_POOL_NEW(pool, TYPENAME); \ 209 if (!hash) \ 210 return DE_NULL; \ 211 \ 212 memset(hash, 0, sizeof(TYPENAME)); \ 213 hash->pool = pool; \ 214 \ 215 return hash; \ 216 } \ 217 \ 218 void TYPENAME##_reset(DE_PTR_TYPE(TYPENAME) hash) \ 219 { \ 220 int slotNdx; \ 221 for (slotNdx = 0; slotNdx < hash->slotTableSize; slotNdx++) \ 222 { \ 223 TYPENAME##Slot *slot = hash->slotTable[slotNdx]; \ 224 while (slot) \ 225 { \ 226 TYPENAME##Slot *nextSlot = slot->nextSlot; \ 227 slot->nextSlot = hash->slotFreeList; \ 228 hash->slotFreeList = slot; \ 229 slot->numUsed = 0; \ 230 slot = nextSlot; \ 231 } \ 232 hash->slotTable[slotNdx] = DE_NULL; \ 233 } \ 234 hash->numElements = 0; \ 235 } \ 236 \ 237 TYPENAME##Slot *TYPENAME##_allocSlot(DE_PTR_TYPE(TYPENAME) hash) \ 238 { \ 239 TYPENAME##Slot *slot; \ 240 if (hash->slotFreeList) \ 241 { \ 242 slot = hash->slotFreeList; \ 243 hash->slotFreeList = hash->slotFreeList->nextSlot; \ 244 } \ 245 else \ 246 slot = (TYPENAME##Slot *)deMemPool_alloc(hash->pool, sizeof(TYPENAME##Slot) * DE_HASH_ELEMENTS_PER_SLOT); \ 247 \ 248 if (slot) \ 249 { \ 250 slot->nextSlot = DE_NULL; \ 251 slot->numUsed = 0; \ 252 } \ 253 \ 254 return slot; \ 255 } \ 256 \ 257 bool TYPENAME##_rehash(DE_PTR_TYPE(TYPENAME) hash, int newSlotTableSize) \ 258 { \ 259 DE_ASSERT(deIsPowerOfTwo32(newSlotTableSize) && newSlotTableSize > 0); \ 260 if (newSlotTableSize > hash->slotTableSize) \ 261 { \ 262 TYPENAME##Slot **oldSlotTable = hash->slotTable; \ 263 TYPENAME##Slot **newSlotTable = \ 264 (TYPENAME##Slot **)deMemPool_alloc(hash->pool, sizeof(TYPENAME##Slot *) * (size_t)newSlotTableSize); \ 265 int oldSlotTableSize = hash->slotTableSize; \ 266 int slotNdx; \ 267 \ 268 if (!newSlotTable) \ 269 return false; \ 270 \ 271 for (slotNdx = 0; slotNdx < oldSlotTableSize; slotNdx++) \ 272 newSlotTable[slotNdx] = oldSlotTable[slotNdx]; \ 273 \ 274 for (slotNdx = oldSlotTableSize; slotNdx < newSlotTableSize; slotNdx++) \ 275 newSlotTable[slotNdx] = DE_NULL; \ 276 \ 277 hash->slotTableSize = newSlotTableSize; \ 278 hash->slotTable = newSlotTable; \ 279 \ 280 for (slotNdx = 0; slotNdx < oldSlotTableSize; slotNdx++) \ 281 { \ 282 TYPENAME##Slot *slot = oldSlotTable[slotNdx]; \ 283 newSlotTable[slotNdx] = DE_NULL; \ 284 while (slot) \ 285 { \ 286 int elemNdx; \ 287 for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \ 288 { \ 289 hash->numElements--; \ 290 if (!TYPENAME##_insert(hash, slot->keys[elemNdx], slot->values[elemNdx])) \ 291 return false; \ 292 } \ 293 slot = slot->nextSlot; \ 294 } \ 295 } \ 296 } \ 297 \ 298 return true; \ 299 } \ 300 \ 301 VALUETYPE *TYPENAME##_find(const TYPENAME *hash, KEYTYPE key) \ 302 { \ 303 if (hash->numElements > 0) \ 304 { \ 305 int slotNdx = (int)(HASHFUNC(key) & (uint32_t)(hash->slotTableSize - 1)); \ 306 TYPENAME##Slot *slot = hash->slotTable[slotNdx]; \ 307 DE_ASSERT(deInBounds32(slotNdx, 0, hash->slotTableSize)); \ 308 \ 309 while (slot) \ 310 { \ 311 int elemNdx; \ 312 for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \ 313 { \ 314 if (CMPFUNC(slot->keys[elemNdx], key)) \ 315 return &slot->values[elemNdx]; \ 316 } \ 317 slot = slot->nextSlot; \ 318 } \ 319 } \ 320 \ 321 return DE_NULL; \ 322 } \ 323 \ 324 bool TYPENAME##_insert(DE_PTR_TYPE(TYPENAME) hash, KEYTYPE key, VALUETYPE value) \ 325 { \ 326 int slotNdx; \ 327 TYPENAME##Slot *slot; \ 328 \ 329 DE_ASSERT(!TYPENAME##_find(hash, key)); \ 330 \ 331 if ((hash->numElements + 1) >= hash->slotTableSize * DE_HASH_ELEMENTS_PER_SLOT) \ 332 if (!TYPENAME##_rehash(hash, deMax32(4, 2 * hash->slotTableSize))) \ 333 return false; \ 334 \ 335 slotNdx = (int)(HASHFUNC(key) & (uint32_t)(hash->slotTableSize - 1)); \ 336 DE_ASSERT(slotNdx >= 0 && slotNdx < hash->slotTableSize); \ 337 slot = hash->slotTable[slotNdx]; \ 338 \ 339 if (!slot) \ 340 { \ 341 slot = TYPENAME##_allocSlot(hash); \ 342 if (!slot) \ 343 return false; \ 344 hash->slotTable[slotNdx] = slot; \ 345 } \ 346 \ 347 for (;;) \ 348 { \ 349 if (slot->numUsed == DE_HASH_ELEMENTS_PER_SLOT) \ 350 { \ 351 if (slot->nextSlot) \ 352 slot = slot->nextSlot; \ 353 else \ 354 { \ 355 TYPENAME##Slot *nextSlot = TYPENAME##_allocSlot(hash); \ 356 if (!nextSlot) \ 357 return false; \ 358 slot->nextSlot = nextSlot; \ 359 slot = nextSlot; \ 360 } \ 361 } \ 362 else \ 363 { \ 364 slot->keys[slot->numUsed] = key; \ 365 slot->values[slot->numUsed] = value; \ 366 slot->numUsed++; \ 367 hash->numElements++; \ 368 return true; \ 369 } \ 370 } \ 371 } \ 372 \ 373 void TYPENAME##_delete(DE_PTR_TYPE(TYPENAME) hash, KEYTYPE key) \ 374 { \ 375 int slotNdx; \ 376 TYPENAME##Slot *slot; \ 377 TYPENAME##Slot *prevSlot = DE_NULL; \ 378 \ 379 DE_ASSERT(hash->numElements > 0); \ 380 slotNdx = (int)(HASHFUNC(key) & (uint32_t)(hash->slotTableSize - 1)); \ 381 DE_ASSERT(slotNdx >= 0 && slotNdx < hash->slotTableSize); \ 382 slot = hash->slotTable[slotNdx]; \ 383 DE_ASSERT(slot); \ 384 \ 385 for (;;) \ 386 { \ 387 int elemNdx; \ 388 DE_ASSERT(slot->numUsed > 0); \ 389 for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \ 390 { \ 391 if (CMPFUNC(slot->keys[elemNdx], key)) \ 392 { \ 393 TYPENAME##Slot *lastSlot = slot; \ 394 while (lastSlot->nextSlot) \ 395 { \ 396 prevSlot = lastSlot; \ 397 lastSlot = lastSlot->nextSlot; \ 398 } \ 399 \ 400 slot->keys[elemNdx] = lastSlot->keys[lastSlot->numUsed - 1]; \ 401 slot->values[elemNdx] = lastSlot->values[lastSlot->numUsed - 1]; \ 402 lastSlot->numUsed--; \ 403 \ 404 if (lastSlot->numUsed == 0) \ 405 { \ 406 if (prevSlot) \ 407 prevSlot->nextSlot = DE_NULL; \ 408 else \ 409 hash->slotTable[slotNdx] = DE_NULL; \ 410 \ 411 lastSlot->nextSlot = hash->slotFreeList; \ 412 hash->slotFreeList = lastSlot; \ 413 } \ 414 \ 415 hash->numElements--; \ 416 return; \ 417 } \ 418 } \ 419 \ 420 prevSlot = slot; \ 421 slot = slot->nextSlot; \ 422 DE_ASSERT(slot); \ 423 } \ 424 } \ 425 struct TYPENAME##Unused2_s \ 426 { \ 427 int unused; \ 428 } 429 430 /* Copy-to-array templates. */ 431 432 #define DE_DECLARE_POOL_HASH_TO_ARRAY(HASHTYPENAME, KEYARRAYTYPENAME, VALUEARRAYTYPENAME) \ 433 bool HASHTYPENAME##_copyToArray(const HASHTYPENAME *set, DE_PTR_TYPE(KEYARRAYTYPENAME) keyArray, \ 434 DE_PTR_TYPE(VALUEARRAYTYPENAME) valueArray); \ 435 struct HASHTYPENAME##_##KEYARRAYTYPENAME##_##VALUEARRAYTYPENAME##_declare_unused \ 436 { \ 437 int unused; \ 438 } 439 440 #define DE_IMPLEMENT_POOL_HASH_TO_ARRAY(HASHTYPENAME, KEYARRAYTYPENAME, VALUEARRAYTYPENAME) \ 441 bool HASHTYPENAME##_copyToArray(const HASHTYPENAME *hash, DE_PTR_TYPE(KEYARRAYTYPENAME) keyArray, \ 442 DE_PTR_TYPE(VALUEARRAYTYPENAME) valueArray) \ 443 { \ 444 int numElements = hash->numElements; \ 445 int arrayNdx = 0; \ 446 int slotNdx; \ 447 \ 448 if ((keyArray && !KEYARRAYTYPENAME##_setSize(keyArray, numElements)) || \ 449 (valueArray && !VALUEARRAYTYPENAME##_setSize(valueArray, numElements))) \ 450 return false; \ 451 \ 452 for (slotNdx = 0; slotNdx < hash->slotTableSize; slotNdx++) \ 453 { \ 454 const HASHTYPENAME##Slot *slot = hash->slotTable[slotNdx]; \ 455 while (slot) \ 456 { \ 457 int elemNdx; \ 458 for (elemNdx = 0; elemNdx < slot->numUsed; elemNdx++) \ 459 { \ 460 if (keyArray) \ 461 KEYARRAYTYPENAME##_set(keyArray, arrayNdx, slot->keys[elemNdx]); \ 462 if (valueArray) \ 463 VALUEARRAYTYPENAME##_set(valueArray, arrayNdx, slot->values[elemNdx]); \ 464 arrayNdx++; \ 465 } \ 466 slot = slot->nextSlot; \ 467 } \ 468 } \ 469 DE_ASSERT(arrayNdx == numElements); \ 470 return true; \ 471 } \ 472 struct HASHTYPENAME##_##KEYARRAYTYPENAME##_##VALUEARRAYTYPENAME##_implement_unused \ 473 { \ 474 int unused; \ 475 } 476 477 #endif /* _DEPOOLHASH_H */ 478