tif_hash_set.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. /**********************************************************************
  2. *
  3. * Name: tif_hash_set.c
  4. * Purpose: Hash set functions.
  5. * Author: Even Rouault, <even dot rouault at spatialys.com>
  6. *
  7. **********************************************************************
  8. * Copyright (c) 2008-2009, Even Rouault <even dot rouault at spatialys.com>
  9. *
  10. * Permission is hereby granted, free of charge, to any person obtaining a
  11. * copy of this software and associated documentation files (the "Software"),
  12. * to deal in the Software without restriction, including without limitation
  13. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  14. * and/or sell copies of the Software, and to permit persons to whom the
  15. * Software is furnished to do so, subject to the following conditions:
  16. *
  17. * The above copyright notice and this permission notice shall be included
  18. * in all copies or substantial portions of the Software.
  19. *
  20. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  23. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  25. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  26. * DEALINGS IN THE SOFTWARE.
  27. ****************************************************************************/
  28. #include "tif_config.h"
  29. #include "tif_hash_set.h"
  30. #include <assert.h>
  31. #include <stdbool.h>
  32. #include <stdint.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. /** List element structure. */
  36. typedef struct _TIFFList TIFFList;
  37. /** List element structure. */
  38. struct _TIFFList
  39. {
  40. /*! Pointer to the data object. Should be allocated and freed by the
  41. * caller.
  42. * */
  43. void *pData;
  44. /*! Pointer to the next element in list. NULL, if current element is the
  45. * last one.
  46. */
  47. struct _TIFFList *psNext;
  48. };
  49. struct _TIFFHashSet
  50. {
  51. TIFFHashSetHashFunc fnHashFunc;
  52. TIFFHashSetEqualFunc fnEqualFunc;
  53. TIFFHashSetFreeEltFunc fnFreeEltFunc;
  54. TIFFList **tabList;
  55. int nSize;
  56. int nIndiceAllocatedSize;
  57. int nAllocatedSize;
  58. TIFFList *psRecyclingList;
  59. int nRecyclingListSize;
  60. bool bRehash;
  61. #ifdef HASH_DEBUG
  62. int nCollisions;
  63. #endif
  64. };
  65. static const int anPrimes[] = {
  66. 53, 97, 193, 389, 769, 1543, 3079,
  67. 6151, 12289, 24593, 49157, 98317, 196613, 393241,
  68. 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653,
  69. 100663319, 201326611, 402653189, 805306457, 1610612741};
  70. /************************************************************************/
  71. /* TIFFHashSetHashPointer() */
  72. /************************************************************************/
  73. /**
  74. * Hash function for an arbitrary pointer
  75. *
  76. * @param elt the arbitrary pointer to hash
  77. *
  78. * @return the hash value of the pointer
  79. */
  80. static unsigned long TIFFHashSetHashPointer(const void *elt)
  81. {
  82. return (unsigned long)(uintptr_t)((void *)(elt));
  83. }
  84. /************************************************************************/
  85. /* TIFFHashSetEqualPointer() */
  86. /************************************************************************/
  87. /**
  88. * Equality function for arbitrary pointers
  89. *
  90. * @param elt1 the first arbitrary pointer to compare
  91. * @param elt2 the second arbitrary pointer to compare
  92. *
  93. * @return true if the pointers are equal
  94. */
  95. static bool TIFFHashSetEqualPointer(const void *elt1, const void *elt2)
  96. {
  97. return elt1 == elt2;
  98. }
  99. /************************************************************************/
  100. /* TIFFHashSetNew() */
  101. /************************************************************************/
  102. /**
  103. * Creates a new hash set
  104. *
  105. * The hash function must return a hash value for the elements to insert.
  106. * If fnHashFunc is NULL, TIFFHashSetHashPointer will be used.
  107. *
  108. * The equal function must return if two elements are equal.
  109. * If fnEqualFunc is NULL, TIFFHashSetEqualPointer will be used.
  110. *
  111. * The free function is used to free elements inserted in the hash set,
  112. * when the hash set is destroyed, when elements are removed or replaced.
  113. * If fnFreeEltFunc is NULL, elements inserted into the hash set will not be
  114. * freed.
  115. *
  116. * @param fnHashFunc hash function. May be NULL.
  117. * @param fnEqualFunc equal function. May be NULL.
  118. * @param fnFreeEltFunc element free function. May be NULL.
  119. *
  120. * @return a new hash set
  121. */
  122. TIFFHashSet *TIFFHashSetNew(TIFFHashSetHashFunc fnHashFunc,
  123. TIFFHashSetEqualFunc fnEqualFunc,
  124. TIFFHashSetFreeEltFunc fnFreeEltFunc)
  125. {
  126. TIFFHashSet *set = (TIFFHashSet *)malloc(sizeof(TIFFHashSet));
  127. if (set == NULL)
  128. return NULL;
  129. set->fnHashFunc = fnHashFunc ? fnHashFunc : TIFFHashSetHashPointer;
  130. set->fnEqualFunc = fnEqualFunc ? fnEqualFunc : TIFFHashSetEqualPointer;
  131. set->fnFreeEltFunc = fnFreeEltFunc;
  132. set->nSize = 0;
  133. set->tabList = (TIFFList **)(calloc(sizeof(TIFFList *), 53));
  134. if (set->tabList == NULL)
  135. {
  136. free(set);
  137. return NULL;
  138. }
  139. set->nIndiceAllocatedSize = 0;
  140. set->nAllocatedSize = 53;
  141. set->psRecyclingList = NULL;
  142. set->nRecyclingListSize = 0;
  143. set->bRehash = false;
  144. #ifdef HASH_DEBUG
  145. set->nCollisions = 0;
  146. #endif
  147. return set;
  148. }
  149. /************************************************************************/
  150. /* TIFFHashSetSize() */
  151. /************************************************************************/
  152. /**
  153. * Returns the number of elements inserted in the hash set
  154. *
  155. * Note: this is not the internal size of the hash set
  156. *
  157. * @param set the hash set
  158. *
  159. * @return the number of elements in the hash set
  160. */
  161. int TIFFHashSetSize(const TIFFHashSet *set)
  162. {
  163. assert(set != NULL);
  164. return set->nSize;
  165. }
  166. /************************************************************************/
  167. /* TIFFHashSetGetNewListElt() */
  168. /************************************************************************/
  169. static TIFFList *TIFFHashSetGetNewListElt(TIFFHashSet *set)
  170. {
  171. if (set->psRecyclingList)
  172. {
  173. TIFFList *psRet = set->psRecyclingList;
  174. psRet->pData = NULL;
  175. set->nRecyclingListSize--;
  176. set->psRecyclingList = psRet->psNext;
  177. return psRet;
  178. }
  179. return (TIFFList *)malloc(sizeof(TIFFList));
  180. }
  181. /************************************************************************/
  182. /* TIFFHashSetReturnListElt() */
  183. /************************************************************************/
  184. static void TIFFHashSetReturnListElt(TIFFHashSet *set, TIFFList *psList)
  185. {
  186. if (set->nRecyclingListSize < 128)
  187. {
  188. psList->psNext = set->psRecyclingList;
  189. set->psRecyclingList = psList;
  190. set->nRecyclingListSize++;
  191. }
  192. else
  193. {
  194. free(psList);
  195. }
  196. }
  197. /************************************************************************/
  198. /* TIFFHashSetClearInternal() */
  199. /************************************************************************/
  200. static void TIFFHashSetClearInternal(TIFFHashSet *set, bool bFinalize)
  201. {
  202. assert(set != NULL);
  203. for (int i = 0; i < set->nAllocatedSize; i++)
  204. {
  205. TIFFList *cur = set->tabList[i];
  206. while (cur)
  207. {
  208. if (set->fnFreeEltFunc)
  209. set->fnFreeEltFunc(cur->pData);
  210. TIFFList *psNext = cur->psNext;
  211. if (bFinalize)
  212. free(cur);
  213. else
  214. TIFFHashSetReturnListElt(set, cur);
  215. cur = psNext;
  216. }
  217. set->tabList[i] = NULL;
  218. }
  219. set->bRehash = false;
  220. }
  221. /************************************************************************/
  222. /* TIFFListDestroy() */
  223. /************************************************************************/
  224. /**
  225. * Destroy a list. Caller responsible for freeing data objects contained in
  226. * list elements.
  227. *
  228. * @param psList pointer to list head.
  229. *
  230. */
  231. static void TIFFListDestroy(TIFFList *psList)
  232. {
  233. TIFFList *psCurrent = psList;
  234. while (psCurrent)
  235. {
  236. TIFFList *const psNext = psCurrent->psNext;
  237. free(psCurrent);
  238. psCurrent = psNext;
  239. }
  240. }
  241. /************************************************************************/
  242. /* TIFFHashSetDestroy() */
  243. /************************************************************************/
  244. /**
  245. * Destroys an allocated hash set.
  246. *
  247. * This function also frees the elements if a free function was
  248. * provided at the creation of the hash set.
  249. *
  250. * @param set the hash set
  251. */
  252. void TIFFHashSetDestroy(TIFFHashSet *set)
  253. {
  254. if (set)
  255. {
  256. TIFFHashSetClearInternal(set, true);
  257. free(set->tabList);
  258. TIFFListDestroy(set->psRecyclingList);
  259. free(set);
  260. }
  261. }
  262. #ifdef notused
  263. /************************************************************************/
  264. /* TIFFHashSetClear() */
  265. /************************************************************************/
  266. /**
  267. * Clear all elements from a hash set.
  268. *
  269. * This function also frees the elements if a free function was
  270. * provided at the creation of the hash set.
  271. *
  272. * @param set the hash set
  273. */
  274. void TIFFHashSetClear(TIFFHashSet *set)
  275. {
  276. TIFFHashSetClearInternal(set, false);
  277. set->nIndiceAllocatedSize = 0;
  278. set->nAllocatedSize = 53;
  279. #ifdef HASH_DEBUG
  280. set->nCollisions = 0;
  281. #endif
  282. set->nSize = 0;
  283. }
  284. /************************************************************************/
  285. /* TIFFHashSetForeach() */
  286. /************************************************************************/
  287. /**
  288. * Walk through the hash set and runs the provided function on all the
  289. * elements
  290. *
  291. * This function is provided the user_data argument of TIFFHashSetForeach.
  292. * It must return true to go on the walk through the hash set, or FALSE to
  293. * make it stop.
  294. *
  295. * Note : the structure of the hash set must *NOT* be modified during the
  296. * walk.
  297. *
  298. * @param set the hash set.
  299. * @param fnIterFunc the function called on each element.
  300. * @param user_data the user data provided to the function.
  301. */
  302. void TIFFHashSetForeach(TIFFHashSet *set, TIFFHashSetIterEltFunc fnIterFunc,
  303. void *user_data)
  304. {
  305. assert(set != NULL);
  306. if (!fnIterFunc)
  307. return;
  308. for (int i = 0; i < set->nAllocatedSize; i++)
  309. {
  310. TIFFList *cur = set->tabList[i];
  311. while (cur)
  312. {
  313. if (!fnIterFunc(cur->pData, user_data))
  314. return;
  315. cur = cur->psNext;
  316. }
  317. }
  318. }
  319. #endif
  320. /************************************************************************/
  321. /* TIFFHashSetRehash() */
  322. /************************************************************************/
  323. static bool TIFFHashSetRehash(TIFFHashSet *set)
  324. {
  325. int nNewAllocatedSize = anPrimes[set->nIndiceAllocatedSize];
  326. TIFFList **newTabList =
  327. (TIFFList **)(calloc(sizeof(TIFFList *), nNewAllocatedSize));
  328. if (newTabList == NULL)
  329. return false;
  330. #ifdef HASH_DEBUG
  331. TIFFDebug("TIFFHASH",
  332. "hashSet=%p, nSize=%d, nCollisions=%d, "
  333. "fCollisionRate=%.02f",
  334. set, set->nSize, set->nCollisions,
  335. set->nCollisions * 100.0 / set->nSize);
  336. set->nCollisions = 0;
  337. #endif
  338. for (int i = 0; i < set->nAllocatedSize; i++)
  339. {
  340. TIFFList *cur = set->tabList[i];
  341. while (cur)
  342. {
  343. const unsigned long nNewHashVal =
  344. set->fnHashFunc(cur->pData) % nNewAllocatedSize;
  345. #ifdef HASH_DEBUG
  346. if (newTabList[nNewHashVal])
  347. set->nCollisions++;
  348. #endif
  349. TIFFList *psNext = cur->psNext;
  350. cur->psNext = newTabList[nNewHashVal];
  351. newTabList[nNewHashVal] = cur;
  352. cur = psNext;
  353. }
  354. }
  355. free(set->tabList);
  356. set->tabList = newTabList;
  357. set->nAllocatedSize = nNewAllocatedSize;
  358. set->bRehash = false;
  359. return true;
  360. }
  361. /************************************************************************/
  362. /* TIFFHashSetFindPtr() */
  363. /************************************************************************/
  364. static void **TIFFHashSetFindPtr(TIFFHashSet *set, const void *elt)
  365. {
  366. const unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
  367. TIFFList *cur = set->tabList[nHashVal];
  368. while (cur)
  369. {
  370. if (set->fnEqualFunc(cur->pData, elt))
  371. return &cur->pData;
  372. cur = cur->psNext;
  373. }
  374. return NULL;
  375. }
  376. /************************************************************************/
  377. /* TIFFHashSetInsert() */
  378. /************************************************************************/
  379. /**
  380. * Inserts an element into a hash set.
  381. *
  382. * If the element was already inserted in the hash set, the previous
  383. * element is replaced by the new element. If a free function was provided,
  384. * it is used to free the previously inserted element
  385. *
  386. * @param set the hash set
  387. * @param elt the new element to insert in the hash set
  388. *
  389. * @return true if success. If false is returned, elt has not been inserted,
  390. * but TIFFHashSetInsert() will have run the free function if provided.
  391. */
  392. bool TIFFHashSetInsert(TIFFHashSet *set, void *elt)
  393. {
  394. assert(set != NULL);
  395. void **pElt = TIFFHashSetFindPtr(set, elt);
  396. if (pElt)
  397. {
  398. if (set->fnFreeEltFunc)
  399. set->fnFreeEltFunc(*pElt);
  400. *pElt = elt;
  401. return true;
  402. }
  403. if (set->nSize >= 2 * set->nAllocatedSize / 3 ||
  404. (set->bRehash && set->nIndiceAllocatedSize > 0 &&
  405. set->nSize <= set->nAllocatedSize / 2))
  406. {
  407. set->nIndiceAllocatedSize++;
  408. if (!TIFFHashSetRehash(set))
  409. {
  410. set->nIndiceAllocatedSize--;
  411. if (set->fnFreeEltFunc)
  412. set->fnFreeEltFunc(elt);
  413. return false;
  414. }
  415. }
  416. const unsigned long nHashVal = set->fnHashFunc(elt) % set->nAllocatedSize;
  417. #ifdef HASH_DEBUG
  418. if (set->tabList[nHashVal])
  419. set->nCollisions++;
  420. #endif
  421. TIFFList *new_elt = TIFFHashSetGetNewListElt(set);
  422. if (new_elt == NULL)
  423. {
  424. if (set->fnFreeEltFunc)
  425. set->fnFreeEltFunc(elt);
  426. return false;
  427. }
  428. new_elt->pData = elt;
  429. new_elt->psNext = set->tabList[nHashVal];
  430. set->tabList[nHashVal] = new_elt;
  431. set->nSize++;
  432. return true;
  433. }
  434. /************************************************************************/
  435. /* TIFFHashSetLookup() */
  436. /************************************************************************/
  437. /**
  438. * Returns the element found in the hash set corresponding to the element to
  439. * look up The element must not be modified.
  440. *
  441. * @param set the hash set
  442. * @param elt the element to look up in the hash set
  443. *
  444. * @return the element found in the hash set or NULL
  445. */
  446. void *TIFFHashSetLookup(TIFFHashSet *set, const void *elt)
  447. {
  448. assert(set != NULL);
  449. void **pElt = TIFFHashSetFindPtr(set, elt);
  450. if (pElt)
  451. return *pElt;
  452. return NULL;
  453. }
  454. /************************************************************************/
  455. /* TIFFHashSetRemoveInternal() */
  456. /************************************************************************/
  457. static bool TIFFHashSetRemoveInternal(TIFFHashSet *set, const void *elt,
  458. bool bDeferRehash)
  459. {
  460. assert(set != NULL);
  461. if (set->nIndiceAllocatedSize > 0 && set->nSize <= set->nAllocatedSize / 2)
  462. {
  463. set->nIndiceAllocatedSize--;
  464. if (bDeferRehash)
  465. set->bRehash = true;
  466. else
  467. {
  468. if (!TIFFHashSetRehash(set))
  469. {
  470. set->nIndiceAllocatedSize++;
  471. return false;
  472. }
  473. }
  474. }
  475. int nHashVal = (int)(set->fnHashFunc(elt) % set->nAllocatedSize);
  476. TIFFList *cur = set->tabList[nHashVal];
  477. TIFFList *prev = NULL;
  478. while (cur)
  479. {
  480. if (set->fnEqualFunc(cur->pData, elt))
  481. {
  482. if (prev)
  483. prev->psNext = cur->psNext;
  484. else
  485. set->tabList[nHashVal] = cur->psNext;
  486. if (set->fnFreeEltFunc)
  487. set->fnFreeEltFunc(cur->pData);
  488. TIFFHashSetReturnListElt(set, cur);
  489. #ifdef HASH_DEBUG
  490. if (set->tabList[nHashVal])
  491. set->nCollisions--;
  492. #endif
  493. set->nSize--;
  494. return true;
  495. }
  496. prev = cur;
  497. cur = cur->psNext;
  498. }
  499. return false;
  500. }
  501. /************************************************************************/
  502. /* TIFFHashSetRemove() */
  503. /************************************************************************/
  504. /**
  505. * Removes an element from a hash set
  506. *
  507. * @param set the hash set
  508. * @param elt the new element to remove from the hash set
  509. *
  510. * @return true if the element was in the hash set
  511. */
  512. bool TIFFHashSetRemove(TIFFHashSet *set, const void *elt)
  513. {
  514. return TIFFHashSetRemoveInternal(set, elt, false);
  515. }
  516. #ifdef notused
  517. /************************************************************************/
  518. /* TIFFHashSetRemoveDeferRehash() */
  519. /************************************************************************/
  520. /**
  521. * Removes an element from a hash set.
  522. *
  523. * This will defer potential rehashing of the set to later calls to
  524. * TIFFHashSetInsert() or TIFFHashSetRemove().
  525. *
  526. * @param set the hash set
  527. * @param elt the new element to remove from the hash set
  528. *
  529. * @return true if the element was in the hash set
  530. */
  531. bool TIFFHashSetRemoveDeferRehash(TIFFHashSet *set, const void *elt)
  532. {
  533. return TIFFHashSetRemoveInternal(set, elt, true);
  534. }
  535. #endif