gboolean have_tombstone = FALSE;
guint step = 0;
+ /* If this happens, then the application is probably doing too much work
+ * from a destroy notifier. The alternative would be to crash any second
+ * (as keys, etc. will be NULL).
+ * Applications need to either use g_hash_table_destroy, or ensure the hash
+ * table is empty prior to removing the last reference using g_hash_table_unref(). */
+ g_assert (hash_table->ref_count > 0);
+
hash_value = hash_table->hash_func (key);
if (G_UNLIKELY (!HASH_IS_REAL (hash_value)))
hash_value = 2;
*/
static void
g_hash_table_remove_all_nodes (GHashTable *hash_table,
- gboolean notify)
+ gboolean notify,
+ gboolean destruction)
{
int i;
gpointer key;
gpointer value;
+ gint old_size;
+ gpointer *old_keys;
+ gpointer *old_values;
+ guint *old_hashes;
+
+ /* If the hash table is already empty, there is nothing to be done. */
+ if (hash_table->nnodes == 0)
+ return;
hash_table->nnodes = 0;
hash_table->noccupied = 0;
(hash_table->key_destroy_func == NULL &&
hash_table->value_destroy_func == NULL))
{
- memset (hash_table->hashes, 0, hash_table->size * sizeof (guint));
- memset (hash_table->keys, 0, hash_table->size * sizeof (gpointer));
- memset (hash_table->values, 0, hash_table->size * sizeof (gpointer));
+ if (!destruction)
+ {
+ memset (hash_table->hashes, 0, hash_table->size * sizeof (guint));
+ memset (hash_table->keys, 0, hash_table->size * sizeof (gpointer));
+ memset (hash_table->values, 0, hash_table->size * sizeof (gpointer));
+ }
return;
}
- for (i = 0; i < hash_table->size; i++)
+ /* Keep the old storage space around to iterate over it. */
+ old_size = hash_table->size;
+ old_keys = hash_table->keys;
+ old_values = hash_table->values;
+ old_hashes = hash_table->hashes;
+
+ /* Now create a new storage space; If the table is destroyed we can use the
+ * shortcut of not creating a new storage. This saves the allocation at the
+ * cost of not allowing any recursive access.
+ * However, the application doesn't own any reference anymore, so access
+ * is not allowed. If accesses are done, then either an assert or crash
+ * *will* happen. */
+ g_hash_table_set_shift (hash_table, HASH_TABLE_MIN_SHIFT);
+ if (!destruction)
{
- if (HASH_IS_REAL (hash_table->hashes[i]))
+ hash_table->keys = g_new0 (gpointer, hash_table->size);
+ hash_table->values = hash_table->keys;
+ hash_table->hashes = g_new0 (guint, hash_table->size);
+ }
+ else
+ {
+ hash_table->keys = NULL;
+ hash_table->values = NULL;
+ hash_table->hashes = NULL;
+ }
+
+ for (i = 0; i < old_size; i++)
+ {
+ if (HASH_IS_REAL (old_hashes[i]))
{
- key = hash_table->keys[i];
- value = hash_table->values[i];
+ key = old_keys[i];
+ value = old_values[i];
- hash_table->hashes[i] = UNUSED_HASH_VALUE;
- hash_table->keys[i] = NULL;
- hash_table->values[i] = NULL;
+ old_hashes[i] = UNUSED_HASH_VALUE;
+ old_keys[i] = NULL;
+ old_values[i] = NULL;
if (hash_table->key_destroy_func != NULL)
hash_table->key_destroy_func (key);
if (hash_table->value_destroy_func != NULL)
hash_table->value_destroy_func (value);
}
- else if (HASH_IS_TOMBSTONE (hash_table->hashes[i]))
- {
- hash_table->hashes[i] = UNUSED_HASH_VALUE;
- }
}
+
+ /* Destroy old storage space. */
+ if (old_keys != old_values)
+ g_free (old_values);
+
+ g_free (old_keys);
+ g_free (old_hashes);
}
/*
* a similar fashion to g_direct_equal(), but without the overhead of
* a function call.
*
- * Return value: a new #GHashTable
+ * Returns: a new #GHashTable
*/
GHashTable *
g_hash_table_new (GHashFunc hash_func,
* allocated for the key and value that get called when removing the
* entry from the #GHashTable.
*
- * Return value: a new #GHashTable
+ * Since version 2.42 it is permissible for destroy notify functions to
+ * recursively remove further items from the hash table. This is only
+ * permissible if the application still holds a reference to the hash table.
+ * This means that you may need to ensure that the hash table is empty by
+ * calling g_hash_table_remove_all before releasing the last reference using
+ * g_hash_table_unref().
+ *
+ * Returns: a new #GHashTable
*/
GHashTable *
g_hash_table_new_full (GHashFunc hash_func,
* pointed to as a result of this advancement. If %FALSE is returned,
* @key and @value are not set, and the iterator becomes invalid.
*
- * Return value: %FALSE if the end of the #GHashTable has been reached.
+ * Returns: %FALSE if the end of the #GHashTable has been reached.
*
* Since: 2.16
*/
*
* Returns the #GHashTable associated with @iter.
*
- * Return value: the #GHashTable associated with @iter.
+ * Returns: the #GHashTable associated with @iter.
*
* Since: 2.16
*/
* otherwise you have to make sure that any dynamically allocated
* values are freed yourself.
*
+ * It is safe to continue iterating the #GHashTable afterward:
+ * |[<!-- language="C" -->
+ * while (g_hash_table_iter_next (&iter, &key, &value))
+ * {
+ * if (condition)
+ * g_hash_table_iter_remove (&iter);
+ * }
+ * ]|
+ *
* Since: 2.16
*/
void
* Atomically increments the reference count of @hash_table by one.
* This function is MT-safe and may be called from any thread.
*
- * Return value: the passed in #GHashTable
+ * Returns: the passed in #GHashTable
*
* Since: 2.10
*/
if (g_atomic_int_dec_and_test (&hash_table->ref_count))
{
- g_hash_table_remove_all_nodes (hash_table, TRUE);
+ g_hash_table_remove_all_nodes (hash_table, TRUE, TRUE);
if (hash_table->keys != hash_table->values)
g_free (hash_table->values);
g_free (hash_table->keys);
* and has the value %NULL. If you need this distinction, use
* g_hash_table_lookup_extended().
*
- * Return value: (allow-none): the associated value, or %NULL if the key is not found
+ * Returns: (allow-none): the associated value, or %NULL if the key is not found
*/
gpointer
g_hash_table_lookup (GHashTable *hash_table,
* 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
+ * Returns: %TRUE if the key was found in the #GHashTable
*/
gboolean
g_hash_table_lookup_extended (GHashTable *hash_table,
* @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
+ * Returns: %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.
hash_table->version++;
#endif
- g_hash_table_remove_all_nodes (hash_table, TRUE);
+ g_hash_table_remove_all_nodes (hash_table, TRUE, FALSE);
g_hash_table_maybe_resize (hash_table);
}
hash_table->version++;
#endif
- g_hash_table_remove_all_nodes (hash_table, FALSE);
+ g_hash_table_remove_all_nodes (hash_table, FALSE, FALSE);
g_hash_table_maybe_resize (hash_table);
}
* See #GHashTableIter for an alternative way to loop over the
* key/value pairs in the hash table.
*
- * Return value: the number of key/value pairs removed
+ * Returns: the number of key/value pairs removed
*/
guint
g_hash_table_foreach_remove (GHashTable *hash_table,
* See #GHashTableIter for an alternative way to loop over the
* key/value pairs in the hash table.
*
- * Return value: the number of key/value pairs removed.
+ * Returns: the number of key/value pairs removed.
*/
guint
g_hash_table_foreach_steal (GHashTable *hash_table,
* (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: (allow-none): The value of the first key/value pair is returned,
+ * Returns: (allow-none): 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.
*
*
* Returns the number of elements contained in the #GHashTable.
*
- * Return value: the number of key/value pairs in the #GHashTable.
+ * Returns: the number of key/value pairs in the #GHashTable.
*/
guint
g_hash_table_size (GHashTable *hash_table)
* Retrieves every key inside @hash_table. The returned data is valid
* until changes to the hash release those keys.
*
- * Return value: a #GList containing all the keys inside the hash
+ * Returns: a #GList containing all the keys inside the hash
* table. The content of the list is owned by the hash table and
* should not be modified or freed. Use g_list_free() when done
* using the list.
* Retrieves every value inside @hash_table. The returned data
* is valid until @hash_table is modified.
*
- * Return value: a #GList containing all the values inside the hash
+ * Returns: a #GList containing all the values inside the hash
* table. The content of the list is owned by the hash table and
* should not be modified or freed. Use g_list_free() when done
* using the list.