hash_table.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /* hash.c - hash tables for opkg
  2. Steven M. Ayer, Jamey Hicks
  3. Copyright (C) 2002 Compaq Computer Corporation
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License as
  6. published by the Free Software Foundation; either version 2, or (at
  7. your option) any later version.
  8. This program is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. */
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #include "hash_table.h"
  17. #include "opkg_message.h"
  18. #include "libbb/libbb.h"
  19. static unsigned long djb2_hash(const unsigned char *str)
  20. {
  21. unsigned long hash = 5381;
  22. int c;
  23. while ((c = *str++))
  24. hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
  25. return hash;
  26. }
  27. static int hash_index(hash_table_t * hash, const char *key)
  28. {
  29. return djb2_hash((const unsigned char *)key) % hash->n_buckets;
  30. }
  31. /*
  32. * this is an open table keyed by strings
  33. */
  34. void hash_table_init(const char *name, hash_table_t * hash, int len)
  35. {
  36. if (hash->entries != NULL) {
  37. opkg_msg(ERROR, "Internal error: non empty hash table.\n");
  38. return;
  39. }
  40. memset(hash, 0, sizeof(hash_table_t));
  41. hash->name = name;
  42. hash->n_buckets = len;
  43. hash->entries = xcalloc(hash->n_buckets, sizeof(hash_entry_t));
  44. }
  45. void hash_print_stats(hash_table_t * hash)
  46. {
  47. printf("hash_table: %s, %d bytes\n"
  48. "\tn_buckets=%d, n_elements=%d, n_collisions=%d\n"
  49. "\tmax_bucket_len=%d, n_used_buckets=%d, ave_bucket_len=%.2f\n"
  50. "\tn_hits=%d, n_misses=%d\n",
  51. hash->name,
  52. hash->n_buckets * (int)sizeof(hash_entry_t),
  53. hash->n_buckets,
  54. hash->n_elements,
  55. hash->n_collisions,
  56. hash->max_bucket_len,
  57. hash->n_used_buckets,
  58. (hash->n_used_buckets ?
  59. ((float)hash->n_elements) / hash->n_used_buckets : 0.0f),
  60. hash->n_hits, hash->n_misses);
  61. }
  62. void hash_table_deinit(hash_table_t * hash)
  63. {
  64. int i;
  65. if (!hash)
  66. return;
  67. /* free the reminaing entries */
  68. for (i = 0; i < hash->n_buckets; i++) {
  69. hash_entry_t *hash_entry = (hash->entries + i);
  70. free(hash_entry->key);
  71. /* skip the first entry as this is part of the array */
  72. hash_entry = hash_entry->next;
  73. while (hash_entry) {
  74. hash_entry_t *old = hash_entry;
  75. hash_entry = hash_entry->next;
  76. free(old->key);
  77. free(old);
  78. }
  79. }
  80. free(hash->entries);
  81. hash->entries = NULL;
  82. hash->n_buckets = 0;
  83. }
  84. void *hash_table_get(hash_table_t * hash, const char *key)
  85. {
  86. int ndx = hash_index(hash, key);
  87. hash_entry_t *hash_entry = hash->entries + ndx;
  88. while (hash_entry) {
  89. if (hash_entry->key) {
  90. if (strcmp(key, hash_entry->key) == 0) {
  91. hash->n_hits++;
  92. return hash_entry->data;
  93. }
  94. }
  95. hash_entry = hash_entry->next;
  96. }
  97. hash->n_misses++;
  98. return NULL;
  99. }
  100. int hash_table_insert(hash_table_t * hash, const char *key, void *value)
  101. {
  102. int bucket_len = 0;
  103. int ndx = hash_index(hash, key);
  104. hash_entry_t *hash_entry = hash->entries + ndx;
  105. if (hash_entry->key) {
  106. if (strcmp(hash_entry->key, key) == 0) {
  107. /* alread in table, update the value */
  108. hash_entry->data = value;
  109. return 0;
  110. } else {
  111. /*
  112. * if this is a collision, we have to go to the end of the ll,
  113. * then add a new entry
  114. * before we can hook up the value
  115. */
  116. while (hash_entry->next) {
  117. hash_entry = hash_entry->next;
  118. if (strcmp(hash_entry->key, key) == 0) {
  119. hash_entry->data = value;
  120. return 0;
  121. }
  122. bucket_len++;
  123. }
  124. hash_entry->next = xcalloc(1, sizeof(hash_entry_t));
  125. hash_entry = hash_entry->next;
  126. hash_entry->next = NULL;
  127. hash->n_collisions++;
  128. if (++bucket_len > hash->max_bucket_len)
  129. hash->max_bucket_len = bucket_len;
  130. }
  131. } else
  132. hash->n_used_buckets++;
  133. hash->n_elements++;
  134. hash_entry->key = xstrdup(key);
  135. hash_entry->data = value;
  136. return 0;
  137. }
  138. int hash_table_remove(hash_table_t * hash, const char *key)
  139. {
  140. int ndx = hash_index(hash, key);
  141. hash_entry_t *hash_entry = hash->entries + ndx;
  142. hash_entry_t *next_entry = NULL, *last_entry = NULL;
  143. while (hash_entry) {
  144. if (hash_entry->key) {
  145. if (strcmp(key, hash_entry->key) == 0) {
  146. free(hash_entry->key);
  147. if (last_entry) {
  148. last_entry->next = hash_entry->next;
  149. free(hash_entry);
  150. } else {
  151. next_entry = hash_entry->next;
  152. if (next_entry) {
  153. memmove(hash_entry, next_entry,
  154. sizeof(hash_entry_t));
  155. free(next_entry);
  156. } else {
  157. memset(hash_entry, 0,
  158. sizeof(hash_entry_t));
  159. }
  160. }
  161. return 1;
  162. }
  163. }
  164. last_entry = hash_entry;
  165. hash_entry = hash_entry->next;
  166. }
  167. return 0;
  168. }
  169. void hash_table_foreach(hash_table_t * hash,
  170. void (*f) (const char *key, void *entry, void *data),
  171. void *data)
  172. {
  173. int i;
  174. if (!hash || !f)
  175. return;
  176. for (i = 0; i < hash->n_buckets; i++) {
  177. hash_entry_t *hash_entry = (hash->entries + i);
  178. do {
  179. if (hash_entry->key) {
  180. f(hash_entry->key, hash_entry->data, data);
  181. }
  182. } while ((hash_entry = hash_entry->next));
  183. }
  184. }