Files
glib/glib/tests/hash.c
Hans Petter Jansson eed0f182fd tests: Remove assertion that unused buckets should have NULL key/value
We still clear the key/value on removal, but since we're growing the
arrays with realloc() now, we can't guarantee that incoming memory is
cleared. There's no reason it should be either, since we check the
hashes array (which is always in a defined state) before accessing the
other arrays.
2018-10-03 22:14:38 +02:00

1703 lines
41 KiB
C

/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
* Copyright (C) 1999 The Free Software Foundation
*
* 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.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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
#undef G_DISABLE_ASSERT
#undef G_LOG_DOMAIN
#include <config.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <glib.h>
int array[10000];
static void
fill_hash_table_and_array (GHashTable *hash_table)
{
int i;
for (i = 0; i < 10000; i++)
{
array[i] = i;
g_hash_table_insert (hash_table, &array[i], &array[i]);
}
}
static void
init_result_array (int result_array[10000])
{
int i;
for (i = 0; i < 10000; i++)
result_array[i] = -1;
}
static void
verify_result_array (int array[10000])
{
int i;
for (i = 0; i < 10000; i++)
g_assert (array[i] == i);
}
static void
handle_pair (gpointer key, gpointer value, int result_array[10000])
{
int n;
g_assert (key == value);
n = *((int *) value);
g_assert (n >= 0 && n < 10000);
g_assert (result_array[n] == -1);
result_array[n] = n;
}
static gboolean
my_hash_callback_remove (gpointer key,
gpointer value,
gpointer user_data)
{
int *d = value;
if ((*d) % 2)
return TRUE;
return FALSE;
}
static void
my_hash_callback_remove_test (gpointer key,
gpointer value,
gpointer user_data)
{
int *d = value;
if ((*d) % 2)
g_assert_not_reached ();
}
static void
my_hash_callback (gpointer key,
gpointer value,
gpointer user_data)
{
handle_pair (key, value, user_data);
}
static guint
my_hash (gconstpointer key)
{
return (guint) *((const gint*) key);
}
static gboolean
my_hash_equal (gconstpointer a,
gconstpointer b)
{
return *((const gint*) a) == *((const gint*) b);
}
/*
* This is a simplified version of the pathalias hashing function.
* Thanks to Steve Belovin and Peter Honeyman
*
* hash a string into a long int. 31 bit crc (from andrew appel).
* the crc table is computed at run time by crcinit() -- we could
* precompute, but it takes 1 clock tick on a 750.
*
* This fast table calculation works only if POLY is a prime polynomial
* in the field of integers modulo 2. Since the coefficients of a
* 32-bit polynomial won't fit in a 32-bit word, the high-order bit is
* implicit. IT MUST ALSO BE THE CASE that the coefficients of orders
* 31 down to 25 are zero. Happily, we have candidates, from
* E. J. Watson, "Primitive Polynomials (Mod 2)", Math. Comp. 16 (1962):
* x^32 + x^7 + x^5 + x^3 + x^2 + x^1 + x^0
* x^31 + x^3 + x^0
*
* We reverse the bits to get:
* 111101010000000000000000000000001 but drop the last 1
* f 5 0 0 0 0 0 0
* 010010000000000000000000000000001 ditto, for 31-bit crc
* 4 8 0 0 0 0 0 0
*/
#define POLY 0x48000000L /* 31-bit polynomial (avoids sign problems) */
static guint CrcTable[128];
/*
- crcinit - initialize tables for hash function
*/
static void crcinit(void)
{
int i, j;
guint sum;
for (i = 0; i < 128; ++i)
{
sum = 0L;
for (j = 7 - 1; j >= 0; --j)
if (i & (1 << j))
sum ^= POLY >> j;
CrcTable[i] = sum;
}
}
/*
- hash - Honeyman's nice hashing function
*/
static guint
honeyman_hash (gconstpointer key)
{
const gchar *name = (const gchar *) key;
gint size;
guint sum = 0;
g_assert (name != NULL);
g_assert (*name != 0);
size = strlen (name);
while (size--)
sum = (sum >> 7) ^ CrcTable[(sum ^ (*name++)) & 0x7f];
return sum;
}
static gboolean
second_hash_cmp (gconstpointer a, gconstpointer b)
{
return strcmp (a, b) == 0;
}
static guint
one_hash (gconstpointer key)
{
return 1;
}
static void
not_even_foreach (gpointer key,
gpointer value,
gpointer user_data)
{
const char *_key = (const char *) key;
const char *_value = (const char *) value;
int i;
char val [20];
g_assert (_key != NULL);
g_assert (*_key != 0);
g_assert (_value != NULL);
g_assert (*_value != 0);
i = atoi (_key);
sprintf (val, "%d value", i);
g_assert (strcmp (_value, val) == 0);
g_assert ((i % 2) != 0);
g_assert (i != 3);
}
static gboolean
remove_even_foreach (gpointer key,
gpointer value,
gpointer user_data)
{
const char *_key = (const char *) key;
const char *_value = (const char *) value;
int i;
char val [20];
g_assert (_key != NULL);
g_assert (*_key != 0);
g_assert (_value != NULL);
g_assert (*_value != 0);
i = atoi (_key);
sprintf (val, "%d value", i);
g_assert (strcmp (_value, val) == 0);
return ((i % 2) == 0) ? TRUE : FALSE;
}
static void
second_hash_test (gconstpointer d)
{
gboolean simple_hash = GPOINTER_TO_INT (d);
int i;
char key[20] = "", val[20]="", *v, *orig_key, *orig_val;
GHashTable *h;
gboolean found;
crcinit ();
h = g_hash_table_new_full (simple_hash ? one_hash : honeyman_hash,
second_hash_cmp,
g_free, g_free);
g_assert (h != NULL);
for (i = 0; i < 20; i++)
{
sprintf (key, "%d", i);
g_assert (atoi (key) == i);
sprintf (val, "%d value", i);
g_assert (atoi (val) == i);
g_hash_table_insert (h, g_strdup (key), g_strdup (val));
}
g_assert (g_hash_table_size (h) == 20);
for (i = 0; i < 20; i++)
{
sprintf (key, "%d", i);
g_assert (atoi(key) == i);
v = (char *) g_hash_table_lookup (h, key);
g_assert (v != NULL);
g_assert (*v != 0);
g_assert (atoi (v) == i);
}
sprintf (key, "%d", 3);
g_hash_table_remove (h, key);
g_assert (g_hash_table_size (h) == 19);
g_hash_table_foreach_remove (h, remove_even_foreach, NULL);
g_assert (g_hash_table_size (h) == 9);
g_hash_table_foreach (h, not_even_foreach, NULL);
for (i = 0; i < 20; i++)
{
sprintf (key, "%d", i);
g_assert (atoi(key) == i);
sprintf (val, "%d value", i);
g_assert (atoi (val) == i);
orig_key = orig_val = NULL;
found = g_hash_table_lookup_extended (h, key,
(gpointer)&orig_key,
(gpointer)&orig_val);
if ((i % 2) == 0 || i == 3)
{
g_assert (!found);
continue;
}
g_assert (found);
g_assert (orig_key != NULL);
g_assert (strcmp (key, orig_key) == 0);
g_assert (orig_val != NULL);
g_assert (strcmp (val, orig_val) == 0);
}
g_hash_table_destroy (h);
}
static gboolean
find_first (gpointer key,
gpointer value,
gpointer user_data)
{
gint *v = value;
gint *test = user_data;
return (*v == *test);
}
static void
direct_hash_test (void)
{
gint i, rc;
GHashTable *h;
h = g_hash_table_new (NULL, NULL);
g_assert (h != NULL);
for (i = 1; i <= 20; i++)
g_hash_table_insert (h, GINT_TO_POINTER (i),
GINT_TO_POINTER (i + 42));
g_assert (g_hash_table_size (h) == 20);
for (i = 1; i <= 20; i++)
{
rc = GPOINTER_TO_INT (g_hash_table_lookup (h, GINT_TO_POINTER (i)));
g_assert (rc != 0);
g_assert ((rc - 42) == i);
}
g_hash_table_destroy (h);
}
static void
direct_hash_test2 (void)
{
gint i, rc;
GHashTable *h;
h = g_hash_table_new (g_direct_hash, g_direct_equal);
g_assert (h != NULL);
for (i = 1; i <= 20; i++)
g_hash_table_insert (h, GINT_TO_POINTER (i),
GINT_TO_POINTER (i + 42));
g_assert (g_hash_table_size (h) == 20);
for (i = 1; i <= 20; i++)
{
rc = GPOINTER_TO_INT (g_hash_table_lookup (h, GINT_TO_POINTER (i)));
g_assert (rc != 0);
g_assert ((rc - 42) == i);
}
g_hash_table_destroy (h);
}
static void
int_hash_test (void)
{
gint i, rc;
GHashTable *h;
gint values[20];
gint key;
h = g_hash_table_new (g_int_hash, g_int_equal);
g_assert (h != NULL);
for (i = 0; i < 20; i++)
{
values[i] = i + 42;
g_hash_table_insert (h, &values[i], GINT_TO_POINTER (i + 42));
}
g_assert (g_hash_table_size (h) == 20);
for (i = 0; i < 20; i++)
{
key = i + 42;
rc = GPOINTER_TO_INT (g_hash_table_lookup (h, &key));
g_assert_cmpint (rc, ==, i + 42);
}
g_hash_table_destroy (h);
}
static void
int64_hash_test (void)
{
gint i, rc;
GHashTable *h;
gint64 values[20];
gint64 key;
h = g_hash_table_new (g_int64_hash, g_int64_equal);
g_assert (h != NULL);
for (i = 0; i < 20; i++)
{
values[i] = i + 42;
g_hash_table_insert (h, &values[i], GINT_TO_POINTER (i + 42));
}
g_assert (g_hash_table_size (h) == 20);
for (i = 0; i < 20; i++)
{
key = i + 42;
rc = GPOINTER_TO_INT (g_hash_table_lookup (h, &key));
g_assert_cmpint (rc, ==, i + 42);
}
g_hash_table_destroy (h);
}
static void
double_hash_test (void)
{
gint i, rc;
GHashTable *h;
gdouble values[20];
gdouble key;
h = g_hash_table_new (g_double_hash, g_double_equal);
g_assert (h != NULL);
for (i = 0; i < 20; i++)
{
values[i] = i + 42.5;
g_hash_table_insert (h, &values[i], GINT_TO_POINTER (i + 42));
}
g_assert (g_hash_table_size (h) == 20);
for (i = 0; i < 20; i++)
{
key = i + 42.5;
rc = GPOINTER_TO_INT (g_hash_table_lookup (h, &key));
g_assert_cmpint (rc, ==, i + 42);
}
g_hash_table_destroy (h);
}
static void
string_free (gpointer data)
{
GString *s = data;
g_string_free (s, TRUE);
}
static void
string_hash_test (void)
{
gint i, rc;
GHashTable *h;
GString *s;
h = g_hash_table_new_full ((GHashFunc)g_string_hash, (GEqualFunc)g_string_equal, string_free, NULL);
g_assert (h != NULL);
for (i = 0; i < 20; i++)
{
s = g_string_new ("");
g_string_append_printf (s, "%d", i + 42);
g_string_append_c (s, '.');
g_string_prepend_unichar (s, 0x2301);
g_hash_table_insert (h, s, GINT_TO_POINTER (i + 42));
}
g_assert (g_hash_table_size (h) == 20);
s = g_string_new ("");
for (i = 0; i < 20; i++)
{
g_string_assign (s, "");
g_string_append_printf (s, "%d", i + 42);
g_string_append_c (s, '.');
g_string_prepend_unichar (s, 0x2301);
rc = GPOINTER_TO_INT (g_hash_table_lookup (h, s));
g_assert_cmpint (rc, ==, i + 42);
}
g_string_free (s, TRUE);
g_hash_table_destroy (h);
}
static void
set_check (gpointer key,
gpointer value,
gpointer user_data)
{
int *pi = user_data;
if (key != value)
g_assert_not_reached ();
g_assert_cmpint (atoi (key) % 7, ==, 2);
(*pi)++;
}
static void
set_hash_test (void)
{
GHashTable *hash_table =
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
int i;
for (i = 2; i < 5000; i += 7)
{
char *s = g_strdup_printf ("%d", i);
g_assert (g_hash_table_add (hash_table, s));
}
g_assert (!g_hash_table_add (hash_table, g_strdup_printf ("%d", 2)));
i = 0;
g_hash_table_foreach (hash_table, set_check, &i);
g_assert_cmpint (i, ==, g_hash_table_size (hash_table));
g_assert (g_hash_table_contains (hash_table, "2"));
g_assert (g_hash_table_contains (hash_table, "9"));
g_assert (!g_hash_table_contains (hash_table, "a"));
/* this will cause the hash table to loose set nature */
g_assert (g_hash_table_insert (hash_table, g_strdup ("a"), "b"));
g_assert (!g_hash_table_insert (hash_table, g_strdup ("a"), "b"));
g_assert (g_hash_table_replace (hash_table, g_strdup ("c"), "d"));
g_assert (!g_hash_table_replace (hash_table, g_strdup ("c"), "d"));
g_assert_cmpstr (g_hash_table_lookup (hash_table, "2"), ==, "2");
g_assert_cmpstr (g_hash_table_lookup (hash_table, "a"), ==, "b");
g_hash_table_destroy (hash_table);
}
static void
test_hash_misc (void)
{
GHashTable *hash_table;
gint i;
gint value = 120;
gint *pvalue;
GList *keys, *values;
gint keys_len, values_len;
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);
pvalue = g_hash_table_find (hash_table, find_first, &value);
if (!pvalue || *pvalue != value)
g_assert_not_reached();
keys = g_hash_table_get_keys (hash_table);
if (!keys)
g_assert_not_reached ();
values = g_hash_table_get_values (hash_table);
if (!values)
g_assert_not_reached ();
keys_len = g_list_length (keys);
values_len = g_list_length (values);
if (values_len != keys_len && keys_len != g_hash_table_size (hash_table))
g_assert_not_reached ();
g_list_free (keys);
g_list_free (values);
init_result_array (result_array);
g_hash_table_iter_init (&iter, hash_table);
for (i = 0; i < 10000; i++)
{
g_assert (g_hash_table_iter_next (&iter, &ikey, &ivalue));
handle_pair (ikey, ivalue, result_array);
if (i % 2)
g_hash_table_iter_remove (&iter);
}
g_assert (! g_hash_table_iter_next (&iter, &ikey, &ivalue));
g_assert (g_hash_table_size (hash_table) == 5000);
verify_result_array (result_array);
fill_hash_table_and_array (hash_table);
init_result_array (result_array);
g_hash_table_foreach (hash_table, my_hash_callback, result_array);
verify_result_array (result_array);
for (i = 0; i < 10000; i++)
g_hash_table_remove (hash_table, &array[i]);
fill_hash_table_and_array (hash_table);
if (g_hash_table_foreach_remove (hash_table, my_hash_callback_remove, NULL) != 5000 ||
g_hash_table_size (hash_table) != 5000)
g_assert_not_reached();
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;
static void
value_destroy (gpointer value)
{
destroy_counter++;
}
static void
test_hash_ref (void)
{
GHashTable *h;
GHashTableIter iter;
gchar *key, *value;
gboolean abc_seen = FALSE;
gboolean cde_seen = FALSE;
gboolean xyz_seen = FALSE;
h = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, value_destroy);
g_hash_table_insert (h, "abc", "ABC");
g_hash_table_insert (h, "cde", "CDE");
g_hash_table_insert (h, "xyz", "XYZ");
g_assert_cmpint (g_hash_table_size (h), == , 3);
g_hash_table_iter_init (&iter, h);
while (g_hash_table_iter_next (&iter, (gpointer*)&key, (gpointer*)&value))
{
if (strcmp (key, "abc") == 0)
{
g_assert_cmpstr (value, ==, "ABC");
abc_seen = TRUE;
g_hash_table_iter_steal (&iter);
}
else if (strcmp (key, "cde") == 0)
{
g_assert_cmpstr (value, ==, "CDE");
cde_seen = TRUE;
}
else if (strcmp (key, "xyz") == 0)
{
g_assert_cmpstr (value, ==, "XYZ");
xyz_seen = TRUE;
}
}
g_assert_cmpint (destroy_counter, ==, 0);
g_assert (g_hash_table_iter_get_hash_table (&iter) == h);
g_assert (abc_seen && cde_seen && xyz_seen);
g_assert_cmpint (g_hash_table_size (h), == , 2);
g_hash_table_ref (h);
g_hash_table_destroy (h);
g_assert_cmpint (g_hash_table_size (h), == , 0);
g_assert_cmpint (destroy_counter, ==, 2);
g_hash_table_insert (h, "uvw", "UVW");
g_hash_table_unref (h);
g_assert_cmpint (destroy_counter, ==, 3);
}
static guint
null_safe_str_hash (gconstpointer key)
{
if (key == NULL)
return 0;
else
return g_str_hash (key);
}
static gboolean
null_safe_str_equal (gconstpointer a, gconstpointer b)
{
return g_strcmp0 (a, b) == 0;
}
static void
test_lookup_null_key (void)
{
GHashTable *h;
gboolean res;
gpointer key;
gpointer value;
g_test_bug ("642944");
h = g_hash_table_new (null_safe_str_hash, null_safe_str_equal);
g_hash_table_insert (h, "abc", "ABC");
res = g_hash_table_lookup_extended (h, NULL, &key, &value);
g_assert (!res);
g_hash_table_insert (h, NULL, "NULL");
res = g_hash_table_lookup_extended (h, NULL, &key, &value);
g_assert (res);
g_assert_cmpstr (value, ==, "NULL");
g_hash_table_unref (h);
}
static gint destroy_key_counter;
static void
key_destroy (gpointer key)
{
destroy_key_counter++;
}
static void
test_remove_all (void)
{
GHashTable *h;
gboolean res;
h = g_hash_table_new_full (g_str_hash, g_str_equal, key_destroy, value_destroy);
g_hash_table_insert (h, "abc", "cde");
g_hash_table_insert (h, "cde", "xyz");
g_hash_table_insert (h, "xyz", "abc");
destroy_counter = 0;
destroy_key_counter = 0;
g_hash_table_steal_all (h);
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");
res = g_hash_table_steal (h, "nosuchkey");
g_assert (!res);
g_assert_cmpint (destroy_counter, ==, 0);
g_assert_cmpint (destroy_key_counter, ==, 0);
res = g_hash_table_steal (h, "xyz");
g_assert (res);
g_assert_cmpint (destroy_counter, ==, 0);
g_assert_cmpint (destroy_key_counter, ==, 0);
g_hash_table_remove_all (h);
g_assert_cmpint (destroy_counter, ==, 2);
g_assert_cmpint (destroy_key_counter, ==, 2);
g_hash_table_remove_all (h);
g_assert_cmpint (destroy_counter, ==, 2);
g_assert_cmpint (destroy_key_counter, ==, 2);
g_hash_table_unref (h);
}
GHashTable *recursive_destruction_table = NULL;
static void
recursive_value_destroy (gpointer value)
{
destroy_counter++;
if (recursive_destruction_table)
g_hash_table_remove (recursive_destruction_table, value);
}
static void
test_recursive_remove_all_subprocess (void)
{
GHashTable *h;
h = g_hash_table_new_full (g_str_hash, g_str_equal, key_destroy, recursive_value_destroy);
recursive_destruction_table = h;
/* Add more items compared to test_remove_all, as it would not fail otherwise. */
g_hash_table_insert (h, "abc", "cde");
g_hash_table_insert (h, "cde", "fgh");
g_hash_table_insert (h, "fgh", "ijk");
g_hash_table_insert (h, "ijk", "lmn");
g_hash_table_insert (h, "lmn", "opq");
g_hash_table_insert (h, "opq", "rst");
g_hash_table_insert (h, "rst", "uvw");
g_hash_table_insert (h, "uvw", "xyz");
g_hash_table_insert (h, "xyz", "abc");
destroy_counter = 0;
destroy_key_counter = 0;
g_hash_table_remove_all (h);
g_assert_cmpint (destroy_counter, ==, 9);
g_assert_cmpint (destroy_key_counter, ==, 9);
g_hash_table_unref (h);
}
static void
test_recursive_remove_all (void)
{
g_test_trap_subprocess ("/hash/recursive-remove-all/subprocess", 1000000, 0);
g_test_trap_assert_passed ();
}
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);
}
GHashTable *h;
typedef struct {
gchar *string;
gboolean freed;
} FakeFreeData;
GPtrArray *fake_free_data;
static void
fake_free (gpointer dead)
{
guint i;
for (i = 0; i < fake_free_data->len; i++)
{
FakeFreeData *ffd = g_ptr_array_index (fake_free_data, i);
if (ffd->string == (gchar *) dead)
{
g_assert (!ffd->freed);
ffd->freed = TRUE;
return;
}
}
g_assert_not_reached ();
}
static void
value_destroy_insert (gpointer value)
{
g_hash_table_remove_all (h);
}
static void
test_destroy_modify (void)
{
FakeFreeData *ffd;
guint i;
g_test_bug ("650459");
fake_free_data = g_ptr_array_new ();
h = g_hash_table_new_full (g_str_hash, g_str_equal, fake_free, value_destroy_insert);
ffd = g_new0 (FakeFreeData, 1);
ffd->string = g_strdup ("a");
g_ptr_array_add (fake_free_data, ffd);
g_hash_table_insert (h, ffd->string, "b");
ffd = g_new0 (FakeFreeData, 1);
ffd->string = g_strdup ("c");
g_ptr_array_add (fake_free_data, ffd);
g_hash_table_insert (h, ffd->string, "d");
ffd = g_new0 (FakeFreeData, 1);
ffd->string = g_strdup ("e");
g_ptr_array_add (fake_free_data, ffd);
g_hash_table_insert (h, ffd->string, "f");
ffd = g_new0 (FakeFreeData, 1);
ffd->string = g_strdup ("g");
g_ptr_array_add (fake_free_data, ffd);
g_hash_table_insert (h, ffd->string, "h");
ffd = g_new0 (FakeFreeData, 1);
ffd->string = g_strdup ("h");
g_ptr_array_add (fake_free_data, ffd);
g_hash_table_insert (h, ffd->string, "k");
ffd = g_new0 (FakeFreeData, 1);
ffd->string = g_strdup ("a");
g_ptr_array_add (fake_free_data, ffd);
g_hash_table_insert (h, ffd->string, "c");
g_hash_table_remove (h, "c");
/* that removed everything... */
for (i = 0; i < fake_free_data->len; i++)
{
FakeFreeData *ffd = g_ptr_array_index (fake_free_data, i);
g_assert (ffd->freed);
g_free (ffd->string);
g_free (ffd);
}
g_ptr_array_unref (fake_free_data);
/* ... so this is a no-op */
g_hash_table_remove (h, "e");
g_hash_table_unref (h);
}
static gboolean
find_str (gpointer key, gpointer value, gpointer data)
{
return g_str_equal (key, data);
}
static void
test_find (void)
{
GHashTable *hash;
const gchar *value;
hash = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (hash, "a", "A");
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");
value = g_hash_table_find (hash, find_str, "a");
g_assert_cmpstr (value, ==, "A");
value = g_hash_table_find (hash, find_str, "b");
g_assert_cmpstr (value, ==, "B");
value = g_hash_table_find (hash, find_str, "c");
g_assert_cmpstr (value, ==, "C");
value = g_hash_table_find (hash, find_str, "d");
g_assert_cmpstr (value, ==, "D");
value = g_hash_table_find (hash, find_str, "e");
g_assert_cmpstr (value, ==, "E");
value = g_hash_table_find (hash, find_str, "f");
g_assert_cmpstr (value, ==, "F");
value = g_hash_table_find (hash, find_str, "0");
g_assert (value == NULL);
g_hash_table_unref (hash);
}
gboolean seen_key[6];
static void
foreach_func (gpointer key, gpointer value, gpointer data)
{
seen_key[((char*)key)[0] - 'a'] = TRUE;
}
static void
test_foreach (void)
{
GHashTable *hash;
gint i;
hash = g_hash_table_new (g_str_hash, g_str_equal);
g_hash_table_insert (hash, "a", "A");
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");
for (i = 0; i < 6; i++)
seen_key[i] = FALSE;
g_hash_table_foreach (hash, foreach_func, NULL);
for (i = 0; i < 6; i++)
g_assert (seen_key[i]);
g_hash_table_unref (hash);
}
static gboolean
foreach_steal_func (gpointer key, gpointer value, gpointer data)
{
GHashTable *hash2 = data;
if (strstr ("ace", (gchar*)key))
{
g_hash_table_insert (hash2, key, value);
return TRUE;
}
return FALSE;
}
static void
test_foreach_steal (void)
{
GHashTable *hash;
GHashTable *hash2;
hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
hash2 = 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_hash_table_foreach_steal (hash, foreach_steal_func, hash2);
g_assert_cmpint (g_hash_table_size (hash), ==, 3);
g_assert_cmpint (g_hash_table_size (hash2), ==, 3);
g_assert_cmpstr (g_hash_table_lookup (hash2, "a"), ==, "A");
g_assert_cmpstr (g_hash_table_lookup (hash, "b"), ==, "B");
g_assert_cmpstr (g_hash_table_lookup (hash2, "c"), ==, "C");
g_assert_cmpstr (g_hash_table_lookup (hash, "d"), ==, "D");
g_assert_cmpstr (g_hash_table_lookup (hash2, "e"), ==, "E");
g_assert_cmpstr (g_hash_table_lookup (hash, "f"), ==, "F");
g_hash_table_unref (hash);
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;
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;
#ifndef G_DISABLE_ASSERT
int version;
#endif
GDestroyNotify key_destroy_func;
GDestroyNotify value_destroy_func;
};
static void
count_keys (GHashTable *h, gint *unused, gint *occupied, gint *tombstones)
{
gint i;
*unused = 0;
*occupied = 0;
*tombstones = 0;
for (i = 0; i < h->size; i++)
{
if (h->hashes[i] == 0)
(*unused)++;
else if (h->hashes[i] == 1)
(*tombstones)++;
else
(*occupied)++;
}
}
#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;
for (i = 0; i < h->size; i++)
{
if (h->hashes[i] >= 2)
{
g_assert_cmpint (h->hashes[i], ==, h->hash_func (fetch_key_or_value (h->keys, i, h->have_big_keys)));
}
}
}
static void
check_consistency (GHashTable *h)
{
gint unused;
gint occupied;
gint tombstones;
count_keys (h, &unused, &occupied, &tombstones);
g_assert_cmpint (occupied, ==, h->nnodes);
g_assert_cmpint (occupied + tombstones, ==, h->noccupied);
g_assert_cmpint (occupied + tombstones + unused, ==, h->size);
check_data (h);
}
static void
check_counts (GHashTable *h, gint occupied, gint tombstones)
{
g_assert_cmpint (occupied, ==, h->nnodes);
g_assert_cmpint (occupied + tombstones, ==, h->noccupied);
}
static void
trivial_key_destroy (gpointer key)
{
}
static void
test_internal_consistency (void)
{
GHashTable *h;
h = g_hash_table_new_full (g_str_hash, g_str_equal, trivial_key_destroy, NULL);
check_counts (h, 0, 0);
check_consistency (h);
g_hash_table_insert (h, "a", "A");
g_hash_table_insert (h, "b", "B");
g_hash_table_insert (h, "c", "C");
g_hash_table_insert (h, "d", "D");
g_hash_table_insert (h, "e", "E");
g_hash_table_insert (h, "f", "F");
check_counts (h, 6, 0);
check_consistency (h);
g_hash_table_remove (h, "a");
check_counts (h, 5, 1);
check_consistency (h);
g_hash_table_remove (h, "b");
check_counts (h, 4, 2);
check_consistency (h);
g_hash_table_insert (h, "c", "c");
check_counts (h, 4, 2);
check_consistency (h);
g_hash_table_insert (h, "a", "A");
check_counts (h, 5, 1);
check_consistency (h);
g_hash_table_remove_all (h);
check_counts (h, 0, 0);
check_consistency (h);
g_hash_table_unref (h);
}
static void
my_key_free (gpointer v)
{
gchar *s = v;
g_assert (s[0] != 'x');
s[0] = 'x';
g_free (v);
}
static void
my_value_free (gpointer v)
{
gchar *s = v;
g_assert (s[0] != 'y');
s[0] = 'y';
g_free (v);
}
static void
test_iter_replace (void)
{
GHashTable *h;
GHashTableIter iter;
gpointer k, v;
gchar *s;
g_test_bug ("662544");
h = g_hash_table_new_full (g_str_hash, g_str_equal, my_key_free, my_value_free);
g_hash_table_insert (h, g_strdup ("A"), g_strdup ("a"));
g_hash_table_insert (h, g_strdup ("B"), g_strdup ("b"));
g_hash_table_insert (h, g_strdup ("C"), g_strdup ("c"));
g_hash_table_iter_init (&iter, h);
while (g_hash_table_iter_next (&iter, &k, &v))
{
s = (gchar*)v;
g_assert (g_ascii_islower (s[0]));
g_hash_table_iter_replace (&iter, g_strdup (k));
}
g_hash_table_unref (h);
}
static void
replace_first_character (gchar *string)
{
string[0] = 'b';
}
static void
test_set_insert_corruption (void)
{
GHashTable *hash_table =
g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) replace_first_character, NULL);
GHashTableIter iter;
gchar a[] = "foo";
gchar b[] = "foo";
gpointer key, value;
g_test_bug ("692815");
g_hash_table_insert (hash_table, a, a);
g_assert (g_hash_table_contains (hash_table, "foo"));
g_hash_table_insert (hash_table, b, b);
g_assert_cmpuint (g_hash_table_size (hash_table), ==, 1);
g_hash_table_iter_init (&iter, hash_table);
if (!g_hash_table_iter_next (&iter, &key, &value))
g_assert_not_reached();
/* per the documentation to g_hash_table_insert(), 'b' has now been freed,
* and the sole key in 'hash_table' should be 'a'.
*/
g_assert (key != b);
g_assert (key == a);
g_assert_cmpstr (b, ==, "boo");
/* g_hash_table_insert() also says that the value should now be 'b',
* which is probably not what the caller intended but is precisely what they
* asked for.
*/
g_assert (value == b);
/* even though the hash has now been de-set-ified: */
g_assert (g_hash_table_contains (hash_table, "foo"));
g_hash_table_unref (hash_table);
}
static void
test_set_to_strv (void)
{
GHashTable *set;
gchar **strv;
guint n;
set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
g_hash_table_add (set, g_strdup ("xyz"));
g_hash_table_add (set, g_strdup ("xyz"));
g_hash_table_add (set, g_strdup ("abc"));
strv = (gchar **) g_hash_table_get_keys_as_array (set, &n);
g_hash_table_steal_all (set);
g_hash_table_unref (set);
g_assert_cmpint (n, ==, 2);
n = g_strv_length (strv);
g_assert_cmpint (n, ==, 2);
if (g_str_equal (strv[0], "abc"))
g_assert_cmpstr (strv[1], ==, "xyz");
else
{
g_assert_cmpstr (strv[0], ==, "xyz");
g_assert_cmpstr (strv[1], ==, "abc");
}
g_strfreev (strv);
}
static gboolean
is_prime (guint p)
{
guint i;
if (p % 2 == 0)
return FALSE;
i = 3;
while (TRUE)
{
if (i * i > p)
return TRUE;
if (p % i == 0)
return FALSE;
i += 2;
}
}
static void
test_primes (void)
{
guint p, q;
gdouble r, min, max;
max = 1.0;
min = 10.0;
q = 1;
while (1) {
p = q;
q = g_spaced_primes_closest (p);
g_assert (is_prime (q));
if (p == 1) continue;
if (q == p) break;
r = q / (gdouble) p;
min = MIN (min, r);
max = MAX (max, r);
};
g_assert_cmpfloat (1.3, <, min);
g_assert_cmpfloat (max, <, 2.0);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_bug_base ("http://bugzilla.gnome.org/");
g_test_add_func ("/hash/misc", test_hash_misc);
g_test_add_data_func ("/hash/one", GINT_TO_POINTER (TRUE), second_hash_test);
g_test_add_data_func ("/hash/honeyman", GINT_TO_POINTER (FALSE), second_hash_test);
g_test_add_func ("/hash/direct", direct_hash_test);
g_test_add_func ("/hash/direct2", direct_hash_test2);
g_test_add_func ("/hash/int", int_hash_test);
g_test_add_func ("/hash/int64", int64_hash_test);
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);
g_test_add_func ("/hash/recursive-remove-all", test_recursive_remove_all);
g_test_add_func ("/hash/recursive-remove-all/subprocess", test_recursive_remove_all_subprocess);
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);
g_test_add_func ("/hash/destroy-modify", test_destroy_modify);
g_test_add_func ("/hash/consistency", test_internal_consistency);
g_test_add_func ("/hash/iter-replace", test_iter_replace);
g_test_add_func ("/hash/set-insert-corruption", test_set_insert_corruption);
g_test_add_func ("/hash/set-to-strv", test_set_to_strv);
g_test_add_func ("/hash/primes", test_primes);
return g_test_run ();
}