You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

693 lines
20 KiB

10 years ago
  1. /* -*- coding: utf-8 -*-
  2. * ----------------------------------------------------------------------
  3. * Copyright © 2011-2013, RedJack, LLC.
  4. * All rights reserved.
  5. *
  6. * Please see the COPYING file in this distribution for license
  7. * details.
  8. * ----------------------------------------------------------------------
  9. */
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include "libcork/core/callbacks.h"
  13. #include "libcork/core/hash.h"
  14. #include "libcork/core/mempool.h"
  15. #include "libcork/core/types.h"
  16. #include "libcork/ds/dllist.h"
  17. #include "libcork/ds/hash-table.h"
  18. #include "libcork/helpers/errors.h"
  19. #ifndef CORK_HASH_TABLE_DEBUG
  20. #define CORK_HASH_TABLE_DEBUG 0
  21. #endif
  22. #if CORK_HASH_TABLE_DEBUG
  23. #include <stdio.h>
  24. #define DEBUG(...) \
  25. do { \
  26. fprintf(stderr, __VA_ARGS__); \
  27. fprintf(stderr, "\n"); \
  28. } while (0)
  29. #else
  30. #define DEBUG(...) /* nothing */
  31. #endif
  32. /*-----------------------------------------------------------------------
  33. * Hash tables
  34. */
  35. struct cork_hash_table_entry_priv {
  36. struct cork_hash_table_entry public;
  37. struct cork_dllist_item in_bucket;
  38. struct cork_dllist_item insertion_order;
  39. };
  40. struct cork_hash_table {
  41. struct cork_dllist *bins;
  42. struct cork_dllist insertion_order;
  43. size_t bin_count;
  44. size_t bin_mask;
  45. size_t entry_count;
  46. struct cork_mempool *pool;
  47. void *user_data;
  48. cork_free_f free_user_data;
  49. cork_hash_f hash;
  50. cork_equals_f equals;
  51. cork_free_f free_key;
  52. cork_free_f free_value;
  53. };
  54. static cork_hash
  55. cork_hash_table__default_hash(void *user_data, const void *key)
  56. {
  57. return (cork_hash) (uintptr_t) key;
  58. }
  59. static bool
  60. cork_hash_table__default_equals(void *user_data,
  61. const void *key1, const void *key2)
  62. {
  63. return key1 == key2;
  64. }
  65. /* The default initial number of bins to allocate in a new table. */
  66. #define CORK_HASH_TABLE_DEFAULT_INITIAL_SIZE 8
  67. /* The default number of entries per bin to allow before increasing the
  68. * number of bins. */
  69. #define CORK_HASH_TABLE_MAX_DENSITY 5
  70. /* Return a power-of-2 bin count that's at least as big as the given requested
  71. * size. */
  72. static inline size_t
  73. cork_hash_table_new_size(size_t desired_count)
  74. {
  75. size_t v = desired_count;
  76. size_t r = 1;
  77. while (v >>= 1) {
  78. r <<= 1;
  79. }
  80. if (r != desired_count) {
  81. r <<= 1;
  82. }
  83. return r;
  84. }
  85. #define bin_index(table, hash) ((hash) & (table)->bin_mask)
  86. /* Allocates a new bins array in a hash table. We overwrite the old
  87. * array, so make sure to stash it away somewhere safe first. */
  88. static void
  89. cork_hash_table_allocate_bins(struct cork_hash_table *table,
  90. size_t desired_count)
  91. {
  92. size_t i;
  93. table->bin_count = cork_hash_table_new_size(desired_count);
  94. table->bin_mask = table->bin_count - 1;
  95. DEBUG("Allocate %zu bins", table->bin_count);
  96. table->bins = cork_calloc(table->bin_count, sizeof(struct cork_dllist));
  97. for (i = 0; i < table->bin_count; i++) {
  98. cork_dllist_init(&table->bins[i]);
  99. }
  100. }
  101. static struct cork_hash_table_entry_priv *
  102. cork_hash_table_new_entry(struct cork_hash_table *table,
  103. cork_hash hash, void *key, void *value)
  104. {
  105. struct cork_hash_table_entry_priv *entry =
  106. cork_mempool_new_object(table->pool);
  107. cork_dllist_add(&table->insertion_order, &entry->insertion_order);
  108. entry->public.hash = hash;
  109. entry->public.key = key;
  110. entry->public.value = value;
  111. return entry;
  112. }
  113. static void
  114. cork_hash_table_free_entry(struct cork_hash_table *table,
  115. struct cork_hash_table_entry_priv *entry)
  116. {
  117. if (table->free_key != NULL) {
  118. table->free_key(entry->public.key);
  119. }
  120. if (table->free_value != NULL) {
  121. table->free_value(entry->public.value);
  122. }
  123. cork_dllist_remove(&entry->insertion_order);
  124. cork_mempool_free_object(table->pool, entry);
  125. }
  126. struct cork_hash_table *
  127. cork_hash_table_new(size_t initial_size, unsigned int flags)
  128. {
  129. struct cork_hash_table *table = cork_new(struct cork_hash_table);
  130. table->entry_count = 0;
  131. table->user_data = NULL;
  132. table->free_user_data = NULL;
  133. table->hash = cork_hash_table__default_hash;
  134. table->equals = cork_hash_table__default_equals;
  135. table->free_key = NULL;
  136. table->free_value = NULL;
  137. table->pool = cork_mempool_new(struct cork_hash_table_entry_priv);
  138. cork_dllist_init(&table->insertion_order);
  139. if (initial_size < CORK_HASH_TABLE_DEFAULT_INITIAL_SIZE) {
  140. initial_size = CORK_HASH_TABLE_DEFAULT_INITIAL_SIZE;
  141. }
  142. cork_hash_table_allocate_bins(table, initial_size);
  143. return table;
  144. }
  145. void
  146. cork_hash_table_clear(struct cork_hash_table *table)
  147. {
  148. size_t i;
  149. struct cork_dllist_item *curr;
  150. struct cork_dllist_item *next;
  151. DEBUG("(clear) Remove all entries");
  152. for (curr = cork_dllist_start(&table->insertion_order);
  153. !cork_dllist_is_end(&table->insertion_order, curr);
  154. curr = next) {
  155. struct cork_hash_table_entry_priv *entry =
  156. cork_container_of
  157. (curr, struct cork_hash_table_entry_priv, insertion_order);
  158. next = curr->next;
  159. cork_hash_table_free_entry(table, entry);
  160. }
  161. cork_dllist_init(&table->insertion_order);
  162. DEBUG("(clear) Clear bins");
  163. for (i = 0; i < table->bin_count; i++) {
  164. DEBUG(" Bin %zu", i);
  165. cork_dllist_init(&table->bins[i]);
  166. }
  167. table->entry_count = 0;
  168. }
  169. void
  170. cork_hash_table_free(struct cork_hash_table *table)
  171. {
  172. cork_hash_table_clear(table);
  173. cork_mempool_free(table->pool);
  174. free(table->bins);
  175. free(table);
  176. }
  177. size_t
  178. cork_hash_table_size(const struct cork_hash_table *table)
  179. {
  180. return table->entry_count;
  181. }
  182. void
  183. cork_hash_table_set_user_data(struct cork_hash_table *table,
  184. void *user_data, cork_free_f free_user_data)
  185. {
  186. table->user_data = user_data;
  187. table->free_user_data = free_user_data;
  188. }
  189. void
  190. cork_hash_table_set_hash(struct cork_hash_table *table, cork_hash_f hash)
  191. {
  192. table->hash = hash;
  193. }
  194. void
  195. cork_hash_table_set_equals(struct cork_hash_table *table, cork_equals_f equals)
  196. {
  197. table->equals = equals;
  198. }
  199. void
  200. cork_hash_table_set_free_key(struct cork_hash_table *table, cork_free_f free)
  201. {
  202. table->free_key = free;
  203. }
  204. void
  205. cork_hash_table_set_free_value(struct cork_hash_table *table, cork_free_f free)
  206. {
  207. table->free_value = free;
  208. }
  209. void
  210. cork_hash_table_ensure_size(struct cork_hash_table *table, size_t desired_count)
  211. {
  212. if (desired_count > table->bin_count) {
  213. struct cork_dllist *old_bins = table->bins;
  214. size_t old_bin_count = table->bin_count;
  215. cork_hash_table_allocate_bins(table, desired_count);
  216. if (old_bins != NULL) {
  217. size_t i;
  218. for (i = 0; i < old_bin_count; i++) {
  219. struct cork_dllist *bin = &old_bins[i];
  220. struct cork_dllist_item *curr = cork_dllist_start(bin);
  221. while (!cork_dllist_is_end(bin, curr)) {
  222. struct cork_hash_table_entry_priv *entry =
  223. cork_container_of
  224. (curr, struct cork_hash_table_entry_priv, in_bucket);
  225. struct cork_dllist_item *next = curr->next;
  226. size_t bin_index = bin_index(table, entry->public.hash);
  227. DEBUG(" Rehash %p from bin %zu to bin %zu",
  228. entry, i, bin_index);
  229. cork_dllist_add(&table->bins[bin_index], curr);
  230. curr = next;
  231. }
  232. }
  233. free(old_bins);
  234. }
  235. }
  236. }
  237. static void
  238. cork_hash_table_rehash(struct cork_hash_table *table)
  239. {
  240. DEBUG(" Reached maximum density; rehash");
  241. cork_hash_table_ensure_size(table, table->bin_count + 1);
  242. }
  243. struct cork_hash_table_entry *
  244. cork_hash_table_get_entry_hash(const struct cork_hash_table *table,
  245. cork_hash hash, const void *key)
  246. {
  247. size_t bin_index;
  248. struct cork_dllist *bin;
  249. struct cork_dllist_item *curr;
  250. if (table->bin_count == 0) {
  251. DEBUG("(get) Empty table when searching for key %p "
  252. "(hash 0x%08" PRIx32 ")",
  253. key, hash);
  254. return NULL;
  255. }
  256. bin_index = bin_index(table, hash);
  257. DEBUG("(get) Search for key %p (hash 0x%08" PRIx32 ", bin %zu)",
  258. key, hash, bin_index);
  259. bin = &table->bins[bin_index];
  260. curr = cork_dllist_start(bin);
  261. while (!cork_dllist_is_end(bin, curr)) {
  262. struct cork_hash_table_entry_priv *entry =
  263. cork_container_of
  264. (curr, struct cork_hash_table_entry_priv, in_bucket);
  265. DEBUG(" Check entry %p", entry);
  266. if (table->equals(table->user_data, key, entry->public.key)) {
  267. DEBUG(" Match");
  268. return &entry->public;
  269. }
  270. curr = curr->next;
  271. }
  272. DEBUG(" Entry not found");
  273. return NULL;
  274. }
  275. struct cork_hash_table_entry *
  276. cork_hash_table_get_entry(const struct cork_hash_table *table, const void *key)
  277. {
  278. cork_hash hash = table->hash(table->user_data, key);
  279. return cork_hash_table_get_entry_hash(table, hash, key);
  280. }
  281. void *
  282. cork_hash_table_get_hash(const struct cork_hash_table *table,
  283. cork_hash hash, const void *key)
  284. {
  285. struct cork_hash_table_entry *entry =
  286. cork_hash_table_get_entry_hash(table, hash, key);
  287. if (entry == NULL) {
  288. return NULL;
  289. } else {
  290. DEBUG(" Extract value pointer %p", entry->value);
  291. return entry->value;
  292. }
  293. }
  294. void *
  295. cork_hash_table_get(const struct cork_hash_table *table, const void *key)
  296. {
  297. struct cork_hash_table_entry *entry =
  298. cork_hash_table_get_entry(table, key);
  299. if (entry == NULL) {
  300. return NULL;
  301. } else {
  302. DEBUG(" Extract value pointer %p", entry->value);
  303. return entry->value;
  304. }
  305. }
  306. struct cork_hash_table_entry *
  307. cork_hash_table_get_or_create_hash(struct cork_hash_table *table,
  308. cork_hash hash, void *key, bool *is_new)
  309. {
  310. struct cork_hash_table_entry_priv *entry;
  311. size_t bin_index;
  312. if (table->bin_count > 0) {
  313. struct cork_dllist *bin;
  314. struct cork_dllist_item *curr;
  315. bin_index = bin_index(table, hash);
  316. DEBUG("(get_or_create) Search for key %p "
  317. "(hash 0x%08" PRIx32 ", bin %zu)",
  318. key, hash, bin_index);
  319. bin = &table->bins[bin_index];
  320. curr = cork_dllist_start(bin);
  321. while (!cork_dllist_is_end(bin, curr)) {
  322. struct cork_hash_table_entry_priv *entry =
  323. cork_container_of
  324. (curr, struct cork_hash_table_entry_priv, in_bucket);
  325. DEBUG(" Check entry %p", entry);
  326. if (table->equals(table->user_data, key, entry->public.key)) {
  327. DEBUG(" Match");
  328. DEBUG(" Return value pointer %p", entry->public.value);
  329. *is_new = false;
  330. return &entry->public;
  331. }
  332. curr = curr->next;
  333. }
  334. /* create a new entry */
  335. DEBUG(" Entry not found");
  336. if ((table->entry_count / table->bin_count) >
  337. CORK_HASH_TABLE_MAX_DENSITY) {
  338. cork_hash_table_rehash(table);
  339. bin_index = bin_index(table, hash);
  340. }
  341. } else {
  342. DEBUG("(get_or_create) Search for key %p (hash 0x%08" PRIx32 ")",
  343. key, hash);
  344. DEBUG(" Empty table");
  345. cork_hash_table_rehash(table);
  346. bin_index = bin_index(table, hash);
  347. }
  348. DEBUG(" Allocate new entry");
  349. entry = cork_hash_table_new_entry(table, hash, key, NULL);
  350. DEBUG(" Created new entry %p", entry);
  351. DEBUG(" Add entry into bin %zu", bin_index);
  352. cork_dllist_add(&table->bins[bin_index], &entry->in_bucket);
  353. table->entry_count++;
  354. *is_new = true;
  355. return &entry->public;
  356. }
  357. struct cork_hash_table_entry *
  358. cork_hash_table_get_or_create(struct cork_hash_table *table,
  359. void *key, bool *is_new)
  360. {
  361. cork_hash hash = table->hash(table->user_data, key);
  362. return cork_hash_table_get_or_create_hash(table, hash, key, is_new);
  363. }
  364. void
  365. cork_hash_table_put_hash(struct cork_hash_table *table,
  366. cork_hash hash, void *key, void *value,
  367. bool *is_new, void **old_key, void **old_value)
  368. {
  369. struct cork_hash_table_entry_priv *entry;
  370. size_t bin_index;
  371. if (table->bin_count > 0) {
  372. struct cork_dllist *bin;
  373. struct cork_dllist_item *curr;
  374. bin_index = bin_index(table, hash);
  375. DEBUG("(put) Search for key %p (hash 0x%08" PRIx32 ", bin %zu)",
  376. key, hash, bin_index);
  377. bin = &table->bins[bin_index];
  378. curr = cork_dllist_start(bin);
  379. while (!cork_dllist_is_end(bin, curr)) {
  380. struct cork_hash_table_entry_priv *entry =
  381. cork_container_of
  382. (curr, struct cork_hash_table_entry_priv, in_bucket);
  383. DEBUG(" Check entry %p", entry);
  384. if (table->equals(table->user_data, key, entry->public.key)) {
  385. DEBUG(" Found existing entry; overwriting");
  386. DEBUG(" Return old key %p", entry->public.key);
  387. if (old_key != NULL) {
  388. *old_key = entry->public.key;
  389. }
  390. DEBUG(" Return old value %p", entry->public.value);
  391. if (old_value != NULL) {
  392. *old_value = entry->public.value;
  393. }
  394. DEBUG(" Copy key %p into entry", key);
  395. entry->public.key = key;
  396. DEBUG(" Copy value %p into entry", value);
  397. entry->public.value = value;
  398. if (is_new != NULL) {
  399. *is_new = false;
  400. }
  401. return;
  402. }
  403. curr = curr->next;
  404. }
  405. /* create a new entry */
  406. DEBUG(" Entry not found");
  407. if ((table->entry_count / table->bin_count) >
  408. CORK_HASH_TABLE_MAX_DENSITY) {
  409. cork_hash_table_rehash(table);
  410. bin_index = bin_index(table, hash);
  411. }
  412. } else {
  413. DEBUG("(put) Search for key %p (hash 0x%08" PRIx32 ")",
  414. key, hash);
  415. DEBUG(" Empty table");
  416. cork_hash_table_rehash(table);
  417. bin_index = bin_index(table, hash);
  418. }
  419. DEBUG(" Allocate new entry");
  420. entry = cork_hash_table_new_entry(table, hash, key, value);
  421. DEBUG(" Created new entry %p", entry);
  422. DEBUG(" Add entry into bin %zu", bin_index);
  423. cork_dllist_add(&table->bins[bin_index], &entry->in_bucket);
  424. table->entry_count++;
  425. if (old_key != NULL) {
  426. *old_key = NULL;
  427. }
  428. if (old_value != NULL) {
  429. *old_value = NULL;
  430. }
  431. if (is_new != NULL) {
  432. *is_new = true;
  433. }
  434. }
  435. void
  436. cork_hash_table_put(struct cork_hash_table *table,
  437. void *key, void *value,
  438. bool *is_new, void **old_key, void **old_value)
  439. {
  440. cork_hash hash = table->hash(table->user_data, key);
  441. cork_hash_table_put_hash
  442. (table, hash, key, value, is_new, old_key, old_value);
  443. }
  444. void
  445. cork_hash_table_delete_entry(struct cork_hash_table *table,
  446. struct cork_hash_table_entry *ventry)
  447. {
  448. struct cork_hash_table_entry_priv *entry =
  449. cork_container_of(ventry, struct cork_hash_table_entry_priv, public);
  450. cork_dllist_remove(&entry->in_bucket);
  451. table->entry_count--;
  452. cork_hash_table_free_entry(table, entry);
  453. }
  454. bool
  455. cork_hash_table_delete_hash(struct cork_hash_table *table,
  456. cork_hash hash, const void *key,
  457. void **deleted_key, void **deleted_value)
  458. {
  459. size_t bin_index;
  460. struct cork_dllist *bin;
  461. struct cork_dllist_item *curr;
  462. if (table->bin_count == 0) {
  463. DEBUG("(delete) Empty table when searching for key %p "
  464. "(hash 0x%08" PRIx32 ")",
  465. key, hash);
  466. return false;
  467. }
  468. bin_index = bin_index(table, hash);
  469. DEBUG("(delete) Search for key %p (hash 0x%08" PRIx32 ", bin %zu)",
  470. key, hash, bin_index);
  471. bin = &table->bins[bin_index];
  472. curr = cork_dllist_start(bin);
  473. while (!cork_dllist_is_end(bin, curr)) {
  474. struct cork_hash_table_entry_priv *entry =
  475. cork_container_of
  476. (curr, struct cork_hash_table_entry_priv, in_bucket);
  477. DEBUG(" Check entry %p", entry);
  478. if (table->equals(table->user_data, key, entry->public.key)) {
  479. DEBUG(" Match");
  480. if (deleted_key != NULL) {
  481. *deleted_key = entry->public.key;
  482. }
  483. if (deleted_value != NULL) {
  484. *deleted_value = entry->public.value;
  485. }
  486. DEBUG(" Remove entry from hash bin %zu", bin_index);
  487. cork_dllist_remove(curr);
  488. table->entry_count--;
  489. DEBUG(" Free entry %p", entry);
  490. cork_hash_table_free_entry(table, entry);
  491. return true;
  492. }
  493. curr = curr->next;
  494. }
  495. DEBUG(" Entry not found");
  496. return false;
  497. }
  498. bool
  499. cork_hash_table_delete(struct cork_hash_table *table, const void *key,
  500. void **deleted_key, void **deleted_value)
  501. {
  502. cork_hash hash = table->hash(table->user_data, key);
  503. return cork_hash_table_delete_hash
  504. (table, hash, key, deleted_key, deleted_value);
  505. }
  506. void
  507. cork_hash_table_map(struct cork_hash_table *table, void *user_data,
  508. cork_hash_table_map_f map)
  509. {
  510. struct cork_dllist_item *curr;
  511. DEBUG("Map across hash table");
  512. curr = cork_dllist_start(&table->insertion_order);
  513. while (!cork_dllist_is_end(&table->insertion_order, curr)) {
  514. struct cork_hash_table_entry_priv *entry =
  515. cork_container_of
  516. (curr, struct cork_hash_table_entry_priv, insertion_order);
  517. struct cork_dllist_item *next = curr->next;
  518. enum cork_hash_table_map_result result;
  519. DEBUG(" Apply function to entry %p", entry);
  520. result = map(user_data, &entry->public);
  521. if (result == CORK_HASH_TABLE_MAP_ABORT) {
  522. return;
  523. } else if (result == CORK_HASH_TABLE_MAP_DELETE) {
  524. DEBUG(" Delete requested");
  525. cork_dllist_remove(curr);
  526. table->entry_count--;
  527. cork_hash_table_free_entry(table, entry);
  528. }
  529. curr = next;
  530. }
  531. }
  532. void
  533. cork_hash_table_iterator_init(struct cork_hash_table *table,
  534. struct cork_hash_table_iterator *iterator)
  535. {
  536. DEBUG("Iterate through hash table");
  537. iterator->table = table;
  538. iterator->priv = cork_dllist_start(&table->insertion_order);
  539. }
  540. struct cork_hash_table_entry *
  541. cork_hash_table_iterator_next(struct cork_hash_table_iterator *iterator)
  542. {
  543. struct cork_hash_table *table = iterator->table;
  544. struct cork_dllist_item *curr = iterator->priv;
  545. struct cork_hash_table_entry_priv *entry;
  546. if (cork_dllist_is_end(&table->insertion_order, curr)) {
  547. return NULL;
  548. }
  549. entry = cork_container_of
  550. (curr, struct cork_hash_table_entry_priv, insertion_order);
  551. DEBUG(" Return entry %p", entry);
  552. iterator->priv = curr->next;
  553. return &entry->public;
  554. }
  555. /*-----------------------------------------------------------------------
  556. * Built-in key types
  557. */
  558. static cork_hash
  559. string_hash(void *user_data, const void *vk)
  560. {
  561. const char *k = vk;
  562. size_t len = strlen(k);
  563. return cork_hash_buffer(0, k, len);
  564. }
  565. static bool
  566. string_equals(void *user_data, const void *vk1, const void *vk2)
  567. {
  568. const char *k1 = vk1;
  569. const char *k2 = vk2;
  570. return strcmp(k1, k2) == 0;
  571. }
  572. struct cork_hash_table *
  573. cork_string_hash_table_new(size_t initial_size, unsigned int flags)
  574. {
  575. struct cork_hash_table *table = cork_hash_table_new(initial_size, flags);
  576. cork_hash_table_set_hash(table, string_hash);
  577. cork_hash_table_set_equals(table, string_equals);
  578. return table;
  579. }
  580. struct cork_hash_table *
  581. cork_pointer_hash_table_new(size_t initial_size, unsigned int flags)
  582. {
  583. return cork_hash_table_new(initial_size, flags);
  584. }