3 * A hash table which only stores values in the hash nodes.
6 * Miguel de Icaza (miguel@novell.com)
7 * Zoltan Varga (vargaz@gmail.com)
9 * (C) 2006,2008 Novell, Inc.
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:
19 * The above copyright notice and this permission notice shall be
20 * included in all copies or substantial portions of the Software.
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.
34 #include "mono-value-hash.h"
37 #define G_MAXINT32 2147483647
41 * This code is based on eglib/ghashtable.c with work done by Hans Petter Jansson
42 * (hpj@novell.com) to make it use internal probing instead of chaining.
45 #define HASH_TABLE_MIN_SHIFT 3 /* 1 << 3 == 8 buckets */
47 typedef struct _Slot Slot
;
49 #define GET_VALUE(slot) ((gpointer)((((gsize)((slot)->value)) >> 2) << 2))
51 #define SET_VALUE(slot,value) ((slot)->value = (value))
53 #define IS_EMPTY(slot) ((gsize)((slot)->value) == 0)
54 #define IS_TOMBSTONE(slot) ((gsize)((slot)->value) & 1)
56 #define MAKE_TOMBSTONE(slot) do { (slot)->value = (gpointer)((gsize)((slot)->value) | 1); } while (1)
58 #define HASH(table, key) ((table)->hash_func ((key)))
61 /* A NULL value means the slot is empty */
62 /* The tombstone status is stored in the lowest order bit of the value. */
66 static gpointer KEYMARKER_REMOVED
= &KEYMARKER_REMOVED
;
68 struct _MonoValueHashTable
{
70 GEqualFunc key_equal_func
;
71 MonoValueHashKeyExtractFunc key_extract_func
;
78 GDestroyNotify value_destroy_func
, key_destroy_func
;
82 mono_value_hash_table_set_shift (MonoValueHashTable
*hash_table
, gint shift
)
87 hash_table
->table_size
= 1 << shift
;
89 for (i
= 0; i
< shift
; i
++) {
94 hash_table
->table_mask
= mask
;
98 mono_value_hash_table_find_closest_shift (gint n
)
109 mono_value_hash_table_set_shift_from_size (MonoValueHashTable
*hash_table
, gint size
)
113 shift
= mono_value_hash_table_find_closest_shift (size
);
114 shift
= MAX (shift
, HASH_TABLE_MIN_SHIFT
);
116 mono_value_hash_table_set_shift (hash_table
, shift
);
120 mono_value_hash_table_new (GHashFunc hash_func
, GEqualFunc key_equal_func
, MonoValueHashKeyExtractFunc key_extract
)
122 MonoValueHashTable
*hash
;
124 if (hash_func
== NULL
)
125 hash_func
= g_direct_hash
;
126 if (key_equal_func
== NULL
)
127 key_equal_func
= g_direct_equal
;
128 hash
= g_new0 (MonoValueHashTable
, 1);
130 hash
->hash_func
= hash_func
;
131 hash
->key_equal_func
= key_equal_func
;
132 hash
->key_extract_func
= key_extract
;
134 mono_value_hash_table_set_shift (hash
, HASH_TABLE_MIN_SHIFT
);
135 hash
->table
= g_new0 (Slot
, hash
->table_size
);
143 mono_value_hash_table_new_full (GHashFunc hash_func
, GEqualFunc key_equal_func
,
144 GDestroyNotify key_destroy_func
, GDestroyNotify value_destroy_func
)
146 MonoValueHashTable
*hash
= mono_value_hash_table_new (hash_func
, key_equal_func
);
150 hash
->key_destroy_func
= key_destroy_func
;
151 hash
->value_destroy_func
= value_destroy_func
;
159 do_rehash (MonoValueHashTable
*hash
)
165 old_size
= hash
->table_size
;
166 old_table
= hash
->table
;
168 mono_value_hash_table_set_shift_from_size (hash
, hash
->in_use
* 2);
170 /* printf ("New size: %d\n", hash->table_size); */
171 hash
->table
= g_new0 (Slot
, hash
->table_size
);
173 for (i
= 0; i
< old_size
; i
++){
174 Slot
*s
= &old_table
[i
];
178 gpointer s_value
, s_key
;
180 if (IS_EMPTY (s
) || IS_TOMBSTONE (s
))
183 s_value
= GET_VALUE (s
);
184 s_key
= hash
->key_extract_func (s_value
);
185 hash_val
= HASH (hash
, s_key
) & hash
->table_mask
;
186 new_s
= &hash
->table
[hash_val
];
188 while (!IS_EMPTY (new_s
)) {
191 hash_val
&= hash
->table_mask
;
192 new_s
= &hash
->table
[hash_val
];
198 hash
->n_occupied
= hash
->in_use
;
202 rehash (MonoValueHashTable
*hash
)
204 int n_occupied
= hash
->n_occupied
;
205 int table_size
= hash
->table_size
;
207 if ((table_size
> hash
->in_use
* 4 && table_size
> 1 << HASH_TABLE_MIN_SHIFT
) ||
208 (table_size
<= n_occupied
+ (n_occupied
/ 16)))
213 mono_value_hash_table_insert_replace (MonoValueHashTable
*hash
, gpointer key
, gpointer value
, gboolean replace
)
219 guint first_tombstone
= 0;
220 gboolean have_tombstone
= FALSE
;
224 g_assert (hash
->key_extract_func (value
) == key
);
226 g_return_if_fail (hash
!= NULL
);
228 hashcode
= HASH (hash
, key
);
230 s_index
= hashcode
& hash
->table_mask
;
231 s
= &hash
->table
[s_index
];
233 equal
= hash
->key_equal_func
;
235 while (!IS_EMPTY (s
)) {
236 gpointer s_value
= GET_VALUE (s
);
237 gpointer s_key
= hash
->key_extract_func (s_value
);
238 guint s_key_hash
= HASH (hash
, s_key
);
239 if (s_key_hash
== hashcode
&& (*equal
) (s_key
, key
)) {
241 if (hash
->key_destroy_func
!= NULL
)
242 (*hash
->key_destroy_func
)(s_key
);
244 if (hash
->value_destroy_func
!= NULL
)
245 (*hash
->value_destroy_func
) (GET_VALUE (s
));
246 SET_VALUE (s
, value
);
248 } else if (IS_TOMBSTONE (s
) && !have_tombstone
) {
249 first_tombstone
= s_index
;
250 have_tombstone
= TRUE
;
255 s_index
&= hash
->table_mask
;
256 s
= &hash
->table
[s_index
];
259 if (have_tombstone
) {
260 s
= &hash
->table
[first_tombstone
];
265 SET_VALUE (s
, value
);
272 mono_value_hash_table_insert (MonoValueHashTable
*hash
, gpointer key
, gpointer value
)
274 mono_value_hash_table_insert_replace (hash
, key
, value
, TRUE
);
278 lookup_internal (MonoValueHashTable
*hash
, gconstpointer key
)
286 hashcode
= HASH (hash
, key
);
288 s_index
= hashcode
& hash
->table_mask
;
289 s
= &hash
->table
[s_index
];
291 equal
= hash
->key_equal_func
;
293 while (!IS_EMPTY (s
)) {
294 gpointer s_value
= GET_VALUE (s
);
295 gpointer s_key
= hash
->key_extract_func (s_value
);
296 guint s_key_hash
= HASH (hash
, s_key
);
297 if (s_key_hash
== hashcode
&& (*equal
) (hash
->key_extract_func (s_value
), key
))
302 s_index
&= hash
->table_mask
;
303 s
= &hash
->table
[s_index
];
310 mono_value_hash_table_lookup (MonoValueHashTable
*hash
, gconstpointer key
)
312 Slot
*slot
= lookup_internal (hash
, key
);
315 return GET_VALUE (slot
);
321 mono_value_hash_table_destroy (MonoValueHashTable
*hash
)
325 g_return_if_fail (hash
!= NULL
);
327 for (i
= 0; i
< hash
->table_size
; i
++){
328 Slot
*s
= &hash
->table
[i
];
330 if (!IS_EMPTY (s
) && !IS_TOMBSTONE (s
)) {
331 if (hash
->key_destroy_func
!= NULL
)
332 (*hash
->key_destroy_func
)(hash
->key_extract_func (GET_VALUE (s
)));
333 if (hash
->value_destroy_func
!= NULL
)
334 (*hash
->value_destroy_func
)(GET_VALUE (s
));
337 g_free (hash
->table
);