/src/lhash-table.c

https://bitbucket.org/manvscode/libcollections · C · 321 lines · 223 code · 58 blank · 40 comment · 32 complexity · 7f1539ea86f9729db03ad517fa8674f3 MD5 · raw file

  1. /*
  2. * Copyright (C) 2010-2022 by Joseph A. Marrero. https://joemarrero.com/
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. * THE SOFTWARE.
  21. */
  22. #include <stdio.h>
  23. #include <math.h>
  24. #include <string.h>
  25. #include <assert.h>
  26. #include "lhash-table.h"
  27. static bool lc_lhash_table_find_bucket_for_insertion( lc_lhash_table_t* p_table, const void *data, size_t *p_index );
  28. static bool lc_lhash_table_find_bucket( lc_lhash_table_t* p_table, const void *data, size_t *p_index );
  29. #define bucket_is_occupied( p_table, bucket ) lc_bitset_test( &(p_table)->occupied, bucket )
  30. #define bucket_mark_occupied( p_table, bucket ) lc_bitset_set( &(p_table)->occupied, bucket )
  31. #define bucket_mark_empty( p_table, bucket ) lc_bitset_unset( &(p_table)->occupied, bucket )
  32. #define bucket_is_deleted( p_table, bucket ) lc_bitset_test( &(p_table)->deleted, bucket )
  33. #define bucket_mark_deleted( p_table, bucket ) lc_bitset_set( &(p_table)->deleted, bucket )
  34. #define bucket_mark_available( p_table, bucket ) lc_bitset_unset( &(p_table)->deleted, bucket )
  35. bool lc_lhash_table_create ( lc_lhash_table_t* p_table, size_t element_size, size_t table_size,
  36. lc_lhash_table_hash_fxn_t hash_function,
  37. lc_lhash_table_compare_fxn_t compare_function,
  38. lc_alloc_fxn_t alloc,
  39. lc_free_fxn_t free )
  40. {
  41. assert( p_table );
  42. if( lc_bitset_create( &p_table->occupied, table_size ) &&
  43. lc_bitset_create( &p_table->deleted, table_size ) )
  44. {
  45. p_table->size = 0;
  46. p_table->hash_callback = hash_function;
  47. p_table->compare_callback = compare_function;
  48. return lc_array_create( &p_table->table, element_size, table_size, alloc, free );
  49. }
  50. return false;
  51. }
  52. void lc_lhash_table_destroy( lc_lhash_table_t* p_table )
  53. {
  54. assert( p_table );
  55. lc_array_destroy( &p_table->table );
  56. lc_bitset_destroy( &p_table->occupied );
  57. lc_bitset_destroy( &p_table->deleted );
  58. }
  59. bool lc_lhash_table_find_bucket_for_insertion( lc_lhash_table_t* p_table, const void *data, size_t *p_index )
  60. {
  61. bool result = false;
  62. size_t hash = p_table->hash_callback( data );
  63. size_t count;
  64. *p_index = hash % lc_array_size( &p_table->table );
  65. for( count = 0; count < lc_array_size(&p_table->table) && !result; count++ )
  66. {
  67. if( bucket_is_deleted( p_table, *p_index ) || !bucket_is_occupied( p_table, *p_index ) )
  68. {
  69. bucket_mark_available( p_table, *p_index );
  70. result = true;
  71. }
  72. else
  73. {
  74. *p_index = (*p_index + LC_LHASH_TABLE_LINEAR_CONSTANT) % lc_array_size( &p_table->table );
  75. }
  76. }
  77. return result;
  78. }
  79. bool lc_lhash_table_find_bucket( lc_lhash_table_t* p_table, const void *data, size_t *p_index )
  80. {
  81. bool result = false;
  82. size_t hash = p_table->hash_callback( data );
  83. bool found_deleted = false;
  84. size_t deleted;
  85. size_t count;
  86. *p_index = hash % lc_array_size( &p_table->table );
  87. deleted = *p_index;
  88. for( count = 0; count < lc_array_size(&p_table->table) && !result; count++ )
  89. {
  90. if( bucket_is_deleted( p_table, *p_index ) )
  91. {
  92. /* Save the deleted index so that if we find an occupied matching element
  93. * we can move them into the deleted bucket and mark the original bucket
  94. * as
  95. */
  96. deleted = *p_index;
  97. *p_index = (*p_index + LC_LHASH_TABLE_LINEAR_CONSTANT) % lc_array_size( &p_table->table );
  98. found_deleted = true;
  99. continue;
  100. }
  101. else
  102. if( bucket_is_occupied(p_table, *p_index) && p_table->compare_callback( lc_array_element( &p_table->table, *p_index ), data ) == 0 )
  103. {
  104. /* We found an item */
  105. if( found_deleted )
  106. {
  107. memmove( lc_array_element(&p_table->table, deleted), lc_array_element(&p_table->table, *p_index), lc_array_element_size(&p_table->table) );
  108. assert( !bucket_is_deleted( p_table, *p_index ) );
  109. bucket_mark_deleted( p_table, *p_index );
  110. bucket_mark_available( p_table, deleted );
  111. bucket_mark_occupied( p_table, deleted );
  112. *p_index = deleted;
  113. }
  114. result = true;
  115. }
  116. else if( !bucket_is_occupied( p_table, *p_index ) )
  117. {
  118. /* We didn't find an item but we did
  119. * find an empty bucket.
  120. */
  121. result = false;
  122. }
  123. else
  124. {
  125. *p_index = (*p_index + LC_LHASH_TABLE_LINEAR_CONSTANT) % lc_array_size( &p_table->table );
  126. }
  127. }
  128. return result;
  129. }
  130. bool lc_lhash_table_insert( lc_lhash_table_t* p_table, const void *data )
  131. {
  132. size_t index;
  133. bool result = false;
  134. assert( p_table );
  135. if( lc_lhash_table_find_bucket_for_insertion( p_table, data, &index ) )
  136. {
  137. memcpy( lc_array_element( &p_table->table, index ), data, lc_array_element_size(&p_table->table) );
  138. bucket_mark_occupied( p_table, index );
  139. p_table->size++;
  140. result = true;
  141. }
  142. return result;
  143. }
  144. bool lc_lhash_table_remove( lc_lhash_table_t* p_table, const void *data )
  145. {
  146. size_t index;
  147. bool result = false;
  148. assert( p_table );
  149. if( lc_lhash_table_find_bucket( p_table, data, &index ) )
  150. {
  151. /* Mark the bucket as unoccupied and deleted */
  152. /*bucket_mark_empty( p_table, index ); // not sure if needed*/
  153. bucket_mark_deleted( p_table, index );
  154. p_table->size--;
  155. result = true;
  156. }
  157. return result;
  158. }
  159. bool lc_lhash_table_find( lc_lhash_table_t* p_table, const void *data, void **found_data )
  160. {
  161. size_t index;
  162. *found_data = NULL;
  163. assert( p_table );
  164. if( lc_lhash_table_find_bucket( p_table, data, &index ) )
  165. {
  166. if( bucket_is_occupied( p_table, index ) )
  167. {
  168. *found_data = lc_array_element( &p_table->table, index );
  169. return true;
  170. }
  171. }
  172. return false;
  173. }
  174. void lc_lhash_table_clear( lc_lhash_table_t* p_table )
  175. {
  176. lc_bitset_clear( &p_table->occupied );
  177. lc_bitset_clear( &p_table->deleted );
  178. }
  179. bool lc_lhash_table_resize( lc_lhash_table_t* p_table, size_t new_size )
  180. {
  181. assert( p_table );
  182. if( new_size != lc_lhash_table_size(p_table) )
  183. {
  184. lc_lhash_table_t new_table;
  185. if( lc_lhash_table_create( &new_table, lc_array_element_size(&p_table->table), new_size, p_table->hash_callback, p_table->compare_callback, p_table->alloc, p_table->free ) )
  186. {
  187. size_t i;
  188. for( i = 0; i < lc_array_size(&p_table->table); i++ )
  189. {
  190. if( bucket_is_deleted( p_table, i ) )
  191. {
  192. continue;
  193. }
  194. else if( bucket_is_occupied( p_table, i ) )
  195. {
  196. lc_lhash_table_insert( &new_table, lc_array_element( &p_table->table, i ) );
  197. }
  198. }
  199. lc_lhash_table_destroy( p_table );
  200. *p_table = new_table;
  201. return true;
  202. }
  203. }
  204. return false;
  205. }
  206. bool lc_lhash_table_rehash( lc_lhash_table_t* p_table, double load_factor )
  207. {
  208. #if defined(LHASH_GROW_AND_SHRINK)
  209. double current_load;
  210. double upper_limit;
  211. double lower_limit;
  212. assert( load_factor > 0.0 );
  213. assert( load_factor < 1.0 );
  214. current_load = lc_lhash_table_load_factor( p_table );
  215. upper_limit = load_factor * (1.0f + LC_LHASH_TABLE_THRESHOLD);
  216. lower_limit = load_factor * (1.0f - LC_LHASH_TABLE_THRESHOLD);
  217. if( current_load > upper_limit )
  218. {
  219. size_t size = ((current_load) / upper_limit) * lc_lhash_table_size(p_table) + 1;
  220. /* Load exceeds load factor threshold. We must increase the
  221. * size to return the hash table to the desired load factor.
  222. */
  223. return lc_lhash_table_resize( p_table, size );
  224. }
  225. else if( current_load < lower_limit )
  226. {
  227. size_t size = (current_load / (lower_limit)) * lc_lhash_table_size(p_table) + 1;
  228. /* Load exceeds load factor threshold. We must decrease the
  229. * size to return the hash table to the desired load factor.
  230. */
  231. return lc_lhash_table_resize( p_table, size );
  232. }
  233. #else
  234. double current_load;
  235. double upper_limit;
  236. assert( load_factor > 0.0 );
  237. assert( load_factor < 1.0 );
  238. current_load = lc_lhash_table_load_factor( p_table );
  239. upper_limit = load_factor * (1.0f + LC_LHASH_TABLE_THRESHOLD);
  240. if( current_load > upper_limit )
  241. {
  242. size_t size = 2 * lc_lhash_table_size(p_table) + 1;
  243. /* Load exceeds load factor threshold. We must increase the
  244. * size to return the hash table to the desired load factor.
  245. */
  246. return lc_lhash_table_resize( p_table, size );
  247. }
  248. #endif
  249. return false;
  250. }
  251. void lc_lhash_table_debug( lc_lhash_table_t* p_table )
  252. {
  253. size_t count;
  254. printf( "[" );
  255. for( count = 0; count < lc_array_size(&p_table->table); count++ )
  256. {
  257. char bucket = '-';
  258. if( bucket_is_occupied( p_table, count ) ) bucket = 'O';
  259. if( bucket_is_deleted( p_table, count ) ) bucket = 'd';
  260. printf( "%c", bucket );
  261. }
  262. printf( "]" );
  263. }