diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index cc7fe6b27..cc28d687c 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -2263,6 +2263,7 @@ GHashTableIter g_hash_table_iter_init g_hash_table_iter_next g_hash_table_iter_get_hash_table +g_hash_table_iter_replace g_hash_table_iter_remove g_hash_table_iter_steal diff --git a/glib/ghash.c b/glib/ghash.c index f381eaa33..00a2a6bd8 100644 --- a/glib/ghash.c +++ b/glib/ghash.c @@ -805,6 +805,112 @@ g_hash_table_iter_remove (GHashTableIter *iter) iter_remove_or_steal ((RealIter *) iter, TRUE); } +/* + * g_hash_table_insert_node: + * @hash_table: our #GHashTable + * @node_index: pointer to node to insert/replace + * @key_hash: key hash + * @key: key to replace with + * @value: value to replace with + * + * Inserts a value at @node_index in the hash table and updates it. + */ +static void +g_hash_table_insert_node (GHashTable *hash_table, + guint node_index, + guint key_hash, + gpointer key, + gpointer value, + gboolean keep_new_key) +{ + guint old_hash; + gpointer old_key; + gpointer old_value; + + if (G_UNLIKELY (hash_table->keys == hash_table->values && key != value)) + hash_table->values = g_memdup (hash_table->keys, sizeof (gpointer) * hash_table->size); + + old_hash = hash_table->hashes[node_index]; + old_key = hash_table->keys[node_index]; + old_value = hash_table->values[node_index]; + + if (HASH_IS_REAL (old_hash)) + { + if (keep_new_key) + hash_table->keys[node_index] = key; + hash_table->values[node_index] = value; + } + else + { + hash_table->keys[node_index] = key; + hash_table->values[node_index] = value; + hash_table->hashes[node_index] = key_hash; + + hash_table->nnodes++; + + if (HASH_IS_UNUSED (old_hash)) + { + /* We replaced an empty node, and not a tombstone */ + hash_table->noccupied++; + g_hash_table_maybe_resize (hash_table); + } + +#ifndef G_DISABLE_ASSERT + hash_table->version++; +#endif + } + + if (HASH_IS_REAL (old_hash)) + { + if (hash_table->key_destroy_func) + hash_table->key_destroy_func (keep_new_key ? old_key : key); + if (hash_table->value_destroy_func) + hash_table->value_destroy_func (old_value); + } +} + +/** + * g_hash_table_iter_replace: + * @iter: an initialized #GHashTableIter. + * @value: the value to replace with + * + * Replaces the value currently pointed to by the iterator + * from its associated #GHashTable. Can only be called after + * g_hash_table_iter_next() returned %TRUE. + * + * If you supplied a @value_destroy_func when creating the #GHashTable, + * the old value is freed using that function. + * + * Since: 2.29.9 + **/ +void +g_hash_table_iter_replace (GHashTableIter *iter, + gpointer value) +{ + RealIter *ri; + guint node_hash; + gpointer key; + + ri = (RealIter *) iter; + + g_return_if_fail (ri != NULL); +#ifndef G_DISABLE_ASSERT + g_return_if_fail (ri->version == ri->hash_table->version); +#endif + g_return_if_fail (ri->position >= 0); + g_return_if_fail (ri->position < ri->hash_table->size); + + node_hash = ri->hash_table->hashes[ri->position]; + key = ri->hash_table->keys[ri->position]; + + g_hash_table_insert_node (ri->hash_table, ri->position, node_hash, key, value, TRUE); + +#ifndef G_DISABLE_ASSERT + ri->version++; + ri->hash_table->version++; +#endif +} + /** * g_hash_table_iter_steal: * @iter: an initialized #GHashTableIter. @@ -985,56 +1091,14 @@ g_hash_table_insert_internal (GHashTable *hash_table, gpointer value, gboolean keep_new_key) { - guint node_index; guint key_hash; - guint old_hash; - gpointer old_key; - gpointer old_value; + guint node_index; g_return_if_fail (hash_table != NULL); - if (G_UNLIKELY (hash_table->keys == hash_table->values && key != value)) - hash_table->values = g_memdup (hash_table->keys, sizeof (gpointer) * hash_table->size); - node_index = g_hash_table_lookup_node (hash_table, key, &key_hash); - old_hash = hash_table->hashes[node_index]; - old_key = hash_table->keys[node_index]; - old_value = hash_table->values[node_index]; - - if (HASH_IS_REAL (old_hash)) - { - if (keep_new_key) - hash_table->keys[node_index] = key; - hash_table->values[node_index] = value; - } - else - { - hash_table->keys[node_index] = key; - hash_table->values[node_index] = value; - hash_table->hashes[node_index] = key_hash; - - hash_table->nnodes++; - - if (HASH_IS_UNUSED (old_hash)) - { - /* We replaced an empty node, and not a tombstone */ - hash_table->noccupied++; - g_hash_table_maybe_resize (hash_table); - } - -#ifndef G_DISABLE_ASSERT - hash_table->version++; -#endif - } - - if (HASH_IS_REAL (old_hash)) - { - if (hash_table->key_destroy_func) - hash_table->key_destroy_func (keep_new_key ? old_key : key); - if (hash_table->value_destroy_func) - hash_table->value_destroy_func (old_value); - } + g_hash_table_insert_node (hash_table, node_index, key_hash, key, value, keep_new_key); } /** diff --git a/glib/ghash.h b/glib/ghash.h index 9128721bc..3bc822698 100644 --- a/glib/ghash.h +++ b/glib/ghash.h @@ -105,6 +105,8 @@ gboolean g_hash_table_iter_next (GHashTableIter *iter, gpointer *value); GHashTable* g_hash_table_iter_get_hash_table (GHashTableIter *iter); void g_hash_table_iter_remove (GHashTableIter *iter); +void g_hash_table_iter_replace (GHashTableIter *iter, + gpointer value); void g_hash_table_iter_steal (GHashTableIter *iter); /* keeping hash tables alive */ diff --git a/glib/glib.symbols b/glib/glib.symbols index d8a18b9f9..bea278433 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -382,6 +382,7 @@ g_hash_table_iter_init g_hash_table_iter_next g_hash_table_iter_get_hash_table g_hash_table_iter_remove +g_hash_table_iter_replace g_hash_table_iter_steal g_hook_alloc g_hook_compare_ids diff --git a/glib/tests/hash.c b/glib/tests/hash.c index 8ef8208da..687447c52 100644 --- a/glib/tests/hash.c +++ b/glib/tests/hash.c @@ -539,6 +539,7 @@ test_hash_misc (void) GHashTableIter iter; gpointer ikey, ivalue; int result_array[10000]; + int n_array[1]; hash_table = g_hash_table_new (my_hash, my_hash_equal); fill_hash_table_and_array (hash_table); @@ -594,6 +595,28 @@ test_hash_misc (void) g_hash_table_foreach (hash_table, my_hash_callback_remove_test, NULL); g_hash_table_destroy (hash_table); + + hash_table = g_hash_table_new (my_hash, my_hash_equal); + fill_hash_table_and_array (hash_table); + + n_array[0] = 1; + + g_hash_table_iter_init (&iter, hash_table); + for (i = 0; i < 10000; i++) + { + g_assert (g_hash_table_iter_next (&iter, &ikey, &ivalue)); + g_hash_table_iter_replace (&iter, &n_array[0]); + } + + g_hash_table_iter_init (&iter, hash_table); + for (i = 0; i < 10000; i++) + { + g_assert (g_hash_table_iter_next (&iter, &ikey, &ivalue)); + + g_assert (ivalue == &n_array[0]); + } + + g_hash_table_destroy (hash_table); } static gint destroy_counter;