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.

689 lines
20 KiB

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