/** * \file * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "config.h" #ifdef HAVE_SGEN_GC #include #include #include #ifdef HEAVY_STATISTICS static guint64 stat_lookups; static guint64 stat_lookup_iterations; static guint64 stat_lookup_max_iterations; #endif static void rehash (SgenHashTable *hash_table) { SgenHashTableEntry **old_hash = hash_table->table; guint old_hash_size = hash_table->size; guint i, hash, new_size; SgenHashTableEntry **new_hash; SgenHashTableEntry *entry, *next; if (!old_hash) { sgen_register_fixed_internal_mem_type (hash_table->entry_mem_type, sizeof (SgenHashTableEntry*) + sizeof (gpointer) + hash_table->data_size); new_size = 13; } else { new_size = g_spaced_primes_closest (hash_table->num_entries); } new_hash = (SgenHashTableEntry **)sgen_alloc_internal_dynamic (new_size * sizeof (SgenHashTableEntry*), hash_table->table_mem_type, TRUE); for (i = 0; i < old_hash_size; ++i) { for (entry = old_hash [i]; entry; entry = next) { hash = hash_table->hash_func (entry->key) % new_size; next = entry->next; entry->next = new_hash [hash]; new_hash [hash] = entry; } } sgen_free_internal_dynamic (old_hash, old_hash_size * sizeof (SgenHashTableEntry*), hash_table->table_mem_type); hash_table->table = new_hash; hash_table->size = new_size; } static void rehash_if_necessary (SgenHashTable *hash_table) { if (hash_table->num_entries >= hash_table->size * 2) rehash (hash_table); SGEN_ASSERT (1, hash_table->size, "rehash guarantees size > 0"); } static SgenHashTableEntry* lookup (SgenHashTable *hash_table, gpointer key, guint *_hash) { SgenHashTableEntry *entry; guint hash; GEqualFunc equal = hash_table->equal_func; #ifdef HEAVY_STATISTICS guint64 iterations = 0; ++stat_lookups; #endif if (!hash_table->size) return NULL; hash = hash_table->hash_func (key) % hash_table->size; if (_hash) *_hash = hash; for (entry = hash_table->table [hash]; entry; entry = entry->next) { #ifdef HEAVY_STATISTICS ++stat_lookup_iterations; ++iterations; if (iterations > stat_lookup_max_iterations) stat_lookup_max_iterations = iterations; #endif if ((equal && equal (entry->key, key)) || (!equal && entry->key == key)) return entry; } return NULL; } gpointer sgen_hash_table_lookup (SgenHashTable *hash_table, gpointer key) { SgenHashTableEntry *entry = lookup (hash_table, key, NULL); if (!entry) return NULL; return entry->data; } gboolean sgen_hash_table_replace (SgenHashTable *hash_table, gpointer key, gpointer new_value, gpointer old_value) { guint hash; SgenHashTableEntry *entry; rehash_if_necessary (hash_table); entry = lookup (hash_table, key, &hash); if (entry) { if (old_value) memcpy (old_value, entry->data, hash_table->data_size); memcpy (entry->data, new_value, hash_table->data_size); return FALSE; } entry = (SgenHashTableEntry *)sgen_alloc_internal (hash_table->entry_mem_type); entry->key = key; memcpy (entry->data, new_value, hash_table->data_size); entry->next = hash_table->table [hash]; hash_table->table [hash] = entry; hash_table->num_entries++; return TRUE; } gboolean sgen_hash_table_set_value (SgenHashTable *hash_table, gpointer key, gpointer new_value, gpointer old_value) { guint hash; SgenHashTableEntry *entry; entry = lookup (hash_table, key, &hash); if (entry) { if (old_value) memcpy (old_value, entry->data, hash_table->data_size); memcpy (entry->data, new_value, hash_table->data_size); return TRUE; } return FALSE; } gboolean sgen_hash_table_set_key (SgenHashTable *hash_table, gpointer old_key, gpointer new_key) { guint hash; SgenHashTableEntry *entry; entry = lookup (hash_table, old_key, &hash); if (entry) { entry->key = new_key; return TRUE; } return FALSE; } gboolean sgen_hash_table_remove (SgenHashTable *hash_table, gpointer key, gpointer data_return) { SgenHashTableEntry *entry, *prev; guint hash; GEqualFunc equal = hash_table->equal_func; rehash_if_necessary (hash_table); hash = hash_table->hash_func (key) % hash_table->size; prev = NULL; for (entry = hash_table->table [hash]; entry; entry = entry->next) { if ((equal && equal (entry->key, key)) || (!equal && entry->key == key)) { if (prev) prev->next = entry->next; else hash_table->table [hash] = entry->next; hash_table->num_entries--; if (data_return) memcpy (data_return, entry->data, hash_table->data_size); sgen_free_internal (entry, hash_table->entry_mem_type); return TRUE; } prev = entry; } return FALSE; } void sgen_hash_table_clean (SgenHashTable *hash_table) { guint i; if (!hash_table->size) { SGEN_ASSERT (1, !hash_table->table, "clean should reset hash_table->table"); SGEN_ASSERT (1, !hash_table->num_entries, "clean should reset hash_table->num_entries"); return; } for (i = 0; i < hash_table->size; ++i) { SgenHashTableEntry *entry = hash_table->table [i]; while (entry) { SgenHashTableEntry *next = entry->next; sgen_free_internal (entry, hash_table->entry_mem_type); entry = next; } } sgen_free_internal_dynamic (hash_table->table, hash_table->size * sizeof (SgenHashTableEntry*), hash_table->table_mem_type); hash_table->table = NULL; hash_table->size = 0; hash_table->num_entries = 0; } void sgen_init_hash_table (void) { #ifdef HEAVY_STATISTICS mono_counters_register ("Hash table lookups", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_lookups); mono_counters_register ("Hash table lookup iterations", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_lookup_iterations); mono_counters_register ("Hash table lookup max iterations", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_lookup_max_iterations); #endif } #endif