2010-04-07 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / utils / mono-value-hash.c
blobadae6f06cad706c00eb40512f3490e3519b09704
1 /*
2 * mono-value-hash.c: A hash table which only stores values in the hash nodes.
4 * Author:
5 * Miguel de Icaza (miguel@novell.com)
6 * Zoltan Varga (vargaz@gmail.com)
8 * (C) 2006,2008 Novell, Inc.
10 * Permission is hereby granted, free of charge, to any person obtaining
11 * a copy of this software and associated documentation files (the
12 * "Software"), to deal in the Software without restriction, including
13 * without limitation the rights to use, copy, modify, merge, publish,
14 * distribute, sublicense, and/or sell copies of the Software, and to
15 * permit persons to whom the Software is furnished to do so, subject to
16 * the following conditions:
18 * The above copyright notice and this permission notice shall be
19 * included in all copies or substantial portions of the Software.
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 #include <stdio.h>
30 #include <math.h>
31 #include <glib.h>
33 #include "mono-value-hash.h"
35 #ifndef G_MAXINT32
36 #define G_MAXINT32 2147483647
37 #endif
40 * This code is based on eglib/ghashtable.c with work done by Hans Petter Jansson
41 * (hpj@novell.com) to make it use internal probing instead of chaining.
44 #define HASH_TABLE_MIN_SHIFT 3 /* 1 << 3 == 8 buckets */
46 typedef struct _Slot Slot;
48 #define GET_VALUE(slot) ((gpointer)((((gsize)((slot)->value)) >> 2) << 2))
50 #define SET_VALUE(slot,value) ((slot)->value = (value))
52 #define IS_EMPTY(slot) ((gsize)((slot)->value) == 0)
53 #define IS_TOMBSTONE(slot) ((gsize)((slot)->value) & 1)
55 #define MAKE_TOMBSTONE(slot) do { (slot)->value = (gpointer)((gsize)((slot)->value) | 1); } while (1)
57 #define HASH(table, key) ((table)->hash_func ((key)))
59 struct _Slot {
60 /* A NULL value means the slot is empty */
61 /* The tombstone status is stored in the lowest order bit of the value. */
62 gpointer value;
65 static gpointer KEYMARKER_REMOVED = &KEYMARKER_REMOVED;
67 struct _MonoValueHashTable {
68 GHashFunc hash_func;
69 GEqualFunc key_equal_func;
70 MonoValueHashKeyExtractFunc key_extract_func;
72 Slot *table;
73 int table_size;
74 int table_mask;
75 int in_use;
76 int n_occupied;
77 GDestroyNotify value_destroy_func, key_destroy_func;
80 static void
81 mono_value_hash_table_set_shift (MonoValueHashTable *hash_table, gint shift)
83 gint i;
84 guint mask = 0;
86 hash_table->table_size = 1 << shift;
88 for (i = 0; i < shift; i++) {
89 mask <<= 1;
90 mask |= 1;
93 hash_table->table_mask = mask;
96 static gint
97 mono_value_hash_table_find_closest_shift (gint n)
99 gint i;
101 for (i = 0; n; i++)
102 n >>= 1;
104 return i;
107 static void
108 mono_value_hash_table_set_shift_from_size (MonoValueHashTable *hash_table, gint size)
110 gint shift;
112 shift = mono_value_hash_table_find_closest_shift (size);
113 shift = MAX (shift, HASH_TABLE_MIN_SHIFT);
115 mono_value_hash_table_set_shift (hash_table, shift);
118 MonoValueHashTable *
119 mono_value_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func, MonoValueHashKeyExtractFunc key_extract)
121 MonoValueHashTable *hash;
123 if (hash_func == NULL)
124 hash_func = g_direct_hash;
125 if (key_equal_func == NULL)
126 key_equal_func = g_direct_equal;
127 hash = g_new0 (MonoValueHashTable, 1);
129 hash->hash_func = hash_func;
130 hash->key_equal_func = key_equal_func;
131 hash->key_extract_func = key_extract;
133 mono_value_hash_table_set_shift (hash, HASH_TABLE_MIN_SHIFT);
134 hash->table = g_new0 (Slot, hash->table_size);
136 return hash;
139 #if 0
141 MonoValueHashTable *
142 mono_value_hash_table_new_full (GHashFunc hash_func, GEqualFunc key_equal_func,
143 GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
145 MonoValueHashTable *hash = mono_value_hash_table_new (hash_func, key_equal_func);
146 if (hash == NULL)
147 return NULL;
149 hash->key_destroy_func = key_destroy_func;
150 hash->value_destroy_func = value_destroy_func;
152 return hash;
155 #endif
157 static void
158 do_rehash (MonoValueHashTable *hash)
160 int i;
161 int old_size;
162 Slot *old_table;
164 old_size = hash->table_size;
165 old_table = hash->table;
167 mono_value_hash_table_set_shift_from_size (hash, hash->in_use * 2);
169 /* printf ("New size: %d\n", hash->table_size); */
170 hash->table = g_new0 (Slot, hash->table_size);
172 for (i = 0; i < old_size; i++){
173 Slot *s = &old_table [i];
174 Slot *new_s;
175 guint hash_val;
176 guint step = 0;
177 gpointer s_value, s_key;
179 if (IS_EMPTY (s) || IS_TOMBSTONE (s))
180 continue;
182 s_value = GET_VALUE (s);
183 s_key = hash->key_extract_func (s_value);
184 hash_val = HASH (hash, s_key) & hash->table_mask;
185 new_s = &hash->table [hash_val];
187 while (!IS_EMPTY (new_s)) {
188 step++;
189 hash_val += step;
190 hash_val &= hash->table_mask;
191 new_s = &hash->table [hash_val];
194 *new_s = *s;
196 g_free (old_table);
197 hash->n_occupied = hash->in_use;
200 static void
201 rehash (MonoValueHashTable *hash)
203 int n_occupied = hash->n_occupied;
204 int table_size = hash->table_size;
206 if ((table_size > hash->in_use * 4 && table_size > 1 << HASH_TABLE_MIN_SHIFT) ||
207 (table_size <= n_occupied + (n_occupied / 16)))
208 do_rehash (hash);
211 static void
212 mono_value_hash_table_insert_replace (MonoValueHashTable *hash, gpointer key, gpointer value, gboolean replace)
214 guint hashcode;
215 Slot *s;
216 guint s_index;
217 GEqualFunc equal;
218 guint first_tombstone = 0;
219 gboolean have_tombstone = FALSE;
220 guint step = 0;
222 g_assert (value);
223 g_assert (hash->key_extract_func (value) == key);
225 g_return_if_fail (hash != NULL);
227 hashcode = HASH (hash, key);
229 s_index = hashcode & hash->table_mask;
230 s = &hash->table [s_index];
232 equal = hash->key_equal_func;
234 while (!IS_EMPTY (s)) {
235 gpointer s_value = GET_VALUE (s);
236 gpointer s_key = hash->key_extract_func (s_value);
237 guint s_key_hash = HASH (hash, s_key);
238 if (s_key_hash == hashcode && (*equal) (s_key, key)) {
239 if (replace){
240 if (hash->key_destroy_func != NULL)
241 (*hash->key_destroy_func)(s_key);
243 if (hash->value_destroy_func != NULL)
244 (*hash->value_destroy_func) (GET_VALUE (s));
245 SET_VALUE (s, value);
246 return;
247 } else if (IS_TOMBSTONE (s) && !have_tombstone) {
248 first_tombstone = s_index;
249 have_tombstone = TRUE;
252 step++;
253 s_index += step;
254 s_index &= hash->table_mask;
255 s = &hash->table [s_index];
258 if (have_tombstone) {
259 s = &hash->table [first_tombstone];
260 } else {
261 hash->n_occupied++;
264 SET_VALUE (s, value);
265 hash->in_use++;
267 rehash (hash);
270 void
271 mono_value_hash_table_insert (MonoValueHashTable *hash, gpointer key, gpointer value)
273 mono_value_hash_table_insert_replace (hash, key, value, TRUE);
276 static Slot *
277 lookup_internal (MonoValueHashTable *hash, gconstpointer key)
279 GEqualFunc equal;
280 Slot *s;
281 guint hashcode;
282 guint s_index;
283 guint step = 0;
285 hashcode = HASH (hash, key);
287 s_index = hashcode & hash->table_mask;
288 s = &hash->table [s_index];
290 equal = hash->key_equal_func;
292 while (!IS_EMPTY (s)) {
293 gpointer s_value = GET_VALUE (s);
294 gpointer s_key = hash->key_extract_func (s_value);
295 guint s_key_hash = HASH (hash, s_key);
296 if (s_key_hash == hashcode && (*equal) (hash->key_extract_func (s_value), key))
297 return s;
299 step++;
300 s_index += step;
301 s_index &= hash->table_mask;
302 s = &hash->table [s_index];
305 return NULL;
308 gpointer
309 mono_value_hash_table_lookup (MonoValueHashTable *hash, gconstpointer key)
311 Slot *slot = lookup_internal (hash, key);
313 if (slot)
314 return GET_VALUE (slot);
315 else
316 return NULL;
319 void
320 mono_value_hash_table_destroy (MonoValueHashTable *hash)
322 int i;
324 g_return_if_fail (hash != NULL);
326 for (i = 0; i < hash->table_size; i++){
327 Slot *s = &hash->table [i];
329 if (!IS_EMPTY (s) && !IS_TOMBSTONE (s)) {
330 if (hash->key_destroy_func != NULL)
331 (*hash->key_destroy_func)(hash->key_extract_func (GET_VALUE (s)));
332 if (hash->value_destroy_func != NULL)
333 (*hash->value_destroy_func)(GET_VALUE (s));
336 g_free (hash->table);
338 g_free (hash);