mono-value-hash.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /**
  2. * \file
  3. * A hash table which only stores values in the hash nodes.
  4. *
  5. * Author:
  6. * Miguel de Icaza (miguel@novell.com)
  7. * Zoltan Varga (vargaz@gmail.com)
  8. *
  9. * (C) 2006,2008 Novell, Inc.
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining
  12. * a copy of this software and associated documentation files (the
  13. * "Software"), to deal in the Software without restriction, including
  14. * without limitation the rights to use, copy, modify, merge, publish,
  15. * distribute, sublicense, and/or sell copies of the Software, and to
  16. * permit persons to whom the Software is furnished to do so, subject to
  17. * the following conditions:
  18. *
  19. * The above copyright notice and this permission notice shall be
  20. * included in all copies or substantial portions of the Software.
  21. *
  22. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. */
  30. #include <stdio.h>
  31. #include <math.h>
  32. #include <glib.h>
  33. #include "mono-value-hash.h"
  34. #ifndef G_MAXINT32
  35. #define G_MAXINT32 2147483647
  36. #endif
  37. /*
  38. * This code is based on eglib/ghashtable.c with work done by Hans Petter Jansson
  39. * (hpj@novell.com) to make it use internal probing instead of chaining.
  40. */
  41. #define HASH_TABLE_MIN_SHIFT 3 /* 1 << 3 == 8 buckets */
  42. typedef struct _Slot Slot;
  43. #define GET_VALUE(slot) ((gpointer)((((gsize)((slot)->value)) >> 2) << 2))
  44. #define SET_VALUE(slot,value) ((slot)->value = (value))
  45. #define IS_EMPTY(slot) ((gsize)((slot)->value) == 0)
  46. #define IS_TOMBSTONE(slot) ((gsize)((slot)->value) & 1)
  47. #define MAKE_TOMBSTONE(slot) do { (slot)->value = (gpointer)((gsize)((slot)->value) | 1); } while (1)
  48. #define HASH(table, key) ((table)->hash_func ((key)))
  49. struct _Slot {
  50. /* A NULL value means the slot is empty */
  51. /* The tombstone status is stored in the lowest order bit of the value. */
  52. gpointer value;
  53. };
  54. static gpointer KEYMARKER_REMOVED = &KEYMARKER_REMOVED;
  55. struct _MonoValueHashTable {
  56. GHashFunc hash_func;
  57. GEqualFunc key_equal_func;
  58. MonoValueHashKeyExtractFunc key_extract_func;
  59. Slot *table;
  60. int table_size;
  61. int table_mask;
  62. int in_use;
  63. int n_occupied;
  64. GDestroyNotify value_destroy_func, key_destroy_func;
  65. };
  66. static void
  67. mono_value_hash_table_set_shift (MonoValueHashTable *hash_table, gint shift)
  68. {
  69. gint i;
  70. guint mask = 0;
  71. hash_table->table_size = 1 << shift;
  72. for (i = 0; i < shift; i++) {
  73. mask <<= 1;
  74. mask |= 1;
  75. }
  76. hash_table->table_mask = mask;
  77. }
  78. static gint
  79. mono_value_hash_table_find_closest_shift (gint n)
  80. {
  81. gint i;
  82. for (i = 0; n; i++)
  83. n >>= 1;
  84. return i;
  85. }
  86. static void
  87. mono_value_hash_table_set_shift_from_size (MonoValueHashTable *hash_table, gint size)
  88. {
  89. gint shift;
  90. shift = mono_value_hash_table_find_closest_shift (size);
  91. shift = MAX (shift, HASH_TABLE_MIN_SHIFT);
  92. mono_value_hash_table_set_shift (hash_table, shift);
  93. }
  94. MonoValueHashTable *
  95. mono_value_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func, MonoValueHashKeyExtractFunc key_extract)
  96. {
  97. MonoValueHashTable *hash;
  98. if (hash_func == NULL)
  99. hash_func = g_direct_hash;
  100. if (key_equal_func == NULL)
  101. key_equal_func = g_direct_equal;
  102. hash = g_new0 (MonoValueHashTable, 1);
  103. hash->hash_func = hash_func;
  104. hash->key_equal_func = key_equal_func;
  105. hash->key_extract_func = key_extract;
  106. mono_value_hash_table_set_shift (hash, HASH_TABLE_MIN_SHIFT);
  107. hash->table = g_new0 (Slot, hash->table_size);
  108. return hash;
  109. }
  110. #if 0
  111. MonoValueHashTable *
  112. mono_value_hash_table_new_full (GHashFunc hash_func, GEqualFunc key_equal_func,
  113. GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
  114. {
  115. MonoValueHashTable *hash = mono_value_hash_table_new (hash_func, key_equal_func);
  116. if (hash == NULL)
  117. return NULL;
  118. hash->key_destroy_func = key_destroy_func;
  119. hash->value_destroy_func = value_destroy_func;
  120. return hash;
  121. }
  122. #endif
  123. static void
  124. do_rehash (MonoValueHashTable *hash)
  125. {
  126. int i;
  127. int old_size;
  128. Slot *old_table;
  129. old_size = hash->table_size;
  130. old_table = hash->table;
  131. mono_value_hash_table_set_shift_from_size (hash, hash->in_use * 2);
  132. /* printf ("New size: %d\n", hash->table_size); */
  133. hash->table = g_new0 (Slot, hash->table_size);
  134. for (i = 0; i < old_size; i++){
  135. Slot *s = &old_table [i];
  136. Slot *new_s;
  137. guint hash_val;
  138. guint step = 0;
  139. gpointer s_value, s_key;
  140. if (IS_EMPTY (s) || IS_TOMBSTONE (s))
  141. continue;
  142. s_value = GET_VALUE (s);
  143. s_key = hash->key_extract_func (s_value);
  144. hash_val = HASH (hash, s_key) & hash->table_mask;
  145. new_s = &hash->table [hash_val];
  146. while (!IS_EMPTY (new_s)) {
  147. step++;
  148. hash_val += step;
  149. hash_val &= hash->table_mask;
  150. new_s = &hash->table [hash_val];
  151. }
  152. *new_s = *s;
  153. }
  154. g_free (old_table);
  155. hash->n_occupied = hash->in_use;
  156. }
  157. static void
  158. rehash (MonoValueHashTable *hash)
  159. {
  160. int n_occupied = hash->n_occupied;
  161. int table_size = hash->table_size;
  162. if ((table_size > hash->in_use * 4 && table_size > 1 << HASH_TABLE_MIN_SHIFT) ||
  163. (table_size <= n_occupied + (n_occupied / 16)))
  164. do_rehash (hash);
  165. }
  166. static void
  167. mono_value_hash_table_insert_replace (MonoValueHashTable *hash, gpointer key, gpointer value, gboolean replace)
  168. {
  169. guint hashcode;
  170. Slot *s;
  171. guint s_index;
  172. GEqualFunc equal;
  173. guint first_tombstone = 0;
  174. gboolean have_tombstone = FALSE;
  175. guint step = 0;
  176. g_assert (value);
  177. g_assert (hash->key_extract_func (value) == key);
  178. g_return_if_fail (hash != NULL);
  179. hashcode = HASH (hash, key);
  180. s_index = hashcode & hash->table_mask;
  181. s = &hash->table [s_index];
  182. equal = hash->key_equal_func;
  183. while (!IS_EMPTY (s)) {
  184. gpointer s_value = GET_VALUE (s);
  185. gpointer s_key = hash->key_extract_func (s_value);
  186. guint s_key_hash = HASH (hash, s_key);
  187. if (s_key_hash == hashcode && (*equal) (s_key, key)) {
  188. if (replace){
  189. if (hash->key_destroy_func != NULL)
  190. (*hash->key_destroy_func)(s_key);
  191. }
  192. if (hash->value_destroy_func != NULL)
  193. (*hash->value_destroy_func) (GET_VALUE (s));
  194. SET_VALUE (s, value);
  195. return;
  196. } else if (IS_TOMBSTONE (s) && !have_tombstone) {
  197. first_tombstone = s_index;
  198. have_tombstone = TRUE;
  199. }
  200. step++;
  201. s_index += step;
  202. s_index &= hash->table_mask;
  203. s = &hash->table [s_index];
  204. }
  205. if (have_tombstone) {
  206. s = &hash->table [first_tombstone];
  207. } else {
  208. hash->n_occupied++;
  209. }
  210. SET_VALUE (s, value);
  211. hash->in_use++;
  212. rehash (hash);
  213. }
  214. void
  215. mono_value_hash_table_insert (MonoValueHashTable *hash, gpointer key, gpointer value)
  216. {
  217. mono_value_hash_table_insert_replace (hash, key, value, TRUE);
  218. }
  219. static Slot *
  220. lookup_internal (MonoValueHashTable *hash, gconstpointer key)
  221. {
  222. GEqualFunc equal;
  223. Slot *s;
  224. guint hashcode;
  225. guint s_index;
  226. guint step = 0;
  227. hashcode = HASH (hash, key);
  228. s_index = hashcode & hash->table_mask;
  229. s = &hash->table [s_index];
  230. equal = hash->key_equal_func;
  231. while (!IS_EMPTY (s)) {
  232. gpointer s_value = GET_VALUE (s);
  233. gpointer s_key = hash->key_extract_func (s_value);
  234. guint s_key_hash = HASH (hash, s_key);
  235. if (s_key_hash == hashcode && (*equal) (hash->key_extract_func (s_value), key))
  236. return s;
  237. step++;
  238. s_index += step;
  239. s_index &= hash->table_mask;
  240. s = &hash->table [s_index];
  241. }
  242. return NULL;
  243. }
  244. gpointer
  245. mono_value_hash_table_lookup (MonoValueHashTable *hash, gconstpointer key)
  246. {
  247. Slot *slot = lookup_internal (hash, key);
  248. if (slot)
  249. return GET_VALUE (slot);
  250. else
  251. return NULL;
  252. }
  253. void
  254. mono_value_hash_table_destroy (MonoValueHashTable *hash)
  255. {
  256. int i;
  257. g_return_if_fail (hash != NULL);
  258. for (i = 0; i < hash->table_size; i++){
  259. Slot *s = &hash->table [i];
  260. if (!IS_EMPTY (s) && !IS_TOMBSTONE (s)) {
  261. if (hash->key_destroy_func != NULL)
  262. (*hash->key_destroy_func)(hash->key_extract_func (GET_VALUE (s)));
  263. if (hash->value_destroy_func != NULL)
  264. (*hash->value_destroy_func)(GET_VALUE (s));
  265. }
  266. }
  267. g_free (hash->table);
  268. g_free (hash);
  269. }