void g_hash_table_foreach (GHashTable *hash_table, GHFunc func, gpointer user_data) { gint i; for (i = 0; i < hash_table->size; i++) { guint node_hash = hash_table->hashes[i]; gpointer node_key = hash_table->keys[i]; gpointer node_value = hash_table->values[i]; if (HASH_IS_REAL (node_hash)) (* func) (node_key, node_value, user_data); } }
static bool hev_hash_table_remove_internal (HevHashTable *self, const void *key, bool notify) { unsigned int node_index; unsigned int node_hash; if (!self) return false; node_index = hev_hash_table_lookup_node (self, key, &node_hash); if (!HASH_IS_REAL (self->hashes[node_index])) return false; hev_hash_table_remove_node (self, node_index, notify); hev_hash_table_maybe_resize (self); return true; }
bool hev_hash_table_lookup_extended (HevHashTable *self, const void *lookup_key, void **orig_key, void **value) { unsigned int node_index; unsigned int node_hash; if (!self) return false; node_index = hev_hash_table_lookup_node (self, lookup_key, &node_hash); if (!HASH_IS_REAL (self->hashes[node_index])) return false; if (orig_key) *orig_key = self->keys[node_index]; if (value) *value = self->values[node_index]; return true; }
/** * g_hash_table_find: * @hash_table: a #GHashTable. * @predicate: function to test the key/value pairs for a certain property. * @user_data: user data to pass to the function. * * Calls the given function for key/value pairs in the #GHashTable until * @predicate returns %TRUE. The function is passed the key and value of * each pair, and the given @user_data parameter. The hash table may not * be modified while iterating over it (you can't add/remove items). * * Note, that hash tables are really only optimized for forward lookups, * i.e. g_hash_table_lookup(). * So code that frequently issues g_hash_table_find() or * g_hash_table_foreach() (e.g. in the order of once per every entry in a * hash table) should probably be reworked to use additional or different * data structures for reverse lookups (keep in mind that an O(n) find/foreach * operation issued for all n values in a hash table ends up needing O(n*n) * operations). * * Return value: The value of the first key/value pair is returned, * for which @predicate evaluates to %TRUE. If no pair with the * requested property is found, %NULL is returned. * * Since: 2.4 **/ gpointer g_hash_table_find (GHashTable *hash_table, GHRFunc predicate, gpointer user_data) { gint i; #ifndef G_DISABLE_ASSERT gint version; #endif gboolean match; g_return_val_if_fail (hash_table != NULL, NULL); g_return_val_if_fail (predicate != NULL, NULL); #ifndef G_DISABLE_ASSERT version = hash_table->version; #endif match = FALSE; for (i = 0; i < hash_table->size; i++) { guint node_hash = hash_table->hashes[i]; gpointer node_key = hash_table->keys[i]; gpointer node_value = hash_table->values[i]; if (HASH_IS_REAL (node_hash)) match = predicate (node_key, node_value, user_data); #ifndef G_DISABLE_ASSERT g_return_val_if_fail (version == hash_table->version, NULL); #endif if (match) return node_value; } return NULL; }
/* * g_hash_table_foreach_remove_or_steal: * @hash_table: our #GHashTable * @func: the user's callback function * @user_data: data for @func * @notify: %TRUE if the destroy notify handlers are to be called * * Implements the common logic for g_hash_table_foreach_remove() and * g_hash_table_foreach_steal(). * * Iterates over every node in the table, calling @func with the key * and value of the node (and @user_data). If @func returns %TRUE the * node is removed from the table. * * If @notify is true then the destroy notify handlers will be called * for each removed node. */ static guint g_hash_table_foreach_remove_or_steal (GHashTable *hash_table, GHRFunc func, gpointer user_data, gboolean notify) { guint deleted = 0; gint i; #ifndef G_DISABLE_ASSERT gint version = hash_table->version; #endif for (i = 0; i < hash_table->size; i++) { guint node_hash = hash_table->hashes[i]; gpointer node_key = hash_table->keys[i]; gpointer node_value = hash_table->values[i]; if (HASH_IS_REAL (node_hash) && (* func) (node_key, node_value, user_data)) { g_hash_table_remove_node (hash_table, i, notify); deleted++; } #ifndef G_DISABLE_ASSERT g_return_val_if_fail (version == hash_table->version, 0); #endif } g_hash_table_maybe_resize (hash_table); #ifndef G_DISABLE_ASSERT if (deleted > 0) hash_table->version++; #endif return deleted; }
static unsigned int hev_hash_table_foreach_remove_or_steal (HevHashTable *self, HevHTRFunc func, void *user_data, bool notify) { unsigned int deleted = 0; int i; for (i=0; i<self->size; i++) { unsigned int node_hash = self->hashes[i]; void *node_key = self->keys[i]; void *node_value = self->values[i]; if (HASH_IS_REAL (node_hash) && (* func) (node_key, node_value, user_data)) { hev_hash_table_remove_node (self, i, notify); deleted++; } } hev_hash_table_maybe_resize (self); return deleted; }
/** * Fetch next entry from iterator. * * @param hxi the hash table iterator * @param vp where value is written, if non-NULL * * @return TRUE if a new entry exists, FALSE otherwise. */ bool hikset_iter_next(hikset_iter_t *hxi, void **vp) { const hikset_t *hx; hikset_iter_check(hxi); hx = hxi->hx; while (hxi->pos < hx->kset.size && !HASH_IS_REAL(hx->kset.hashes[hxi->pos])) hxi->pos++; if (hxi->pos >= hx->kset.size) return FALSE; if (vp != NULL) { void *kptr = deconstify_pointer(hx->kset.keys[hxi->pos]); *vp = ptr_add_offset(kptr, -hx->offset); } hxi->pos++; return TRUE; }
/** * Traverse table, invoking callback for each entry. * * @param hx the hash table * @param fn callback to invoke * @param data additional callback parameter */ void hikset_foreach(const hikset_t *hx, data_fn_t fn, void *data) { unsigned *hp, *end; size_t i, n; hikset_check(hx); end = &hx->kset.hashes[hx->kset.size]; hash_refcnt_inc(HASH(hx)); /* Prevent any key relocation */ for (i = n = 0, hp = hx->kset.hashes; hp != end; i++, hp++) { if (HASH_IS_REAL(*hp)) { void *kptr = deconstify_pointer(hx->kset.keys[i]); (*fn)(ptr_add_offset(kptr, -hx->offset), data); n++; } } g_assert(n == hx->kset.items); hash_refcnt_dec(HASH(hx)); }
static void hev_hash_table_remove_all_nodes (HevHashTable *self, bool notify) { int i = -1; void *key = NULL, *value = NULL; self->nnodes = 0; self->noccupied = 0; if (!notify || ((NULL == self->key_destroy_notify) && (NULL == self->value_destroy_notify))) { memset (self->hashes, 0, self->size * sizeof (unsigned int)); memset (self->keys, 0, self->size * sizeof (void *)); memset (self->values, 0, self->size * sizeof (void *)); return ; } for (i=0; i<self->size; i++) { if (HASH_IS_REAL (self->hashes[i])) { key = self->keys[i]; value = self->values[i]; self->hashes[i] = UNUSED_HASH_VALUE; self->keys[i] = NULL; self->values[i] = NULL; if (NULL != self->key_destroy_notify) self->key_destroy_notify (key); if (NULL != self->value_destroy_notify) self->value_destroy_notify (value); } else if (HASH_IS_TOMBSTONE (self->hashes[i])) { self->hashes[i] = UNUSED_HASH_VALUE; } } }
/** * Traverse table, invoking callback for each entry and removing it when * the callback function returns TRUE. * * @param hx the hash table * @param fn callback to invoke * @param data additional callback parameter * * @return the number of entries removed from the hash table. */ size_t hikset_foreach_remove(hikset_t *hx, data_rm_fn_t fn, void *data) { unsigned *hp, *end; size_t i, n, nr; hikset_check(hx); end = &hx->kset.hashes[hx->kset.size]; hash_refcnt_inc(HASH(hx)); /* Prevent any key relocation */ for (i = n = nr = 0, hp = hx->kset.hashes; hp != end; i++, hp++) { if (HASH_IS_REAL(*hp)) { void *kptr = deconstify_pointer(hx->kset.keys[i]); bool r = (*fn)(ptr_add_offset(kptr, -hx->offset), data); n++; if (r) { nr++; hash_keyset_erect_tombstone(&hx->kset, i); hx->stamp++; } } } g_assert(n == hx->kset.items); g_assert(nr <= hx->kset.items); hash_refcnt_dec(HASH(hx)); hx->kset.items -= nr; if (nr != 0) hash_resize_as_needed(HASH(hx)); return nr; }
/* * g_hash_table_remove_internal: * @hash_table: our #GHashTable * @key: the key to remove * @notify: %TRUE if the destroy notify handlers are to be called * Return value: %TRUE if a node was found and removed, else %FALSE * * Implements the common logic for the g_hash_table_remove() and * g_hash_table_steal() functions. * * Do a lookup of @key and remove it if it is found, calling the * destroy notify handlers only if @notify is %TRUE. */ static gboolean g_hash_table_remove_internal (GHashTable *hash_table, gconstpointer key, gboolean notify) { guint node_index; guint node_hash; g_return_val_if_fail (hash_table != NULL, FALSE); node_index = g_hash_table_lookup_node (hash_table, key, &node_hash); if (!HASH_IS_REAL (hash_table->hashes[node_index])) return FALSE; g_hash_table_remove_node (hash_table, node_index, notify); g_hash_table_maybe_resize (hash_table); #ifndef G_DISABLE_ASSERT hash_table->version++; #endif return TRUE; }
/** * g_hash_table_lookup_extended: * @hash_table: a #GHashTable * @lookup_key: the key to look up * @orig_key: return location for the original key, or %NULL * @value: return location for the value associated with the key, or %NULL * * Looks up a key in the #GHashTable, returning the original key and the * associated value and a #gboolean which is %TRUE if the key was found. This * is useful if you need to free the memory allocated for the original key, * for example before calling g_hash_table_remove(). * * You can actually pass %NULL for @lookup_key to test * whether the %NULL key exists, provided the hash and equal functions * of @hash_table are %NULL-safe. * * Return value: %TRUE if the key was found in the #GHashTable. **/ gboolean g_hash_table_lookup_extended (GHashTable *hash_table, gconstpointer lookup_key, gpointer *orig_key, gpointer *value) { guint node_index; guint node_hash; g_return_val_if_fail (hash_table != NULL, FALSE); node_index = g_hash_table_lookup_node (hash_table, lookup_key, &node_hash); if (!HASH_IS_REAL (hash_table->hashes[node_index])) return FALSE; if (orig_key) *orig_key = hash_table->keys[node_index]; if (value) *value = hash_table->values[node_index]; return TRUE; }
static gboolean g_hash_table_insert_node (GHashTable *hash_table, guint node_index, guint key_hash, gpointer new_key, gpointer new_value, gboolean keep_new_key, gboolean reusing_key) { gboolean already_exists; guint old_hash; gpointer key_to_free = NULL; gpointer value_to_free = NULL; old_hash = hash_table->hashes[node_index]; already_exists = HASH_IS_REAL (old_hash); if (already_exists) { value_to_free = hash_table->values[node_index]; if (keep_new_key) { key_to_free = hash_table->keys[node_index]; hash_table->keys[node_index] = new_key; } else key_to_free = new_key; } else { hash_table->hashes[node_index] = key_hash; hash_table->keys[node_index] = new_key; } if (G_UNLIKELY (hash_table->keys == hash_table->values && hash_table->keys[node_index] != new_value)) hash_table->values = g_memdup (hash_table->keys, sizeof (gpointer) * hash_table->size); hash_table->values[node_index] = new_value; if (!already_exists) { hash_table->nnodes++; if (HASH_IS_UNUSED (old_hash)) { hash_table->noccupied++; g_hash_table_maybe_resize (hash_table); } } if (already_exists) { if (hash_table->key_destroy_func && !reusing_key) (* hash_table->key_destroy_func) (key_to_free); if (hash_table->value_destroy_func) (* hash_table->value_destroy_func) (value_to_free); } return !already_exists; }
static bool hev_hash_table_insert_node (HevHashTable *self, unsigned int node_index, unsigned int key_hash, void *new_key, void *new_value, bool keep_new_key, bool reusing_key) { bool already_exists; unsigned int old_hash; void *key_to_free = NULL; void *value_to_free = NULL; old_hash = self->hashes[node_index]; already_exists = HASH_IS_REAL (old_hash); /* Proceed in three steps. First, deal with the key because it is the * most complicated. Then consider if we need to split the table in * two (because writing the value will result in the set invariant * becoming broken). Then deal with the value. * * There are three cases for the key: * * - entry already exists in table, reusing key: * free the just-passed-in new_key and use the existing value * * - entry already exists in table, not reusing key: * free the entry in the table, use the new key * * - entry not already in table: * use the new key, free nothing * * We update the hash at the same time... */ if (already_exists) { /* Note: we must record the old value before writing the new key * because we might change the value in the event that the two * arrays are shared. */ value_to_free = self->values[node_index]; if (keep_new_key) { key_to_free = self->keys[node_index]; self->keys[node_index] = new_key; } else { key_to_free = new_key; } } else { self->hashes[node_index] = key_hash; self->keys[node_index] = new_key; } /* Step two: check if the value that we are about to write to the * table is the same as the key in the same position. If it's not, * split the table. */ if (self->keys == self->values && self->keys[node_index] != new_value) self->values = memdup (self->keys, sizeof (void *) * self->size); /* Step 3: Actually do the write */ self->values[node_index] = new_value; /* Now, the bookkeeping... */ if (!already_exists) { self->nnodes++; if (HASH_IS_UNUSED (old_hash)) { /* We replaced an empty node, and not a tombstone */ self->noccupied++; hev_hash_table_maybe_resize (self); } } if (already_exists) { if (self->key_destroy_notify && !reusing_key) (* self->key_destroy_notify) (key_to_free); if (self->value_destroy_notify) (* self->value_destroy_notify) (value_to_free); } return !already_exists; }