hashtable.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * Copyright (c) 2011-2012 Nokia Corporation and/or its subsidiary(-ies).
  3. * All rights reserved.
  4. * This component and the accompanying materials are made available
  5. * under the terms of the License "Eclipse Public License v1.0"
  6. * which accompanies this distribution, and is available
  7. * at the URL "http://www.eclipse.org/legal/epl-v10.html".
  8. *
  9. * Initial Contributors:
  10. * Nokia Corporation - initial contribution.
  11. *
  12. * Contributors:
  13. *
  14. * Description:
  15. *
  16. * HashTable - basic hashtable, designed with the idea
  17. * of helping to cache filenames for case-independent lookup
  18. * so the keys are strings and the values are filenames.
  19. */
  20. #include <assert.h>
  21. #include <malloc.h>
  22. #include <string.h>
  23. #include "hashtable.h"
  24. /*******************************
  25. Create a new chaining hashtable
  26. size - an estimate of how many entries it may need to contain.
  27. */
  28. HashTable *hashtable_new(size_t size)
  29. {
  30. assert(size > 0);
  31. HashTable *h = (HashTable *)malloc(sizeof(HashTable));
  32. if (!h)
  33. return NULL;
  34. h->size = size;
  35. h->free = h->size;
  36. h->entries = (HashNode **)calloc(size * sizeof(HashNode *), 1);
  37. if (! h->entries)
  38. {
  39. free(h);
  40. return NULL;
  41. }
  42. return h;
  43. }
  44. void hashtable_del(HashTable **h)
  45. /*
  46. Delete an entry in the Hashtable.
  47. Sets the hashtable pointer passed in as a parameter to NULL so that
  48. there is less likelihood of a "dangling pointer" happening during
  49. normal use of this data structure.
  50. */
  51. {
  52. assert(*h);
  53. size_t i;
  54. for (i = 0; i < (*h)->size; i++)
  55. {
  56. HashNode *n = (*h)->entries[i];
  57. while (n != NULL)
  58. {
  59. if (NULL != n->value)
  60. { free(n->value); n->value = NULL; }
  61. if (NULL != n->key)
  62. { free(n->key); n->key = NULL; }
  63. HashNode *t = n->next;
  64. n->next = NULL; // Even if there are dangling pointers this will help to prevent trouble.
  65. free(n);
  66. n = t;
  67. }
  68. }
  69. free((*h)->entries);
  70. (*h)->entries = 0;
  71. free(*h);
  72. *h = NULL;
  73. }
  74. /* return a hash code for a given key or name */
  75. size_t hashtable_gethash(const char *name)
  76. {
  77. assert(name);
  78. const char *l = name;
  79. char last = 0;
  80. size_t hash = 0;
  81. while (*l != '\0')
  82. {
  83. last = *l;
  84. hash += last;
  85. l++;
  86. }
  87. /*
  88. Make the last character twice as important since absolute filenames
  89. tend to differ most at the end.
  90. */
  91. hash += last;
  92. return hash;
  93. }
  94. char *hashtable_lookup(HashTable *ht, const char *name)
  95. {
  96. assert(ht);
  97. size_t h = hashtable_gethash(name);
  98. if (h == 0)
  99. return NULL;
  100. /*
  101. Look for the correct chain which
  102. this key would be in if it's actually
  103. present. To do this, take the hash
  104. of the key and wrap it so it's within
  105. the array of chains.
  106. */
  107. HashNode *chainlink = ht->entries[h % ht->size];
  108. /*
  109. We now know which chain to look in
  110. so search through the keys in it for
  111. the one we're looking for
  112. */
  113. while (chainlink != NULL)
  114. {
  115. if (strcmp(chainlink->key, name) == 0)
  116. return chainlink->value;
  117. chainlink = chainlink->next;
  118. }
  119. return NULL;
  120. }
  121. size_t hashtable_store(HashTable *ht, const char *key, const char *value)
  122. /* Copies the key and value into the hash table */
  123. {
  124. assert(ht);
  125. size_t h = hashtable_gethash(key);
  126. if (h == 0)
  127. return 2;
  128. /*
  129. Entries in a hash chain (HashNodes) have
  130. "next" pointers and to insert a new item into
  131. the chain it is necessary to look through the
  132. chain item by item for a possible duplicate key.
  133. This hashtable departs from "normality" by not
  134. replacing the old value with the new. Instead
  135. the old value wins. This is arguably desirable
  136. in a filesystem (consistency) but the real
  137. reason is that it requires less operations to
  138. do this.
  139. It is desirable to insert the new HashNode
  140. it at the top of the chain if possible - on
  141. the theory that any newly added filename has
  142. a high chance of being looked several more
  143. times by successive operations and that this
  144. will ensure that the second and following
  145. operations will not need to search through
  146. the chain very far.
  147. If a duplicate is found it is "flipped" up to
  148. the head of the chain. If no duplicate exists
  149. then a new HashNode is merely inserted at the
  150. head of the chain.
  151. A pointer to the head of this chain is stored
  152. so that it's relatively easy to alter the head
  153. later on without writing a lot of repeated
  154. code to access it via the array of chains.
  155. i.e. a pointer to the head pointer is needed.
  156. */
  157. HashNode **head = &ht->entries[h % ht->size];
  158. /* Keep track of the next pointer of the
  159. last node we visited so that we can
  160. alter it if we need to delete a
  161. duplicate.
  162. */
  163. HashNode **parent_next = head;
  164. /* iterator used to look for a duplicate */
  165. HashNode *dupe = *head;
  166. HashNode *newnode = NULL;
  167. /* Search for a possible duplicate */
  168. while (dupe)
  169. {
  170. if (strcmp(dupe->key, key) == 0)
  171. {
  172. free(dupe->value);
  173. dupe->value = strdup(value);
  174. break;
  175. }
  176. parent_next = &dupe->next;
  177. dupe = dupe->next;
  178. }
  179. if (!dupe)
  180. {
  181. /* Put the new Node at the top
  182. of the chain. */
  183. newnode = (HashNode *)calloc(sizeof(HashNode),1);
  184. newnode->key = strdup(key);
  185. newnode->value = strdup(value);
  186. newnode->next = *head;
  187. *head = newnode;
  188. } else {
  189. /*
  190. Flip the duplicate up to the head of the
  191. list so the Most Recently Used HashNode
  192. is on top.
  193. */
  194. *parent_next = dupe->next;
  195. dupe->next = *head;
  196. *head = dupe;
  197. }
  198. return 0;
  199. }