4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 #include <mono/sgen/sgen-gc.h>
31 #include <mono/sgen/sgen-hash-table.h>
33 #ifdef HEAVY_STATISTICS
34 static guint64 stat_lookups
;
35 static guint64 stat_lookup_iterations
;
36 static guint64 stat_lookup_max_iterations
;
40 rehash (SgenHashTable
*hash_table
)
42 SgenHashTableEntry
**old_hash
= hash_table
->table
;
43 guint old_hash_size
= hash_table
->size
;
44 guint i
, hash
, new_size
;
45 SgenHashTableEntry
**new_hash
;
46 SgenHashTableEntry
*entry
, *next
;
49 sgen_register_fixed_internal_mem_type (hash_table
->entry_mem_type
,
50 sizeof (SgenHashTableEntry
*) + sizeof (gpointer
) + hash_table
->data_size
);
53 new_size
= g_spaced_primes_closest (hash_table
->num_entries
);
56 new_hash
= (SgenHashTableEntry
**)sgen_alloc_internal_dynamic (new_size
* sizeof (SgenHashTableEntry
*), hash_table
->table_mem_type
, TRUE
);
57 for (i
= 0; i
< old_hash_size
; ++i
) {
58 for (entry
= old_hash
[i
]; entry
; entry
= next
) {
59 hash
= hash_table
->hash_func (entry
->key
) % new_size
;
61 entry
->next
= new_hash
[hash
];
62 new_hash
[hash
] = entry
;
65 sgen_free_internal_dynamic (old_hash
, old_hash_size
* sizeof (SgenHashTableEntry
*), hash_table
->table_mem_type
);
66 hash_table
->table
= new_hash
;
67 hash_table
->size
= new_size
;
71 rehash_if_necessary (SgenHashTable
*hash_table
)
73 if (hash_table
->num_entries
>= hash_table
->size
* 2)
76 SGEN_ASSERT (1, hash_table
->size
, "rehash guarantees size > 0");
79 static SgenHashTableEntry
*
80 lookup (SgenHashTable
*hash_table
, gpointer key
, guint
*_hash
)
82 SgenHashTableEntry
*entry
;
84 GEqualFunc equal
= hash_table
->equal_func
;
85 #ifdef HEAVY_STATISTICS
86 guint64 iterations
= 0;
90 if (!hash_table
->size
)
93 hash
= hash_table
->hash_func (key
) % hash_table
->size
;
97 for (entry
= hash_table
->table
[hash
]; entry
; entry
= entry
->next
) {
98 #ifdef HEAVY_STATISTICS
99 ++stat_lookup_iterations
;
101 if (iterations
> stat_lookup_max_iterations
)
102 stat_lookup_max_iterations
= iterations
;
104 if ((equal
&& equal (entry
->key
, key
)) || (!equal
&& entry
->key
== key
))
111 sgen_hash_table_lookup (SgenHashTable
*hash_table
, gpointer key
)
113 SgenHashTableEntry
*entry
= lookup (hash_table
, key
, NULL
);
120 sgen_hash_table_replace (SgenHashTable
*hash_table
, gpointer key
, gpointer new_value
, gpointer old_value
)
123 SgenHashTableEntry
*entry
;
125 rehash_if_necessary (hash_table
);
126 entry
= lookup (hash_table
, key
, &hash
);
130 memcpy (old_value
, entry
->data
, hash_table
->data_size
);
131 memcpy (entry
->data
, new_value
, hash_table
->data_size
);
135 entry
= (SgenHashTableEntry
*)sgen_alloc_internal (hash_table
->entry_mem_type
);
137 memcpy (entry
->data
, new_value
, hash_table
->data_size
);
139 entry
->next
= hash_table
->table
[hash
];
140 hash_table
->table
[hash
] = entry
;
142 hash_table
->num_entries
++;
148 sgen_hash_table_set_value (SgenHashTable
*hash_table
, gpointer key
, gpointer new_value
, gpointer old_value
)
151 SgenHashTableEntry
*entry
;
153 entry
= lookup (hash_table
, key
, &hash
);
157 memcpy (old_value
, entry
->data
, hash_table
->data_size
);
158 memcpy (entry
->data
, new_value
, hash_table
->data_size
);
166 sgen_hash_table_set_key (SgenHashTable
*hash_table
, gpointer old_key
, gpointer new_key
)
169 SgenHashTableEntry
*entry
;
171 entry
= lookup (hash_table
, old_key
, &hash
);
174 entry
->key
= new_key
;
182 sgen_hash_table_remove (SgenHashTable
*hash_table
, gpointer key
, gpointer data_return
)
184 SgenHashTableEntry
*entry
, *prev
;
186 GEqualFunc equal
= hash_table
->equal_func
;
188 rehash_if_necessary (hash_table
);
189 hash
= hash_table
->hash_func (key
) % hash_table
->size
;
192 for (entry
= hash_table
->table
[hash
]; entry
; entry
= entry
->next
) {
193 if ((equal
&& equal (entry
->key
, key
)) || (!equal
&& entry
->key
== key
)) {
195 prev
->next
= entry
->next
;
197 hash_table
->table
[hash
] = entry
->next
;
199 hash_table
->num_entries
--;
202 memcpy (data_return
, entry
->data
, hash_table
->data_size
);
204 sgen_free_internal (entry
, hash_table
->entry_mem_type
);
215 sgen_hash_table_clean (SgenHashTable
*hash_table
)
219 if (!hash_table
->size
) {
220 SGEN_ASSERT (1, !hash_table
->table
, "clean should reset hash_table->table");
221 SGEN_ASSERT (1, !hash_table
->num_entries
, "clean should reset hash_table->num_entries");
225 for (i
= 0; i
< hash_table
->size
; ++i
) {
226 SgenHashTableEntry
*entry
= hash_table
->table
[i
];
228 SgenHashTableEntry
*next
= entry
->next
;
229 sgen_free_internal (entry
, hash_table
->entry_mem_type
);
234 sgen_free_internal_dynamic (hash_table
->table
, hash_table
->size
* sizeof (SgenHashTableEntry
*), hash_table
->table_mem_type
);
236 hash_table
->table
= NULL
;
237 hash_table
->size
= 0;
238 hash_table
->num_entries
= 0;
242 sgen_init_hash_table (void)
244 #ifdef HEAVY_STATISTICS
245 mono_counters_register ("Hash table lookups", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &stat_lookups
);
246 mono_counters_register ("Hash table lookup iterations", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &stat_lookup_iterations
);
247 mono_counters_register ("Hash table lookup max iterations", MONO_COUNTER_GC
| MONO_COUNTER_ULONG
, &stat_lookup_max_iterations
);