1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GLib Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GLib Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GLib at ftp://ftp.gtk.org/pub/gtk/.
38 #define HASH_TABLE_MIN_SIZE 11
39 #define HASH_TABLE_MAX_SIZE 13845163
42 typedef struct _GHashNode GHashNode;
57 GEqualFunc key_equal_func;
61 static void g_hash_table_resize (GHashTable *hash_table);
62 static GHashNode** g_hash_table_lookup_node (GHashTable *hash_table,
64 static GHashNode* g_hash_node_new (gpointer key,
66 static void g_hash_node_destroy (GHashNode *hash_node);
67 static void g_hash_nodes_destroy (GHashNode *hash_node);
70 G_LOCK_DEFINE_STATIC (g_hash_global);
72 static GMemChunk *node_mem_chunk = NULL;
73 static GHashNode *node_free_list = NULL;
77 g_hash_table_new (GHashFunc hash_func,
78 GEqualFunc key_equal_func)
80 GHashTable *hash_table;
83 hash_table = g_new (GHashTable, 1);
84 hash_table->size = HASH_TABLE_MIN_SIZE;
85 hash_table->nnodes = 0;
86 hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
87 hash_table->key_equal_func = key_equal_func;
88 hash_table->nodes = g_new (GHashNode*, hash_table->size);
90 for (i = 0; i < hash_table->size; i++)
91 hash_table->nodes[i] = NULL;
97 g_hash_table_destroy (GHashTable *hash_table)
101 g_return_if_fail (hash_table != NULL);
103 for (i = 0; i < hash_table->size; i++)
104 g_hash_nodes_destroy (hash_table->nodes[i]);
106 g_free (hash_table->nodes);
110 static inline GHashNode**
111 g_hash_table_lookup_node (GHashTable *hash_table,
116 node = &hash_table->nodes
117 [(* hash_table->hash_func) (key) % hash_table->size];
119 /* Hash table lookup needs to be fast.
120 * We therefore remove the extra conditional of testing
121 * whether to call the key_equal_func or not from
124 if (hash_table->key_equal_func)
125 while (*node && !(*hash_table->key_equal_func) ((*node)->key, key))
126 node = &(*node)->next;
128 while (*node && (*node)->key != key)
129 node = &(*node)->next;
135 g_hash_table_lookup (GHashTable *hash_table,
140 g_return_val_if_fail (hash_table != NULL, NULL);
142 node = *g_hash_table_lookup_node (hash_table, key);
144 return node ? node->value : NULL;
148 g_hash_table_insert (GHashTable *hash_table,
154 g_return_if_fail (hash_table != NULL);
156 node = g_hash_table_lookup_node (hash_table, key);
160 /* do not reset node->key in this place, keeping
161 * the old key might be intended.
162 * a g_hash_table_remove/g_hash_table_insert pair
163 * can be used otherwise.
165 * node->key = key; */
166 (*node)->value = value;
170 *node = g_hash_node_new (key, value);
171 hash_table->nnodes++;
172 g_hash_table_resize (hash_table);
177 g_hash_table_remove (GHashTable *hash_table,
180 GHashNode **node, *dest;
182 g_return_val_if_fail (hash_table != NULL, FALSE);
184 node = g_hash_table_lookup_node (hash_table, key);
188 (*node) = dest->next;
189 g_hash_node_destroy (dest);
190 hash_table->nnodes--;
192 g_hash_table_resize (hash_table);
201 g_hash_table_lookup_extended (GHashTable *hash_table,
202 gconstpointer lookup_key,
208 g_return_val_if_fail (hash_table != NULL, FALSE);
210 node = *g_hash_table_lookup_node (hash_table, lookup_key);
215 *orig_key = node->key;
217 *value = node->value;
225 g_hash_table_freeze (GHashTable *hash_table)
227 #ifdef G_ENABLE_DEBUG
228 static gboolean first_call = TRUE;
232 g_warning("g_hash_table_freeze and g_hash_table_thaw are deprecated.");
235 #endif /* G_ENABLE_DEBUG */
239 g_hash_table_thaw (GHashTable *hash_table)
244 g_hash_table_foreach_remove (GHashTable *hash_table,
248 GHashNode *node, *prev;
252 g_return_val_if_fail (hash_table != NULL, 0);
253 g_return_val_if_fail (func != NULL, 0);
255 for (i = 0; i < hash_table->size; i++)
261 for (node = hash_table->nodes[i]; node; prev = node, node = node->next)
263 if ((* func) (node->key, node->value, user_data))
267 hash_table->nnodes -= 1;
271 prev->next = node->next;
272 g_hash_node_destroy (node);
277 hash_table->nodes[i] = node->next;
278 g_hash_node_destroy (node);
285 g_hash_table_resize (hash_table);
291 g_hash_table_foreach (GHashTable *hash_table,
298 g_return_if_fail (hash_table != NULL);
299 g_return_if_fail (func != NULL);
301 for (i = 0; i < hash_table->size; i++)
302 for (node = hash_table->nodes[i]; node; node = node->next)
303 (* func) (node->key, node->value, user_data);
306 /* Returns the number of elements contained in the hash table. */
308 g_hash_table_size (GHashTable *hash_table)
310 g_return_val_if_fail (hash_table != NULL, 0);
312 return hash_table->nnodes;
316 g_hash_table_resize (GHashTable *hash_table)
318 GHashNode **new_nodes;
321 gfloat nodes_per_list;
326 nodes_per_list = (gfloat) hash_table->nnodes / (gfloat) hash_table->size;
328 if ((nodes_per_list > 0.3 || hash_table->size <= HASH_TABLE_MIN_SIZE) &&
329 (nodes_per_list < 3.0 || hash_table->size >= HASH_TABLE_MAX_SIZE))
332 new_size = CLAMP(g_spaced_primes_closest (hash_table->nnodes),
334 HASH_TABLE_MAX_SIZE);
335 new_nodes = g_new0 (GHashNode*, new_size);
337 for (i = 0; i < hash_table->size; i++)
338 for (node = hash_table->nodes[i]; node; node = next)
342 hash_val = (* hash_table->hash_func) (node->key) % new_size;
344 node->next = new_nodes[hash_val];
345 new_nodes[hash_val] = node;
348 g_free (hash_table->nodes);
349 hash_table->nodes = new_nodes;
350 hash_table->size = new_size;
354 g_hash_node_new (gpointer key,
357 GHashNode *hash_node;
359 G_LOCK (g_hash_global);
362 hash_node = node_free_list;
363 node_free_list = node_free_list->next;
368 node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
372 hash_node = g_chunk_new (GHashNode, node_mem_chunk);
374 G_UNLOCK (g_hash_global);
376 hash_node->key = key;
377 hash_node->value = value;
378 hash_node->next = NULL;
384 g_hash_node_destroy (GHashNode *hash_node)
387 #ifdef ENABLE_GC_FRIENDLY
388 hash_node->key = NULL;
389 hash_node->value = NULL;
390 #endif /* ENABLE_GC_FRIENDLY */
392 G_LOCK (g_hash_global);
393 hash_node->next = node_free_list;
394 node_free_list = hash_node;
395 G_UNLOCK (g_hash_global);
399 g_hash_nodes_destroy (GHashNode *hash_node)
403 GHashNode *node = hash_node;
407 #ifdef ENABLE_GC_FRIENDLY
410 #endif /* ENABLE_GC_FRIENDLY */
414 #ifdef ENABLE_GC_FRIENDLY
417 #endif /* ENABLE_GC_FRIENDLY */
419 G_LOCK (g_hash_global);
420 node->next = node_free_list;
421 node_free_list = hash_node;
422 G_UNLOCK (g_hash_global);