* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
+ * version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
+#include <config.h>
#include <stdio.h>
#include <string.h>
honeyman_hash (gconstpointer key)
{
const gchar *name = (const gchar *) key;
- gint size;
+ gsize size;
guint sum = 0;
g_assert (name != NULL);
gint value = 120;
gint *pvalue;
GList *keys, *values;
- gint keys_len, values_len;
+ gsize keys_len, values_len;
GHashTableIter iter;
gpointer ikey, ivalue;
int result_array[10000];
g_assert_cmpint (destroy_counter, ==, 0);
g_assert_cmpint (destroy_key_counter, ==, 0);
+ /* Test stealing on an empty hash table. */
+ g_hash_table_steal_all (h);
+ g_assert_cmpint (destroy_counter, ==, 0);
+ g_assert_cmpint (destroy_key_counter, ==, 0);
+
g_hash_table_insert (h, "abc", "ABC");
g_hash_table_insert (h, "cde", "CDE");
g_hash_table_insert (h, "xyz", "XYZ");
g_hash_table_unref (hash2);
}
+/* Test g_hash_table_steal_extended() works properly with existing and
+ * non-existing keys. */
+static void
+test_steal_extended (void)
+{
+ GHashTable *hash;
+ gchar *stolen_key = NULL, *stolen_value = NULL;
+
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ g_hash_table_insert (hash, g_strdup ("a"), g_strdup ("A"));
+ g_hash_table_insert (hash, g_strdup ("b"), g_strdup ("B"));
+ g_hash_table_insert (hash, g_strdup ("c"), g_strdup ("C"));
+ g_hash_table_insert (hash, g_strdup ("d"), g_strdup ("D"));
+ g_hash_table_insert (hash, g_strdup ("e"), g_strdup ("E"));
+ g_hash_table_insert (hash, g_strdup ("f"), g_strdup ("F"));
+
+ g_assert_true (g_hash_table_steal_extended (hash, "a",
+ (gpointer *) &stolen_key,
+ (gpointer *) &stolen_value));
+ g_assert_cmpstr (stolen_key, ==, "a");
+ g_assert_cmpstr (stolen_value, ==, "A");
+ g_clear_pointer (&stolen_key, g_free);
+ g_clear_pointer (&stolen_value, g_free);
+
+ g_assert_cmpuint (g_hash_table_size (hash), ==, 5);
+
+ g_assert_false (g_hash_table_steal_extended (hash, "a",
+ (gpointer *) &stolen_key,
+ (gpointer *) &stolen_value));
+ g_assert_null (stolen_key);
+ g_assert_null (stolen_value);
+
+ g_assert_false (g_hash_table_steal_extended (hash, "never a key",
+ (gpointer *) &stolen_key,
+ (gpointer *) &stolen_value));
+ g_assert_null (stolen_key);
+ g_assert_null (stolen_value);
+
+ g_assert_cmpuint (g_hash_table_size (hash), ==, 5);
+
+ g_hash_table_unref (hash);
+}
+
+/* Test that passing %NULL to the optional g_hash_table_steal_extended()
+ * arguments works. */
+static void
+test_steal_extended_optional (void)
+{
+ GHashTable *hash;
+ const gchar *stolen_key = NULL, *stolen_value = NULL;
+
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
+
+ g_hash_table_insert (hash, "b", "B");
+ g_hash_table_insert (hash, "c", "C");
+ g_hash_table_insert (hash, "d", "D");
+ g_hash_table_insert (hash, "e", "E");
+ g_hash_table_insert (hash, "f", "F");
+
+ g_assert_true (g_hash_table_steal_extended (hash, "b",
+ (gpointer *) &stolen_key,
+ NULL));
+ g_assert_cmpstr (stolen_key, ==, "b");
+
+ g_assert_cmpuint (g_hash_table_size (hash), ==, 4);
+
+ g_assert_false (g_hash_table_steal_extended (hash, "b",
+ (gpointer *) &stolen_key,
+ NULL));
+ g_assert_null (stolen_key);
+
+ g_assert_true (g_hash_table_steal_extended (hash, "c",
+ NULL,
+ (gpointer *) &stolen_value));
+ g_assert_cmpstr (stolen_value, ==, "C");
+
+ g_assert_cmpuint (g_hash_table_size (hash), ==, 3);
+
+ g_assert_false (g_hash_table_steal_extended (hash, "c",
+ NULL,
+ (gpointer *) &stolen_value));
+ g_assert_null (stolen_value);
+
+ g_assert_true (g_hash_table_steal_extended (hash, "d", NULL, NULL));
+
+ g_assert_cmpuint (g_hash_table_size (hash), ==, 2);
+
+ g_assert_false (g_hash_table_steal_extended (hash, "d", NULL, NULL));
+
+ g_assert_cmpuint (g_hash_table_size (hash), ==, 2);
+
+ g_hash_table_unref (hash);
+}
+
+/* Test g_hash_table_lookup_extended() works with its optional parameters
+ * sometimes set to %NULL. */
+static void
+test_lookup_extended (void)
+{
+ GHashTable *hash;
+ const gchar *original_key = NULL, *value = NULL;
+
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ g_hash_table_insert (hash, g_strdup ("a"), g_strdup ("A"));
+ g_hash_table_insert (hash, g_strdup ("b"), g_strdup ("B"));
+ g_hash_table_insert (hash, g_strdup ("c"), g_strdup ("C"));
+ g_hash_table_insert (hash, g_strdup ("d"), g_strdup ("D"));
+ g_hash_table_insert (hash, g_strdup ("e"), g_strdup ("E"));
+ g_hash_table_insert (hash, g_strdup ("f"), g_strdup ("F"));
+
+ g_assert_true (g_hash_table_lookup_extended (hash, "a",
+ (gpointer *) &original_key,
+ (gpointer *) &value));
+ g_assert_cmpstr (original_key, ==, "a");
+ g_assert_cmpstr (value, ==, "A");
+
+ g_assert_true (g_hash_table_lookup_extended (hash, "b",
+ NULL,
+ (gpointer *) &value));
+ g_assert_cmpstr (value, ==, "B");
+
+ g_assert_true (g_hash_table_lookup_extended (hash, "c",
+ (gpointer *) &original_key,
+ NULL));
+ g_assert_cmpstr (original_key, ==, "c");
+
+ g_assert_true (g_hash_table_lookup_extended (hash, "d", NULL, NULL));
+
+ g_assert_false (g_hash_table_lookup_extended (hash, "not a key",
+ (gpointer *) &original_key,
+ (gpointer *) &value));
+ g_assert_null (original_key);
+ g_assert_null (value);
+
+ g_assert_false (g_hash_table_lookup_extended (hash, "not a key",
+ NULL,
+ (gpointer *) &value));
+ g_assert_null (value);
+
+ g_assert_false (g_hash_table_lookup_extended (hash, "not a key",
+ (gpointer *) &original_key,
+ NULL));
+ g_assert_null (original_key);
+
+ g_assert_false (g_hash_table_lookup_extended (hash, "not a key", NULL, NULL));
+
+ g_hash_table_unref (hash);
+}
+
struct _GHashTable
{
- gint size;
+ gsize size;
gint mod;
guint mask;
gint nnodes;
gint noccupied; /* nnodes + tombstones */
+ guint have_big_keys : 1;
+ guint have_big_values : 1;
+
gpointer *keys;
guint *hashes;
gpointer *values;
GHashFunc hash_func;
GEqualFunc key_equal_func;
- volatile gint ref_count;
+ gint ref_count; /* (atomic) */
#ifndef G_DISABLE_ASSERT
int version;
static void
count_keys (GHashTable *h, gint *unused, gint *occupied, gint *tombstones)
{
- gint i;
+ gsize i;
*unused = 0;
*occupied = 0;
}
}
+#define BIG_ENTRY_SIZE (SIZEOF_VOID_P)
+#define SMALL_ENTRY_SIZE (SIZEOF_INT)
+
+#if SMALL_ENTRY_SIZE < BIG_ENTRY_SIZE
+# define USE_SMALL_ARRAYS
+#endif
+
+static gpointer
+fetch_key_or_value (gpointer a, guint index, gboolean is_big)
+{
+#ifdef USE_SMALL_ARRAYS
+ return is_big ? *(((gpointer *) a) + index) : GUINT_TO_POINTER (*(((guint *) a) + index));
+#else
+ return *(((gpointer *) a) + index);
+#endif
+}
+
static void
check_data (GHashTable *h)
{
- gint i;
+ gsize i;
for (i = 0; i < h->size; i++)
{
- if (h->hashes[i] < 2)
- {
- g_assert (h->keys[i] == NULL);
- g_assert (h->values[i] == NULL);
- }
- else
+ if (h->hashes[i] >= 2)
{
- g_assert_cmpint (h->hashes[i], ==, h->hash_func (h->keys[i]));
+ g_assert_cmpint (h->hashes[i], ==, h->hash_func (fetch_key_or_value (h->keys, i, h->have_big_keys)));
}
}
}
g_test_add_func ("/hash/find", test_find);
g_test_add_func ("/hash/foreach", test_foreach);
g_test_add_func ("/hash/foreach-steal", test_foreach_steal);
+ g_test_add_func ("/hash/steal-extended", test_steal_extended);
+ g_test_add_func ("/hash/steal-extended/optional", test_steal_extended_optional);
+ g_test_add_func ("/hash/lookup-extended", test_lookup_extended);
/* tests for individual bugs */
g_test_add_func ("/hash/lookup-null-key", test_lookup_null_key);