X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=glib%2Fghash.c;h=b42dd1423223e46ca51149995e19ed8a4f211d42;hb=ea4f9ce8a060d53cbc299e4c384089f6cc926caa;hp=417ed5d5e5102d56d85771bcb2d2088fc1273510;hpb=a4e38786750d538b334b8a7a7cc9f5a3ff48bc33;p=platform%2Fupstream%2Fglib.git diff --git a/glib/ghash.c b/glib/ghash.c index 417ed5d..b42dd14 100644 --- a/glib/ghash.c +++ b/glib/ghash.c @@ -12,9 +12,7 @@ * 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, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library; if not, see . */ /* @@ -32,14 +30,20 @@ #include /* memset */ -#include "glib.h" -#include "galias.h" +#include "ghash.h" + +#include "glib-private.h" +#include "gstrfuncs.h" +#include "gatomic.h" +#include "gtestutils.h" +#include "gslice.h" + /** - * SECTION: hash_tables + * SECTION:hash_tables * @title: Hash Tables * @short_description: associations between keys and values so that - * given a key the value can be found quickly + * given a key the value can be found quickly * * A #GHashTable provides associations between keys and values which is * optimized so that given a key, the associated value can be found @@ -66,6 +70,9 @@ * To lookup a value corresponding to a given key, use * g_hash_table_lookup() and g_hash_table_lookup_extended(). * + * g_hash_table_lookup_extended() can also be used to simply + * check if a key is present in the hash table. + * * To remove a key and value, use g_hash_table_remove(). * * To call a function for each key and value pair use @@ -73,72 +80,101 @@ * key/value pairs in the hash table, see #GHashTableIter. * * To destroy a #GHashTable use g_hash_table_destroy(). - **/ + * + * A common use-case for hash tables is to store information about a + * set of keys, without associating any particular value with each + * key. GHashTable optimizes one way of doing so: If you store only + * key-value pairs where key == value, then GHashTable does not + * allocate memory to store the values, which can be a considerable + * space saving, if your set is large. The functions + * g_hash_table_add() and g_hash_table_contains() are designed to be + * used when using #GHashTable this way. + */ /** * GHashTable: * * The #GHashTable struct is an opaque data structure to represent a - * Hash Table. It should only be - * accessed via the following functions. - **/ + * [Hash Table][glib-Hash-Tables]. It should only be accessed via the + * following functions. + */ /** * GHashFunc: - * @key: a key. - * @Returns: the hash value corresponding to the key. + * @key: a key * * Specifies the type of the hash function which is passed to * g_hash_table_new() when a #GHashTable is created. * * The function is passed a key and should return a #guint hash value. * The functions g_direct_hash(), g_int_hash() and g_str_hash() provide - * hash functions which can be used when the key is a #gpointer, #gint, + * hash functions which can be used when the key is a #gpointer, #gint*, * and #gchar* respectively. * - * The hash values should be evenly - * distributed over a fairly large range? The modulus is taken with the - * hash table size (a prime number) to find the 'bucket' to place each - * key into. The function should also be very fast, since it is called - * for each key lookup. - **/ + * g_direct_hash() is also the appropriate hash function for keys + * of the form `GINT_TO_POINTER (n)` (or similar macros). + * + * A good hash functions should produce + * hash values that are evenly distributed over a fairly large range. + * The modulus is taken with the hash table size (a prime number) to + * find the 'bucket' to place each key into. The function should also + * be very fast, since it is called for each key lookup. + * + * Note that the hash functions provided by GLib have these qualities, + * but are not particularly robust against manufactured keys that + * cause hash collisions. Therefore, you should consider choosing + * a more secure hash function when using a GHashTable with keys + * that originate in untrusted data (such as HTTP requests). + * Using g_str_hash() in that situation might make your application + * vulerable to + * [Algorithmic Complexity Attacks](https://lwn.net/Articles/474912/). + * + * The key to choosing a good hash is unpredictability. Even + * cryptographic hashes are very easy to find collisions for when the + * remainder is taken modulo a somewhat predictable prime number. There + * must be an element of randomness that an attacker is unable to guess. + * + * Returns: the hash value corresponding to the key + */ /** * GHFunc: - * @key: a key. - * @value: the value corresponding to the key. - * @user_data: user data passed to g_hash_table_foreach(). + * @key: a key + * @value: the value corresponding to the key + * @user_data: user data passed to g_hash_table_foreach() * * Specifies the type of the function passed to g_hash_table_foreach(). * It is called with each key/value pair, together with the @user_data * parameter which is passed to g_hash_table_foreach(). - **/ + */ /** * GHRFunc: - * @key: a key. - * @value: the value associated with the key. - * @user_data: user data passed to g_hash_table_remove(). - * @Returns: %TRUE if the key/value pair should be removed from the - * #GHashTable. + * @key: a key + * @value: the value associated with the key + * @user_data: user data passed to g_hash_table_remove() * * Specifies the type of the function passed to * g_hash_table_foreach_remove(). It is called with each key/value * pair, together with the @user_data parameter passed to * g_hash_table_foreach_remove(). It should return %TRUE if the * key/value pair should be removed from the #GHashTable. - **/ + * + * Returns: %TRUE if the key/value pair should be removed from the + * #GHashTable + */ /** * GEqualFunc: - * @a: a value. - * @b: a value to compare with. - * @Returns: %TRUE if @a = @b; %FALSE otherwise. + * @a: a value + * @b: a value to compare with * * Specifies the type of a function used to test two values for * equality. The function should return %TRUE if both values are equal * and %FALSE otherwise. - **/ + * + * Returns: %TRUE if @a = @b; %FALSE otherwise + */ /** * GHashTableIter: @@ -147,22 +183,31 @@ * to iterate over the elements of a #GHashTable. GHashTableIter * structures are typically allocated on the stack and then initialized * with g_hash_table_iter_init(). - **/ + */ -#define HASH_TABLE_MIN_SHIFT 3 /* 1 << 3 == 8 buckets */ +/** + * g_hash_table_freeze: + * @hash_table: a #GHashTable + * + * This function is deprecated and will be removed in the next major + * release of GLib. It does nothing. + */ -typedef struct _GHashNode GHashNode; +/** + * g_hash_table_thaw: + * @hash_table: a #GHashTable + * + * This function is deprecated and will be removed in the next major + * release of GLib. It does nothing. + */ -struct _GHashNode -{ - gpointer key; - gpointer value; +#define HASH_TABLE_MIN_SHIFT 3 /* 1 << 3 == 8 buckets */ - /* If key_hash == 0, node is not in use - * If key_hash == 1, node is a tombstone - * If key_hash >= 2, node contains data */ - guint key_hash; -}; +#define UNUSED_HASH_VALUE 0 +#define TOMBSTONE_HASH_VALUE 1 +#define HASH_IS_UNUSED(h_) ((h_) == UNUSED_HASH_VALUE) +#define HASH_IS_TOMBSTONE(h_) ((h_) == TOMBSTONE_HASH_VALUE) +#define HASH_IS_REAL(h_) ((h_) >= 2) struct _GHashTable { @@ -171,10 +216,14 @@ struct _GHashTable guint mask; gint nnodes; gint noccupied; /* nnodes + tombstones */ - GHashNode *nodes; + + gpointer *keys; + guint *hashes; + gpointer *values; + GHashFunc hash_func; GEqualFunc key_equal_func; - volatile gint ref_count; + gint ref_count; #ifndef G_DISABLE_ASSERT /* * Tracks the structure of the hash table, not its contents: is only @@ -197,10 +246,14 @@ typedef struct int version; } RealIter; +G_STATIC_ASSERT (sizeof (GHashTableIter) == sizeof (RealIter)); +G_STATIC_ASSERT (_g_alignof (GHashTableIter) >= _g_alignof (RealIter)); + /* Each table size has an associated prime modulo (the first prime * lower than the table size) used to find the initial bucket. Probing * then works modulo 2^n. The prime modulo is necessary to get a - * good distribution with poor hash functions. */ + * good distribution with poor hash functions. + */ static const gint prime_mod [] = { 1, /* For 1 << 0 */ @@ -281,73 +334,7 @@ g_hash_table_set_shift_from_size (GHashTable *hash_table, gint size) * g_hash_table_lookup_node: * @hash_table: our #GHashTable * @key: the key to lookup against - * @hash_return: optional key hash return location - * Return value: index of the described #GHashNode - * - * Performs a lookup in the hash table. Virtually all hash operations - * will use this function internally. - * - * This function first computes the hash value of the key using the - * user's hash function. - * - * If an entry in the table matching @key is found then this function - * returns the index of that entry in the table, and if not, the - * index of an empty node (never a tombstone). - */ -static inline guint -g_hash_table_lookup_node (GHashTable *hash_table, - gconstpointer key) -{ - GHashNode *node; - guint node_index; - guint hash_value; - guint step = 0; - - /* Empty buckets have hash_value set to 0, and for tombstones, it's 1. - * We need to make sure our hash value is not one of these. */ - - hash_value = (* hash_table->hash_func) (key); - if (G_UNLIKELY (hash_value <= 1)) - hash_value = 2; - - node_index = hash_value % hash_table->mod; - node = &hash_table->nodes [node_index]; - - while (node->key_hash) - { - /* We first check if our full hash values - * are equal so we can avoid calling the full-blown - * key equality function in most cases. - */ - - if (node->key_hash == hash_value) - { - if (hash_table->key_equal_func) - { - if (hash_table->key_equal_func (node->key, key)) - break; - } - else if (node->key == key) - { - break; - } - } - - step++; - node_index += step; - node_index &= hash_table->mask; - node = &hash_table->nodes [node_index]; - } - - return node_index; -} - -/* - * g_hash_table_lookup_node_for_insertion: - * @hash_table: our #GHashTable - * @key: the key to lookup against * @hash_return: key hash return location - * Return value: index of the described #GHashNode * * Performs a lookup in the hash table, preserving extra information * usually needed for insertion. @@ -363,51 +350,58 @@ g_hash_table_lookup_node (GHashTable *hash_table, * The computed hash value is returned in the variable pointed to * by @hash_return. This is to save insertions from having to compute * the hash record again for the new record. + * + * Returns: index of the described node */ static inline guint -g_hash_table_lookup_node_for_insertion (GHashTable *hash_table, - gconstpointer key, - guint *hash_return) +g_hash_table_lookup_node (GHashTable *hash_table, + gconstpointer key, + guint *hash_return) { - GHashNode *node; guint node_index; + guint node_hash; guint hash_value; - guint first_tombstone; + guint first_tombstone = 0; gboolean have_tombstone = FALSE; guint step = 0; - /* Empty buckets have hash_value set to 0, and for tombstones, it's 1. - * We need to make sure our hash value is not one of these. */ + /* 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_value <= 1)) + hash_value = hash_table->hash_func (key); + if (G_UNLIKELY (!HASH_IS_REAL (hash_value))) hash_value = 2; *hash_return = hash_value; node_index = hash_value % hash_table->mod; - node = &hash_table->nodes [node_index]; + node_hash = hash_table->hashes[node_index]; - while (node->key_hash) + while (!HASH_IS_UNUSED (node_hash)) { - /* We first check if our full hash values - * are equal so we can avoid calling the full-blown - * key equality function in most cases. + /* We first check if our full hash values + * are equal so we can avoid calling the full-blown + * key equality function in most cases. */ - - if (node->key_hash == hash_value) + if (node_hash == hash_value) { + gpointer node_key = hash_table->keys[node_index]; + if (hash_table->key_equal_func) { - if (hash_table->key_equal_func (node->key, key)) + if (hash_table->key_equal_func (node_key, key)) return node_index; } - else if (node->key == key) + else if (node_key == key) { return node_index; } } - else if (node->key_hash == 1 && !have_tombstone) + else if (HASH_IS_TOMBSTONE (node_hash) && !have_tombstone) { first_tombstone = node_index; have_tombstone = TRUE; @@ -416,7 +410,7 @@ g_hash_table_lookup_node_for_insertion (GHashTable *hash_table, step++; node_index += step; node_index &= hash_table->mask; - node = &hash_table->nodes [node_index]; + node_hash = hash_table->hashes[node_index]; } if (have_tombstone) @@ -439,23 +433,30 @@ g_hash_table_lookup_node_for_insertion (GHashTable *hash_table, */ static void g_hash_table_remove_node (GHashTable *hash_table, - GHashNode *node, + gint i, gboolean notify) { - if (notify && hash_table->key_destroy_func) - hash_table->key_destroy_func (node->key); + gpointer key; + gpointer value; - if (notify && hash_table->value_destroy_func) - hash_table->value_destroy_func (node->value); + key = hash_table->keys[i]; + value = hash_table->values[i]; /* Erect tombstone */ - node->key_hash = 1; + hash_table->hashes[i] = TOMBSTONE_HASH_VALUE; /* Be GC friendly */ - node->key = NULL; - node->value = NULL; + hash_table->keys[i] = NULL; + hash_table->values[i] = NULL; hash_table->nnodes--; + + if (notify && hash_table->key_destroy_func) + hash_table->key_destroy_func (key); + + if (notify && hash_table->value_destroy_func) + hash_table->value_destroy_func (value); + } /* @@ -471,30 +472,89 @@ g_hash_table_remove_node (GHashTable *hash_table, */ 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; - for (i = 0; i < hash_table->size; i++) + /* 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; + + if (!notify || + (hash_table->key_destroy_func == NULL && + hash_table->value_destroy_func == NULL)) { - GHashNode *node = &hash_table->nodes [i]; + 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)); + } - if (node->key_hash > 1) + return; + } + + /* 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) + { + 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])) { - if (notify && hash_table->key_destroy_func) - hash_table->key_destroy_func (node->key); + key = old_keys[i]; + value = old_values[i]; + + 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 (notify && hash_table->value_destroy_func) - hash_table->value_destroy_func (node->value); + if (hash_table->value_destroy_func != NULL) + hash_table->value_destroy_func (value); } } - /* We need to set node->key_hash = 0 for all nodes - might as well be GC - * friendly and clear everything */ - memset (hash_table->nodes, 0, hash_table->size * sizeof (GHashNode)); + /* Destroy old storage space. */ + if (old_keys != old_values) + g_free (old_values); - hash_table->nnodes = 0; - hash_table->noccupied = 0; + g_free (old_keys); + g_free (old_hashes); } /* @@ -502,9 +562,9 @@ g_hash_table_remove_all_nodes (GHashTable *hash_table, * @hash_table: our #GHashTable * * Resizes the hash table to the optimal size based on the number of - * nodes currently held. If you call this function then a resize will - * occur, even if one does not need to occur. Use - * g_hash_table_maybe_resize() instead. + * nodes currently held. If you call this function then a resize will + * occur, even if one does not need to occur. + * Use g_hash_table_maybe_resize() instead. * * This function may "resize" the hash table to its current size, with * the side effect of cleaning up tombstones and otherwise optimizing @@ -513,41 +573,55 @@ g_hash_table_remove_all_nodes (GHashTable *hash_table, static void g_hash_table_resize (GHashTable *hash_table) { - GHashNode *new_nodes; + gpointer *new_keys; + gpointer *new_values; + guint *new_hashes; gint old_size; gint i; old_size = hash_table->size; g_hash_table_set_shift_from_size (hash_table, hash_table->nnodes * 2); - new_nodes = g_new0 (GHashNode, hash_table->size); + new_keys = g_new0 (gpointer, hash_table->size); + if (hash_table->keys == hash_table->values) + new_values = new_keys; + else + new_values = g_new0 (gpointer, hash_table->size); + new_hashes = g_new0 (guint, hash_table->size); for (i = 0; i < old_size; i++) { - GHashNode *node = &hash_table->nodes [i]; - GHashNode *new_node; + guint node_hash = hash_table->hashes[i]; guint hash_val; guint step = 0; - if (node->key_hash <= 1) + if (!HASH_IS_REAL (node_hash)) continue; - hash_val = node->key_hash % hash_table->mod; - new_node = &new_nodes [hash_val]; + hash_val = node_hash % hash_table->mod; - while (new_node->key_hash) + while (!HASH_IS_UNUSED (new_hashes[hash_val])) { step++; hash_val += step; hash_val &= hash_table->mask; - new_node = &new_nodes [hash_val]; } - *new_node = *node; + new_hashes[hash_val] = hash_table->hashes[i]; + new_keys[hash_val] = hash_table->keys[i]; + new_values[hash_val] = hash_table->values[i]; } - g_free (hash_table->nodes); - hash_table->nodes = new_nodes; + if (hash_table->keys != hash_table->values) + g_free (hash_table->values); + + g_free (hash_table->keys); + g_free (hash_table->hashes); + + hash_table->keys = new_keys; + hash_table->values = new_values; + hash_table->hashes = new_hashes; + hash_table->noccupied = hash_table->nnodes; } @@ -573,26 +647,29 @@ g_hash_table_maybe_resize (GHashTable *hash_table) /** * g_hash_table_new: - * @hash_func: a function to create a hash value from a key. - * Hash values are used to determine where keys are stored within the - * #GHashTable data structure. The g_direct_hash(), g_int_hash(), - * g_int64_hash(), g_double_hash() and g_str_hash() functions are provided - * for some common types of keys. - * If hash_func is %NULL, g_direct_hash() is used. - * @key_equal_func: a function to check two keys for equality. This is - * used when looking up keys in the #GHashTable. The g_direct_equal(), - * g_int_equal(), g_int64_equal(), g_double_equal() and g_str_equal() - * functions are provided for the most common types of keys. - * If @key_equal_func is %NULL, keys are compared directly in a similar - * fashion to g_direct_equal(), but without the overhead of a function call. + * @hash_func: a function to create a hash value from a key + * @key_equal_func: a function to check two keys for equality * * Creates a new #GHashTable with a reference count of 1. * - * Return value: a new #GHashTable. - **/ -GHashTable* -g_hash_table_new (GHashFunc hash_func, - GEqualFunc key_equal_func) + * Hash values returned by @hash_func are used to determine where keys + * are stored within the #GHashTable data structure. The g_direct_hash(), + * g_int_hash(), g_int64_hash(), g_double_hash() and g_str_hash() + * functions are provided for some common types of keys. + * If @hash_func is %NULL, g_direct_hash() is used. + * + * @key_equal_func is used when looking up keys in the #GHashTable. + * The g_direct_equal(), g_int_equal(), g_int64_equal(), g_double_equal() + * and g_str_equal() functions are provided for the most common types + * of keys. If @key_equal_func is %NULL, keys are compared directly in + * a similar fashion to g_direct_equal(), but without the overhead of + * a function call. + * + * Returns: a new #GHashTable + */ +GHashTable * +g_hash_table_new (GHashFunc hash_func, + GEqualFunc key_equal_func) { return g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL); } @@ -600,26 +677,34 @@ g_hash_table_new (GHashFunc hash_func, /** * g_hash_table_new_full: - * @hash_func: a function to create a hash value from a key. - * @key_equal_func: a function to check two keys for equality. - * @key_destroy_func: a function to free the memory allocated for the key - * used when removing the entry from the #GHashTable or %NULL if you - * don't want to supply such a function. - * @value_destroy_func: a function to free the memory allocated for the - * value used when removing the entry from the #GHashTable or %NULL if - * you don't want to supply such a function. - * - * Creates a new #GHashTable like g_hash_table_new() with a reference count - * of 1 and allows to specify functions to free the memory allocated for the - * key and value that get called when removing the entry from the #GHashTable. - * - * Return value: a new #GHashTable. - **/ -GHashTable* -g_hash_table_new_full (GHashFunc hash_func, - GEqualFunc key_equal_func, - GDestroyNotify key_destroy_func, - GDestroyNotify value_destroy_func) + * @hash_func: a function to create a hash value from a key + * @key_equal_func: a function to check two keys for equality + * @key_destroy_func: (allow-none): a function to free the memory allocated for the key + * used when removing the entry from the #GHashTable, or %NULL + * if you don't want to supply such a function. + * @value_destroy_func: (allow-none): a function to free the memory allocated for the + * value used when removing the entry from the #GHashTable, or %NULL + * if you don't want to supply such a function. + * + * Creates a new #GHashTable like g_hash_table_new() with a reference + * count of 1 and allows to specify functions to free the memory + * allocated for the key and value that get called when removing the + * entry from the #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, + GEqualFunc key_equal_func, + GDestroyNotify key_destroy_func, + GDestroyNotify value_destroy_func) { GHashTable *hash_table; @@ -635,35 +720,37 @@ g_hash_table_new_full (GHashFunc hash_func, #endif hash_table->key_destroy_func = key_destroy_func; hash_table->value_destroy_func = value_destroy_func; - hash_table->nodes = g_new0 (GHashNode, hash_table->size); + hash_table->keys = g_new0 (gpointer, hash_table->size); + hash_table->values = hash_table->keys; + hash_table->hashes = g_new0 (guint, hash_table->size); return hash_table; } /** * g_hash_table_iter_init: - * @iter: an uninitialized #GHashTableIter. - * @hash_table: a #GHashTable. + * @iter: an uninitialized #GHashTableIter + * @hash_table: a #GHashTable * * Initializes a key/value pair iterator and associates it with * @hash_table. Modifying the hash table after calling this function * invalidates the returned iterator. - * |[ + * |[ * GHashTableIter iter; * gpointer key, value; * * g_hash_table_iter_init (&iter, hash_table); - * while (g_hash_table_iter_next (&iter, &key, &value)) + * while (g_hash_table_iter_next (&iter, &key, &value)) * { - * /* do something with key and value */ + * // do something with key and value * } * ]| * * Since: 2.16 - **/ + */ void g_hash_table_iter_init (GHashTableIter *iter, - GHashTable *hash_table) + GHashTable *hash_table) { RealIter *ri = (RealIter *) iter; @@ -679,25 +766,24 @@ g_hash_table_iter_init (GHashTableIter *iter, /** * g_hash_table_iter_next: - * @iter: an initialized #GHashTableIter. - * @key: a location to store the key, or %NULL. - * @value: a location to store the value, or %NULL. + * @iter: an initialized #GHashTableIter + * @key: (allow-none): a location to store the key, or %NULL + * @value: (allow-none): a location to store the value, or %NULL * * Advances @iter and retrieves the key and/or value that are now * 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 - **/ + */ gboolean g_hash_table_iter_next (GHashTableIter *iter, - gpointer *key, - gpointer *value) + gpointer *key, + gpointer *value) { RealIter *ri = (RealIter *) iter; - GHashNode *node; gint position; g_return_val_if_fail (iter != NULL, FALSE); @@ -716,15 +802,13 @@ g_hash_table_iter_next (GHashTableIter *iter, ri->position = position; return FALSE; } - - node = &ri->hash_table->nodes [position]; } - while (node->key_hash <= 1); + while (!HASH_IS_REAL (ri->hash_table->hashes[position])); if (key != NULL) - *key = node->key; + *key = ri->hash_table->keys[position]; if (value != NULL) - *value = node->value; + *value = ri->hash_table->values[position]; ri->position = position; return TRUE; @@ -732,14 +816,14 @@ g_hash_table_iter_next (GHashTableIter *iter, /** * g_hash_table_iter_get_hash_table: - * @iter: an initialized #GHashTableIter. + * @iter: an initialized #GHashTableIter * * Returns the #GHashTable associated with @iter. * - * Return value: the #GHashTable associated with @iter. + * Returns: the #GHashTable associated with @iter. * * Since: 2.16 - **/ + */ GHashTable * g_hash_table_iter_get_hash_table (GHashTableIter *iter) { @@ -758,7 +842,7 @@ iter_remove_or_steal (RealIter *ri, gboolean notify) g_return_if_fail (ri->position >= 0); g_return_if_fail (ri->position < ri->hash_table->size); - g_hash_table_remove_node (ri->hash_table, &ri->hash_table->nodes [ri->position], notify); + g_hash_table_remove_node (ri->hash_table, ri->position, notify); #ifndef G_DISABLE_ASSERT ri->version++; @@ -768,38 +852,202 @@ iter_remove_or_steal (RealIter *ri, gboolean notify) /** * g_hash_table_iter_remove: - * @iter: an initialized #GHashTableIter. + * @iter: an initialized #GHashTableIter * * Removes the key/value pair currently pointed to by the iterator * from its associated #GHashTable. Can only be called after - * g_hash_table_iter_next() returned %TRUE, and cannot be called more - * than once for the same key/value pair. + * g_hash_table_iter_next() returned %TRUE, and cannot be called + * more than once for the same key/value pair. * - * If the #GHashTable was created using g_hash_table_new_full(), the - * key and value are freed using the supplied destroy functions, otherwise - * you have to make sure that any dynamically allocated values are freed - * yourself. + * If the #GHashTable was created using g_hash_table_new_full(), + * the key and value are freed using the supplied destroy functions, + * otherwise you have to make sure that any dynamically allocated + * values are freed yourself. + * + * It is safe to continue iterating the #GHashTable afterward: + * |[ + * while (g_hash_table_iter_next (&iter, &key, &value)) + * { + * if (condition) + * g_hash_table_iter_remove (&iter); + * } + * ]| * * Since: 2.16 - **/ + */ void 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: (allow-none): key to replace with, or %NULL + * @value: value to replace with + * @keep_new_key: whether to replace the key in the node with @key + * @reusing_key: whether @key was taken out of the existing node + * + * Inserts a value at @node_index in the hash table and updates it. + * + * If @key has been taken out of the existing node (ie it is not + * passed in via a g_hash_table_insert/replace) call, then @reusing_key + * should be %TRUE. + * + * Returns: %TRUE if the key did not exist yet + */ +static gboolean +g_hash_table_insert_node (GHashTable *hash_table, + guint node_index, + guint key_hash, + gpointer new_key, + gpointer new_value, + gboolean keep_new_key, + gboolean reusing_key) +{ + gboolean already_exists; + guint old_hash; + gpointer key_to_free = NULL; + gpointer value_to_free = NULL; + + old_hash = hash_table->hashes[node_index]; + already_exists = HASH_IS_REAL (old_hash); + + /* Proceed in three steps. First, deal with the key because it is the + * most complicated. Then consider if we need to split the table in + * two (because writing the value will result in the set invariant + * becoming broken). Then deal with the value. + * + * There are three cases for the key: + * + * - entry already exists in table, reusing key: + * free the just-passed-in new_key and use the existing value + * + * - entry already exists in table, not reusing key: + * free the entry in the table, use the new key + * + * - entry not already in table: + * use the new key, free nothing + * + * We update the hash at the same time... + */ + if (already_exists) + { + /* Note: we must record the old value before writing the new key + * because we might change the value in the event that the two + * arrays are shared. + */ + value_to_free = hash_table->values[node_index]; + + if (keep_new_key) + { + key_to_free = hash_table->keys[node_index]; + hash_table->keys[node_index] = new_key; + } + else + key_to_free = new_key; + } + else + { + hash_table->hashes[node_index] = key_hash; + hash_table->keys[node_index] = new_key; + } + + /* Step two: check if the value that we are about to write to the + * table is the same as the key in the same position. If it's not, + * split the table. + */ + if (G_UNLIKELY (hash_table->keys == hash_table->values && hash_table->keys[node_index] != new_value)) + hash_table->values = g_memdup (hash_table->keys, sizeof (gpointer) * hash_table->size); + + /* Step 3: Actually do the write */ + hash_table->values[node_index] = new_value; + + /* Now, the bookkeeping... */ + if (!already_exists) + { + 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 (already_exists) + { + if (hash_table->key_destroy_func && !reusing_key) + (* hash_table->key_destroy_func) (key_to_free); + if (hash_table->value_destroy_func) + (* hash_table->value_destroy_func) (value_to_free); + } + + return !already_exists; +} + +/** + * 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.30 + */ +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, TRUE); + +#ifndef G_DISABLE_ASSERT + ri->version++; + ri->hash_table->version++; +#endif +} + /** * g_hash_table_iter_steal: - * @iter: an initialized #GHashTableIter. + * @iter: an initialized #GHashTableIter * - * Removes the key/value pair currently pointed to by the iterator - * from its associated #GHashTable, without calling the key and value - * destroy functions. Can only be called after - * g_hash_table_iter_next() returned %TRUE, and cannot be called more - * than once for the same key/value pair. + * Removes the key/value pair currently pointed to by the + * iterator from its associated #GHashTable, without calling + * the key and value destroy functions. Can only be called + * after g_hash_table_iter_next() returned %TRUE, and cannot + * be called more than once for the same key/value pair. * * Since: 2.16 - **/ + */ void g_hash_table_iter_steal (GHashTableIter *iter) { @@ -809,28 +1057,28 @@ g_hash_table_iter_steal (GHashTableIter *iter) /** * g_hash_table_ref: - * @hash_table: a valid #GHashTable. + * @hash_table: a valid #GHashTable * * 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 - **/ -GHashTable* + */ +GHashTable * g_hash_table_ref (GHashTable *hash_table) { g_return_val_if_fail (hash_table != NULL, NULL); - g_return_val_if_fail (hash_table->ref_count > 0, hash_table); - g_atomic_int_add (&hash_table->ref_count, 1); + g_atomic_int_inc (&hash_table->ref_count); + return hash_table; } /** * g_hash_table_unref: - * @hash_table: a valid #GHashTable. + * @hash_table: a valid #GHashTable * * Atomically decrements the reference count of @hash_table by one. * If the reference count drops to 0, all keys and values will be @@ -838,24 +1086,26 @@ g_hash_table_ref (GHashTable *hash_table) * This function is MT-safe and may be called from any thread. * * Since: 2.10 - **/ + */ void g_hash_table_unref (GHashTable *hash_table) { g_return_if_fail (hash_table != NULL); - g_return_if_fail (hash_table->ref_count > 0); - if (g_atomic_int_exchange_and_add (&hash_table->ref_count, -1) - 1 == 0) + if (g_atomic_int_dec_and_test (&hash_table->ref_count)) { - g_hash_table_remove_all_nodes (hash_table, TRUE); - g_free (hash_table->nodes); + 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); + g_free (hash_table->hashes); g_slice_free (GHashTable, hash_table); } } /** * g_hash_table_destroy: - * @hash_table: a #GHashTable. + * @hash_table: a #GHashTable * * Destroys all keys and values in the #GHashTable and decrements its * reference count by 1. If keys and/or values are dynamically allocated, @@ -863,12 +1113,11 @@ g_hash_table_unref (GHashTable *hash_table) * notifiers using g_hash_table_new_full(). In the latter case the destroy * functions you supplied will be called on all keys and values during the * destruction phase. - **/ + */ void g_hash_table_destroy (GHashTable *hash_table) { g_return_if_fail (hash_table != NULL); - g_return_if_fail (hash_table->ref_count > 0); g_hash_table_remove_all (hash_table); g_hash_table_unref (hash_table); @@ -876,37 +1125,38 @@ g_hash_table_destroy (GHashTable *hash_table) /** * g_hash_table_lookup: - * @hash_table: a #GHashTable. - * @key: the key to look up. + * @hash_table: a #GHashTable + * @key: the key to look up * * Looks up a key in a #GHashTable. Note that this function cannot * distinguish between a key that is not present and one which is present * and has the value %NULL. If you need this distinction, use * g_hash_table_lookup_extended(). * - * Return value: 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, - gconstpointer key) +g_hash_table_lookup (GHashTable *hash_table, + gconstpointer key) { - GHashNode *node; - guint node_index; + guint node_index; + guint node_hash; g_return_val_if_fail (hash_table != NULL, NULL); - node_index = g_hash_table_lookup_node (hash_table, key); - node = &hash_table->nodes [node_index]; + node_index = g_hash_table_lookup_node (hash_table, key, &node_hash); - return node->key_hash ? node->value : NULL; + return HASH_IS_REAL (hash_table->hashes[node_index]) + ? hash_table->values[node_index] + : NULL; } /** * g_hash_table_lookup_extended: * @hash_table: a #GHashTable * @lookup_key: the key to look up - * @orig_key: return location for the original key, or %NULL - * @value: return location for the value associated with the key, or %NULL + * @orig_key: (allow-none): return location for the original key, or %NULL + * @value: (allow-none): return location for the value associated with the key, or %NULL * * Looks up a key in the #GHashTable, returning the original key and the * associated value and a #gboolean which is %TRUE if the key was found. This @@ -914,32 +1164,32 @@ g_hash_table_lookup (GHashTable *hash_table, * for example before calling g_hash_table_remove(). * * You can actually pass %NULL for @lookup_key to test - * whether the %NULL key exists. + * 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, gconstpointer lookup_key, gpointer *orig_key, gpointer *value) { - GHashNode *node; - guint node_index; + guint node_index; + guint node_hash; g_return_val_if_fail (hash_table != NULL, FALSE); - node_index = g_hash_table_lookup_node (hash_table, lookup_key); - node = &hash_table->nodes [node_index]; + node_index = g_hash_table_lookup_node (hash_table, lookup_key, &node_hash); - if (!node->key_hash) + if (!HASH_IS_REAL (hash_table->hashes[node_index])) return FALSE; if (orig_key) - *orig_key = node->key; + *orig_key = hash_table->keys[node_index]; if (value) - *value = node->value; + *value = hash_table->values[node_index]; return TRUE; } @@ -956,110 +1206,122 @@ g_hash_table_lookup_extended (GHashTable *hash_table, * Implements the common logic for the g_hash_table_insert() and * g_hash_table_replace() functions. * - * Do a lookup of @key. If it is found, replace it with the new - * @value (and perhaps the new @key). If it is not found, create a - * new node. + * Do a lookup of @key. If it is found, replace it with the new + * @value (and perhaps the new @key). If it is not found, create + * a new node. + * + * Returns: %TRUE if the key did not exist yet */ -static void +static gboolean g_hash_table_insert_internal (GHashTable *hash_table, gpointer key, gpointer value, gboolean keep_new_key) { - GHashNode *node; - guint node_index; guint key_hash; - guint old_hash; - - g_return_if_fail (hash_table != NULL); - g_return_if_fail (hash_table->ref_count > 0); - - node_index = g_hash_table_lookup_node_for_insertion (hash_table, key, &key_hash); - node = &hash_table->nodes [node_index]; - - old_hash = node->key_hash; - - if (old_hash > 1) - { - if (keep_new_key) - { - if (hash_table->key_destroy_func) - hash_table->key_destroy_func (node->key); - node->key = key; - } - else - { - if (hash_table->key_destroy_func) - hash_table->key_destroy_func (key); - } - - if (hash_table->value_destroy_func) - hash_table->value_destroy_func (node->value); + guint node_index; - node->value = value; - } - else - { - node->key = key; - node->value = value; - node->key_hash = key_hash; + g_return_val_if_fail (hash_table != NULL, FALSE); - hash_table->nnodes++; + node_index = g_hash_table_lookup_node (hash_table, key, &key_hash); - if (old_hash == 0) - { - /* 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 - } + return g_hash_table_insert_node (hash_table, node_index, key_hash, key, value, keep_new_key, FALSE); } /** * g_hash_table_insert: - * @hash_table: a #GHashTable. - * @key: a key to insert. - * @value: the value to associate with the key. + * @hash_table: a #GHashTable + * @key: a key to insert + * @value: the value to associate with the key * * Inserts a new key and value into a #GHashTable. * - * If the key already exists in the #GHashTable its current value is replaced - * with the new value. If you supplied a @value_destroy_func when creating the - * #GHashTable, the old value is freed using that function. If you supplied - * a @key_destroy_func when creating the #GHashTable, the passed key is freed - * using that function. - **/ -void + * If the key already exists in the #GHashTable its current + * value is replaced with the new value. If you supplied a + * @value_destroy_func when creating the #GHashTable, the old + * value is freed using that function. If you supplied a + * @key_destroy_func when creating the #GHashTable, the passed + * key is freed using that function. + * + * Returns: %TRUE if the key did not exist yet + */ +gboolean g_hash_table_insert (GHashTable *hash_table, gpointer key, gpointer value) { - g_hash_table_insert_internal (hash_table, key, value, FALSE); + return g_hash_table_insert_internal (hash_table, key, value, FALSE); } /** * g_hash_table_replace: - * @hash_table: a #GHashTable. - * @key: a key to insert. - * @value: the value to associate with the key. + * @hash_table: a #GHashTable + * @key: a key to insert + * @value: the value to associate with the key * * Inserts a new key and value into a #GHashTable similar to - * g_hash_table_insert(). The difference is that if the key already exists - * in the #GHashTable, it gets replaced by the new key. If you supplied a - * @value_destroy_func when creating the #GHashTable, the old value is freed - * using that function. If you supplied a @key_destroy_func when creating the + * g_hash_table_insert(). The difference is that if the key + * already exists in the #GHashTable, it gets replaced by the + * new key. If you supplied a @value_destroy_func when creating + * the #GHashTable, the old value is freed using that function. + * If you supplied a @key_destroy_func when creating the * #GHashTable, the old key is freed using that function. - **/ -void + * + * Returns: %TRUE of the key did not exist yet + */ +gboolean g_hash_table_replace (GHashTable *hash_table, gpointer key, gpointer value) { - g_hash_table_insert_internal (hash_table, key, value, TRUE); + return g_hash_table_insert_internal (hash_table, key, value, TRUE); +} + +/** + * g_hash_table_add: + * @hash_table: a #GHashTable + * @key: a key to insert + * + * This is a convenience function for using a #GHashTable as a set. It + * is equivalent to calling g_hash_table_replace() with @key as both the + * key and the value. + * + * When a hash table only ever contains keys that have themselves as the + * corresponding value it is able to be stored more efficiently. See + * the discussion in the section description. + * + * Returns: %TRUE if the key did not exist yet + * + * Since: 2.32 + */ +gboolean +g_hash_table_add (GHashTable *hash_table, + gpointer key) +{ + return g_hash_table_insert_internal (hash_table, key, key, TRUE); +} + +/** + * g_hash_table_contains: + * @hash_table: a #GHashTable + * @key: a key to check + * + * Checks if @key is in @hash_table. + * + * Since: 2.32 + **/ +gboolean +g_hash_table_contains (GHashTable *hash_table, + gconstpointer key) +{ + guint node_index; + guint node_hash; + + g_return_val_if_fail (hash_table != NULL, FALSE); + + node_index = g_hash_table_lookup_node (hash_table, key, &node_hash); + + return HASH_IS_REAL (hash_table->hashes[node_index]); } /* @@ -1067,7 +1329,7 @@ g_hash_table_replace (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. @@ -1080,19 +1342,17 @@ g_hash_table_remove_internal (GHashTable *hash_table, gconstpointer key, gboolean notify) { - GHashNode *node; guint node_index; + guint node_hash; g_return_val_if_fail (hash_table != NULL, FALSE); - node_index = g_hash_table_lookup_node (hash_table, key); - node = &hash_table->nodes [node_index]; + node_index = g_hash_table_lookup_node (hash_table, key, &node_hash); - /* g_hash_table_lookup_node() never returns a tombstone, so this is safe */ - if (!node->key_hash) + if (!HASH_IS_REAL (hash_table->hashes[node_index])) return FALSE; - g_hash_table_remove_node (hash_table, node, notify); + g_hash_table_remove_node (hash_table, node_index, notify); g_hash_table_maybe_resize (hash_table); #ifndef G_DISABLE_ASSERT @@ -1104,8 +1364,8 @@ g_hash_table_remove_internal (GHashTable *hash_table, /** * g_hash_table_remove: - * @hash_table: a #GHashTable. - * @key: the key to remove. + * @hash_table: a #GHashTable + * @key: the key to remove * * Removes a key and its associated value from a #GHashTable. * @@ -1114,8 +1374,8 @@ g_hash_table_remove_internal (GHashTable *hash_table, * you have to make sure that any dynamically allocated values are freed * yourself. * - * Return value: %TRUE if the key was found and removed from the #GHashTable. - **/ + * Returns: %TRUE if the key was found and removed from the #GHashTable + */ gboolean g_hash_table_remove (GHashTable *hash_table, gconstpointer key) @@ -1125,14 +1385,14 @@ g_hash_table_remove (GHashTable *hash_table, /** * g_hash_table_steal: - * @hash_table: a #GHashTable. - * @key: the key to remove. + * @hash_table: a #GHashTable + * @key: the key to remove * * Removes a key and its associated value from a #GHashTable without * calling the key and value destroy functions. * - * Return value: %TRUE if the key was found and removed from the #GHashTable. - **/ + * Returns: %TRUE if the key was found and removed from the #GHashTable + */ gboolean g_hash_table_steal (GHashTable *hash_table, gconstpointer key) @@ -1146,13 +1406,13 @@ g_hash_table_steal (GHashTable *hash_table, * * Removes all keys and their associated values from a #GHashTable. * - * If the #GHashTable was created using g_hash_table_new_full(), the keys - * and values are freed using the supplied destroy functions, otherwise you - * have to make sure that any dynamically allocated values are freed - * yourself. + * If the #GHashTable was created using g_hash_table_new_full(), + * the keys and values are freed using the supplied destroy functions, + * otherwise you have to make sure that any dynamically allocated + * values are freed yourself. * * Since: 2.12 - **/ + */ void g_hash_table_remove_all (GHashTable *hash_table) { @@ -1163,19 +1423,19 @@ g_hash_table_remove_all (GHashTable *hash_table) 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); } /** * g_hash_table_steal_all: - * @hash_table: a #GHashTable. + * @hash_table: a #GHashTable * * Removes all keys and their associated values from a #GHashTable * without calling the key and value destroy functions. * * Since: 2.12 - **/ + */ void g_hash_table_steal_all (GHashTable *hash_table) { @@ -1186,22 +1446,22 @@ g_hash_table_steal_all (GHashTable *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); } /* * g_hash_table_foreach_remove_or_steal: - * @hash_table: our #GHashTable + * @hash_table: a #GHashTable * @func: the user's callback function * @user_data: data for @func * @notify: %TRUE if the destroy notify handlers are to be called * - * Implements the common logic for g_hash_table_foreach_remove() and - * g_hash_table_foreach_steal(). + * Implements the common logic for g_hash_table_foreach_remove() + * and g_hash_table_foreach_steal(). * * Iterates over every node in the table, calling @func with the key - * and value of the node (and @user_data). If @func returns %TRUE the + * and value of the node (and @user_data). If @func returns %TRUE the * node is removed from the table. * * If @notify is true then the destroy notify handlers will be called @@ -1209,22 +1469,32 @@ g_hash_table_steal_all (GHashTable *hash_table) */ static guint g_hash_table_foreach_remove_or_steal (GHashTable *hash_table, - GHRFunc func, + GHRFunc func, gpointer user_data, gboolean notify) { guint deleted = 0; gint i; +#ifndef G_DISABLE_ASSERT + gint version = hash_table->version; +#endif for (i = 0; i < hash_table->size; i++) { - GHashNode *node = &hash_table->nodes [i]; + guint node_hash = hash_table->hashes[i]; + gpointer node_key = hash_table->keys[i]; + gpointer node_value = hash_table->values[i]; - if (node->key_hash > 1 && (* func) (node->key, node->value, user_data)) + if (HASH_IS_REAL (node_hash) && + (* func) (node_key, node_value, user_data)) { - g_hash_table_remove_node (hash_table, node, notify); + g_hash_table_remove_node (hash_table, i, notify); deleted++; } + +#ifndef G_DISABLE_ASSERT + g_return_val_if_fail (version == hash_table->version, 0); +#endif } g_hash_table_maybe_resize (hash_table); @@ -1239,21 +1509,21 @@ g_hash_table_foreach_remove_or_steal (GHashTable *hash_table, /** * g_hash_table_foreach_remove: - * @hash_table: a #GHashTable. - * @func: the function to call for each key/value pair. - * @user_data: user data to pass to the function. + * @hash_table: a #GHashTable + * @func: the function to call for each key/value pair + * @user_data: user data to pass to the function * - * Calls the given function for each key/value pair in the #GHashTable. - * If the function returns %TRUE, then the key/value pair is removed from the - * #GHashTable. If you supplied key or value destroy functions when creating - * the #GHashTable, they are used to free the memory allocated for the removed - * keys and values. + * Calls the given function for each key/value pair in the + * #GHashTable. If the function returns %TRUE, then the key/value + * pair is removed from the #GHashTable. If you supplied key or + * value destroy functions when creating the #GHashTable, they are + * used to free the memory allocated for the removed keys and values. * - * See #GHashTableIter for an alternative way to loop over the + * 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, GHRFunc func, @@ -1267,19 +1537,20 @@ g_hash_table_foreach_remove (GHashTable *hash_table, /** * g_hash_table_foreach_steal: - * @hash_table: a #GHashTable. - * @func: the function to call for each key/value pair. - * @user_data: user data to pass to the function. + * @hash_table: a #GHashTable + * @func: the function to call for each key/value pair + * @user_data: user data to pass to the function * - * Calls the given function for each key/value pair in the #GHashTable. - * If the function returns %TRUE, then the key/value pair is removed from the - * #GHashTable, but no key or value destroy functions are called. + * Calls the given function for each key/value pair in the + * #GHashTable. If the function returns %TRUE, then the key/value + * pair is removed from the #GHashTable, but no key or value + * destroy functions are called. * - * See #GHashTableIter for an alternative way to loop over the + * 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, GHRFunc func, @@ -1293,9 +1564,9 @@ g_hash_table_foreach_steal (GHashTable *hash_table, /** * g_hash_table_foreach: - * @hash_table: a #GHashTable. - * @func: the function to call for each key/value pair. - * @user_data: user data to pass to the function. + * @hash_table: a #GHashTable + * @func: the function to call for each key/value pair + * @user_data: user data to pass to the function * * Calls the given function for each of the key/value pairs in the * #GHashTable. The function is passed the key and value of each @@ -1306,68 +1577,100 @@ g_hash_table_foreach_steal (GHashTable *hash_table, * * See g_hash_table_find() for performance caveats for linear * order searches in contrast to g_hash_table_lookup(). - **/ + */ void g_hash_table_foreach (GHashTable *hash_table, GHFunc func, gpointer user_data) { gint i; +#ifndef G_DISABLE_ASSERT + gint version; +#endif g_return_if_fail (hash_table != NULL); g_return_if_fail (func != NULL); +#ifndef G_DISABLE_ASSERT + version = hash_table->version; +#endif + for (i = 0; i < hash_table->size; i++) { - GHashNode *node = &hash_table->nodes [i]; + guint node_hash = hash_table->hashes[i]; + gpointer node_key = hash_table->keys[i]; + gpointer node_value = hash_table->values[i]; + + if (HASH_IS_REAL (node_hash)) + (* func) (node_key, node_value, user_data); - if (node->key_hash > 1) - (* func) (node->key, node->value, user_data); +#ifndef G_DISABLE_ASSERT + g_return_if_fail (version == hash_table->version); +#endif } } /** * g_hash_table_find: - * @hash_table: a #GHashTable. - * @predicate: function to test the key/value pairs for a certain property. - * @user_data: user data to pass to the function. - * - * Calls the given function for key/value pairs in the #GHashTable until - * @predicate returns %TRUE. The function is passed the key and value of - * each pair, and the given @user_data parameter. The hash table may not - * be modified while iterating over it (you can't add/remove items). - * - * Note, that hash tables are really only optimized for forward lookups, - * i.e. g_hash_table_lookup(). - * So code that frequently issues g_hash_table_find() or - * g_hash_table_foreach() (e.g. in the order of once per every entry in a - * hash table) should probably be reworked to use additional or different - * data structures for reverse lookups (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: The value of the first key/value pair is returned, for which - * func evaluates to %TRUE. If no pair with the requested property is found, - * %NULL is returned. + * @hash_table: a #GHashTable + * @predicate: function to test the key/value pairs for a certain property + * @user_data: user data to pass to the function + * + * Calls the given function for key/value pairs in the #GHashTable + * until @predicate returns %TRUE. The function is passed the key + * and value of each pair, and the given @user_data parameter. The + * hash table may not be modified while iterating over it (you can't + * add/remove items). + * + * Note, that hash tables are really only optimized for forward + * lookups, i.e. g_hash_table_lookup(). So code that frequently issues + * g_hash_table_find() or g_hash_table_foreach() (e.g. in the order of + * once per every entry in a hash table) should probably be reworked + * to use additional or different data structures for reverse lookups + * (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). + * + * 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. * * Since: 2.4 - **/ + */ gpointer -g_hash_table_find (GHashTable *hash_table, - GHRFunc predicate, - gpointer user_data) +g_hash_table_find (GHashTable *hash_table, + GHRFunc predicate, + gpointer user_data) { gint i; +#ifndef G_DISABLE_ASSERT + gint version; +#endif + gboolean match; g_return_val_if_fail (hash_table != NULL, NULL); g_return_val_if_fail (predicate != NULL, NULL); +#ifndef G_DISABLE_ASSERT + version = hash_table->version; +#endif + + match = FALSE; + for (i = 0; i < hash_table->size; i++) { - GHashNode *node = &hash_table->nodes [i]; + guint node_hash = hash_table->hashes[i]; + gpointer node_key = hash_table->keys[i]; + gpointer node_value = hash_table->values[i]; + + if (HASH_IS_REAL (node_hash)) + match = predicate (node_key, node_value, user_data); + +#ifndef G_DISABLE_ASSERT + g_return_val_if_fail (version == hash_table->version, NULL); +#endif - if (node->key_hash > 1 && predicate (node->key, node->value, user_data)) - return node->value; + if (match) + return node_value; } return NULL; @@ -1375,12 +1678,12 @@ g_hash_table_find (GHashTable *hash_table, /** * g_hash_table_size: - * @hash_table: a #GHashTable. + * @hash_table: a #GHashTable * * 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) { @@ -1394,12 +1697,12 @@ g_hash_table_size (GHashTable *hash_table) * @hash_table: a #GHashTable * * Retrieves every key inside @hash_table. The returned data is valid - * until @hash_table is modified. + * until changes to the hash release those keys. * - * Return value: 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. + * 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. * * Since: 2.14 */ @@ -1414,26 +1717,70 @@ g_hash_table_get_keys (GHashTable *hash_table) retval = NULL; for (i = 0; i < hash_table->size; i++) { - GHashNode *node = &hash_table->nodes [i]; - - if (node->key_hash > 1) - retval = g_list_prepend (retval, node->key); + if (HASH_IS_REAL (hash_table->hashes[i])) + retval = g_list_prepend (retval, hash_table->keys[i]); } return retval; } /** + * g_hash_table_get_keys_as_array: + * @hash_table: a #GHashTable + * @length: (out): the length of the returned array + * + * Retrieves every key inside @hash_table, as an array. + * + * The returned array is %NULL-terminated but may contain %NULL as a + * key. Use @length to determine the true length if it's possible that + * %NULL was used as the value for a key. + * + * Note: in the common case of a string-keyed #GHashTable, the return + * value of this function can be conveniently cast to (gchar **). + * + * You should always free the return result with g_free(). In the + * above-mentioned case of a string-keyed hash table, it may be + * appropriate to use g_strfreev() if you call g_hash_table_steal_all() + * first to transfer ownership of the keys. + * + * Returns: (array length=length) (transfer container): a + * %NULL-terminated array containing each key from the table. + * + * Since: 2.40 + **/ +gpointer * +g_hash_table_get_keys_as_array (GHashTable *hash_table, + guint *length) +{ + gpointer *result; + guint i, j = 0; + + result = g_new (gpointer, hash_table->nnodes + 1); + for (i = 0; i < hash_table->size; i++) + { + if (HASH_IS_REAL (hash_table->hashes[i])) + result[j++] = hash_table->keys[i]; + } + g_assert_cmpint (j, ==, hash_table->nnodes); + result[j] = NULL; + + if (length) + *length = j; + + return result; +} + +/** * g_hash_table_get_values: * @hash_table: a #GHashTable * - * Retrieves every value inside @hash_table. The returned data is - * valid until @hash_table is modified. + * 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 - * 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. + * 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. * * Since: 2.14 */ @@ -1448,14 +1795,236 @@ g_hash_table_get_values (GHashTable *hash_table) retval = NULL; for (i = 0; i < hash_table->size; i++) { - GHashNode *node = &hash_table->nodes [i]; - - if (node->key_hash > 1) - retval = g_list_prepend (retval, node->value); + if (HASH_IS_REAL (hash_table->hashes[i])) + retval = g_list_prepend (retval, hash_table->values[i]); } return retval; } -#define __G_HASH_C__ -#include "galiasdef.c" +/* Hash functions. + */ + +/** + * g_str_equal: + * @v1: a key + * @v2: a key to compare with @v1 + * + * Compares two strings for byte-by-byte equality and returns %TRUE + * if they are equal. It can be passed to g_hash_table_new() as the + * @key_equal_func parameter, when using non-%NULL strings as keys in a + * #GHashTable. + * + * Note that this function is primarily meant as a hash table comparison + * function. For a general-purpose, %NULL-safe string comparison function, + * see g_strcmp0(). + * + * Returns: %TRUE if the two keys match + */ +gboolean +g_str_equal (gconstpointer v1, + gconstpointer v2) +{ + const gchar *string1 = v1; + const gchar *string2 = v2; + + return strcmp (string1, string2) == 0; +} + +/** + * g_str_hash: + * @v: a string key + * + * Converts a string to a hash value. + * + * This function implements the widely used "djb" hash apparently + * posted by Daniel Bernstein to comp.lang.c some time ago. The 32 + * bit unsigned hash value starts at 5381 and for each byte 'c' in + * the string, is updated: `hash = hash * 33 + c`. This function + * uses the signed value of each byte. + * + * It can be passed to g_hash_table_new() as the @hash_func parameter, + * when using non-%NULL strings as keys in a #GHashTable. + * + * Returns: a hash value corresponding to the key + */ +guint +g_str_hash (gconstpointer v) +{ + const signed char *p; + guint32 h = 5381; + + for (p = v; *p != '\0'; p++) + h = (h << 5) + h + *p; + + return h; +} + +/** + * g_direct_hash: + * @v: (allow-none): a #gpointer key + * + * Converts a gpointer to a hash value. + * It can be passed to g_hash_table_new() as the @hash_func parameter, + * when using opaque pointers compared by pointer value as keys in a + * #GHashTable. + * + * This hash function is also appropriate for keys that are integers + * stored in pointers, such as `GINT_TO_POINTER (n)`. + * + * Returns: a hash value corresponding to the key. + */ +guint +g_direct_hash (gconstpointer v) +{ + return GPOINTER_TO_UINT (v); +} + +/** + * g_direct_equal: + * @v1: (allow-none): a key + * @v2: (allow-none): a key to compare with @v1 + * + * Compares two #gpointer arguments and returns %TRUE if they are equal. + * It can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using opaque pointers compared by pointer value as + * keys in a #GHashTable. + * + * This equality function is also appropriate for keys that are integers + * stored in pointers, such as `GINT_TO_POINTER (n)`. + * + * Returns: %TRUE if the two keys match. + */ +gboolean +g_direct_equal (gconstpointer v1, + gconstpointer v2) +{ + return v1 == v2; +} + +/** + * g_int_equal: + * @v1: a pointer to a #gint key + * @v2: a pointer to a #gint key to compare with @v1 + * + * Compares the two #gint values being pointed to and returns + * %TRUE if they are equal. + * It can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using non-%NULL pointers to integers as keys in a + * #GHashTable. + * + * Note that this function acts on pointers to #gint, not on #gint + * directly: if your hash table's keys are of the form + * `GINT_TO_POINTER (n)`, use g_direct_equal() instead. + * + * Returns: %TRUE if the two keys match. + */ +gboolean +g_int_equal (gconstpointer v1, + gconstpointer v2) +{ + return *((const gint*) v1) == *((const gint*) v2); +} + +/** + * g_int_hash: + * @v: a pointer to a #gint key + * + * Converts a pointer to a #gint to a hash value. + * It can be passed to g_hash_table_new() as the @hash_func parameter, + * when using non-%NULL pointers to integer values as keys in a #GHashTable. + * + * Note that this function acts on pointers to #gint, not on #gint + * directly: if your hash table's keys are of the form + * `GINT_TO_POINTER (n)`, use g_direct_hash() instead. + * + * Returns: a hash value corresponding to the key. + */ +guint +g_int_hash (gconstpointer v) +{ + return *(const gint*) v; +} + +/** + * g_int64_equal: + * @v1: a pointer to a #gint64 key + * @v2: a pointer to a #gint64 key to compare with @v1 + * + * Compares the two #gint64 values being pointed to and returns + * %TRUE if they are equal. + * It can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using non-%NULL pointers to 64-bit integers as keys in a + * #GHashTable. + * + * Returns: %TRUE if the two keys match. + * + * Since: 2.22 + */ +gboolean +g_int64_equal (gconstpointer v1, + gconstpointer v2) +{ + return *((const gint64*) v1) == *((const gint64*) v2); +} + +/** + * g_int64_hash: + * @v: a pointer to a #gint64 key + * + * Converts a pointer to a #gint64 to a hash value. + * + * It can be passed to g_hash_table_new() as the @hash_func parameter, + * when using non-%NULL pointers to 64-bit integer values as keys in a + * #GHashTable. + * + * Returns: a hash value corresponding to the key. + * + * Since: 2.22 + */ +guint +g_int64_hash (gconstpointer v) +{ + return (guint) *(const gint64*) v; +} + +/** + * g_double_equal: + * @v1: a pointer to a #gdouble key + * @v2: a pointer to a #gdouble key to compare with @v1 + * + * Compares the two #gdouble values being pointed to and returns + * %TRUE if they are equal. + * It can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using non-%NULL pointers to doubles as keys in a + * #GHashTable. + * + * Returns: %TRUE if the two keys match. + * + * Since: 2.22 + */ +gboolean +g_double_equal (gconstpointer v1, + gconstpointer v2) +{ + return *((const gdouble*) v1) == *((const gdouble*) v2); +} + +/** + * g_double_hash: + * @v: a pointer to a #gdouble key + * + * Converts a pointer to a #gdouble to a hash value. + * It can be passed to g_hash_table_new() as the @hash_func parameter, + * It can be passed to g_hash_table_new() as the @hash_func parameter, + * when using non-%NULL pointers to doubles as keys in a #GHashTable. + * + * Returns: a hash value corresponding to the key. + * + * Since: 2.22 + */ +guint +g_double_hash (gconstpointer v) +{ + return (guint) *(const gdouble*) v; +}