GHash: make sets with refcounted keys work correctly

When keys == values, we have to be careful about the order in
which we replace their elements.
This commit is contained in:
Matthias Clasen 2011-05-01 10:55:24 -04:00
parent be991170fa
commit d09df426ba
2 changed files with 96 additions and 3 deletions

View File

@ -982,6 +982,9 @@ g_hash_table_insert_internal (GHashTable *hash_table,
if (HASH_IS_REAL (old_hash))
{
if (hash_table->value_destroy_func)
hash_table->value_destroy_func (hash_table->values[node_index]);
if (keep_new_key)
{
if (hash_table->key_destroy_func)
@ -994,9 +997,6 @@ g_hash_table_insert_internal (GHashTable *hash_table,
hash_table->key_destroy_func (key);
}
if (hash_table->value_destroy_func)
hash_table->value_destroy_func (hash_table->values[node_index]);
hash_table->values[node_index] = value;
}
else

View File

@ -733,6 +733,98 @@ test_remove_all (void)
g_hash_table_unref (h);
}
typedef struct {
gint ref_count;
const gchar *key;
} RefCountedKey;
static guint
hash_func (gconstpointer key)
{
const RefCountedKey *rkey = key;
return g_str_hash (rkey->key);
}
static gboolean
eq_func (gconstpointer a, gconstpointer b)
{
const RefCountedKey *aa = a;
const RefCountedKey *bb = b;
return g_strcmp0 (aa->key, bb->key) == 0;
}
static void
key_unref (gpointer data)
{
RefCountedKey *key = data;
g_assert (key->ref_count > 0);
key->ref_count -= 1;
if (key->ref_count == 0)
g_free (key);
}
static RefCountedKey *
key_ref (RefCountedKey *key)
{
key->ref_count += 1;
return key;
}
static RefCountedKey *
key_new (const gchar *key)
{
RefCountedKey *rkey;
rkey = g_new (RefCountedKey, 1);
rkey->ref_count = 1;
rkey->key = key;
return rkey;
}
static void
set_ref_hash_test (void)
{
GHashTable *h;
RefCountedKey *key1;
RefCountedKey *key2;
h = g_hash_table_new_full (hash_func, eq_func, key_unref, key_unref);
key1 = key_new ("a");
key2 = key_new ("a");
g_assert_cmpint (key1->ref_count, ==, 1);
g_assert_cmpint (key2->ref_count, ==, 1);
g_hash_table_insert (h, key_ref (key1), key_ref (key1));
g_assert_cmpint (key1->ref_count, ==, 3);
g_assert_cmpint (key2->ref_count, ==, 1);
g_hash_table_replace (h, key_ref (key2), key_ref (key2));
g_assert_cmpint (key1->ref_count, ==, 1);
g_assert_cmpint (key2->ref_count, ==, 3);
g_hash_table_remove (h, key1);
g_assert_cmpint (key1->ref_count, ==, 1);
g_assert_cmpint (key2->ref_count, ==, 1);
g_hash_table_unref (h);
key_unref (key1);
key_unref (key2);
}
int
main (int argc, char *argv[])
{
@ -748,6 +840,7 @@ main (int argc, char *argv[])
g_test_add_func ("/hash/double", double_hash_test);
g_test_add_func ("/hash/string", string_hash_test);
g_test_add_func ("/hash/set", set_hash_test);
g_test_add_func ("/hash/set-ref", set_ref_hash_test);
g_test_add_func ("/hash/ref", test_hash_ref);
g_test_add_func ("/hash/remove-all", test_remove_all);