2 * mono-value-hash.c: A hash table which only stores values in the hash nodes.
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.
33 #include "mono-value-hash.h"
36 #define G_MAXINT32 2147483647
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)))
60 /* A NULL value means the slot is empty */
61 /* The tombstone status is stored in the lowest order bit of the value. */
65 static gpointer KEYMARKER_REMOVED
= &KEYMARKER_REMOVED
;
67 struct _MonoValueHashTable
{
69 GEqualFunc key_equal_func
;
70 MonoValueHashKeyExtractFunc key_extract_func
;
77 GDestroyNotify value_destroy_func
, key_destroy_func
;
81 mono_value_hash_table_set_shift (MonoValueHashTable
*hash_table
, gint shift
)
86 hash_table
->table_size
= 1 << shift
;
88 for (i
= 0; i
< shift
; i
++) {
93 hash_table
->table_mask
= mask
;
97 mono_value_hash_table_find_closest_shift (gint n
)
108 mono_value_hash_table_set_shift_from_size (MonoValueHashTable
*hash_table
, gint size
)
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
);
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
);
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
);
149 hash
->key_destroy_func
= key_destroy_func
;
150 hash
->value_destroy_func
= value_destroy_func
;
158 do_rehash (MonoValueHashTable
*hash
)
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
];
177 gpointer s_value
, s_key
;
179 if (IS_EMPTY (s
) || IS_TOMBSTONE (s
))
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
)) {
190 hash_val
&= hash
->table_mask
;
191 new_s
= &hash
->table
[hash_val
];
197 hash
->n_occupied
= hash
->in_use
;
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)))
212 mono_value_hash_table_insert_replace (MonoValueHashTable
*hash
, gpointer key
, gpointer value
, gboolean replace
)
218 guint first_tombstone
= 0;
219 gboolean have_tombstone
= FALSE
;
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
)) {
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
);
247 } else if (IS_TOMBSTONE (s
) && !have_tombstone
) {
248 first_tombstone
= s_index
;
249 have_tombstone
= TRUE
;
254 s_index
&= hash
->table_mask
;
255 s
= &hash
->table
[s_index
];
258 if (have_tombstone
) {
259 s
= &hash
->table
[first_tombstone
];
264 SET_VALUE (s
, value
);
271 mono_value_hash_table_insert (MonoValueHashTable
*hash
, gpointer key
, gpointer value
)
273 mono_value_hash_table_insert_replace (hash
, key
, value
, TRUE
);
277 lookup_internal (MonoValueHashTable
*hash
, gconstpointer key
)
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
))
301 s_index
&= hash
->table_mask
;
302 s
= &hash
->table
[s_index
];
309 mono_value_hash_table_lookup (MonoValueHashTable
*hash
, gconstpointer key
)
311 Slot
*slot
= lookup_internal (hash
, key
);
314 return GET_VALUE (slot
);
320 mono_value_hash_table_destroy (MonoValueHashTable
*hash
)
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
);