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 Library 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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library 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-1999. 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/.
34 #define HASH_TABLE_MIN_SIZE 11
35 #define HASH_TABLE_MAX_SIZE 13845163
38 typedef struct _GHashNode GHashNode
;
54 GCompareFunc key_compare_func
;
58 static void g_hash_table_resize (GHashTable
*hash_table
);
59 static GHashNode
** g_hash_table_lookup_node (GHashTable
*hash_table
,
61 static GHashNode
* g_hash_node_new (gpointer key
,
63 static void g_hash_node_destroy (GHashNode
*hash_node
);
64 static void g_hash_nodes_destroy (GHashNode
*hash_node
);
67 G_LOCK_DEFINE_STATIC (g_hash_global
);
69 static GMemChunk
*node_mem_chunk
= NULL
;
70 static GHashNode
*node_free_list
= NULL
;
74 g_hash_table_new (GHashFunc hash_func
,
75 GCompareFunc key_compare_func
)
77 GHashTable
*hash_table
;
80 hash_table
= g_new (GHashTable
, 1);
81 hash_table
->size
= HASH_TABLE_MIN_SIZE
;
82 hash_table
->nnodes
= 0;
83 hash_table
->frozen
= FALSE
;
84 hash_table
->hash_func
= hash_func
? hash_func
: g_direct_hash
;
85 hash_table
->key_compare_func
= key_compare_func
;
86 hash_table
->nodes
= g_new (GHashNode
*, hash_table
->size
);
88 for (i
= 0; i
< hash_table
->size
; i
++)
89 hash_table
->nodes
[i
] = NULL
;
95 g_hash_table_destroy (GHashTable
*hash_table
)
99 g_return_if_fail (hash_table
!= NULL
);
101 for (i
= 0; i
< hash_table
->size
; i
++)
102 g_hash_nodes_destroy (hash_table
->nodes
[i
]);
104 g_free (hash_table
->nodes
);
108 static inline GHashNode
**
109 g_hash_table_lookup_node (GHashTable
*hash_table
,
114 node
= &hash_table
->nodes
115 [(* hash_table
->hash_func
) (key
) % hash_table
->size
];
117 /* Hash table lookup needs to be fast.
118 * We therefore remove the extra conditional of testing
119 * whether to call the key_compare_func or not from
122 if (hash_table
->key_compare_func
)
123 while (*node
&& !(*hash_table
->key_compare_func
) ((*node
)->key
, key
))
124 node
= &(*node
)->next
;
126 while (*node
&& (*node
)->key
!= key
)
127 node
= &(*node
)->next
;
133 g_hash_table_lookup (GHashTable
*hash_table
,
138 g_return_val_if_fail (hash_table
!= NULL
, NULL
);
140 node
= *g_hash_table_lookup_node (hash_table
, key
);
142 return node
? node
->value
: NULL
;
146 g_hash_table_insert (GHashTable
*hash_table
,
152 g_return_if_fail (hash_table
!= NULL
);
154 node
= g_hash_table_lookup_node (hash_table
, key
);
158 /* do not reset node->key in this place, keeping
159 * the old key might be intended.
160 * a g_hash_table_remove/g_hash_table_insert pair
161 * can be used otherwise.
163 * node->key = key; */
164 (*node
)->value
= value
;
168 *node
= g_hash_node_new (key
, value
);
169 hash_table
->nnodes
++;
170 if (!hash_table
->frozen
)
171 g_hash_table_resize (hash_table
);
176 g_hash_table_remove (GHashTable
*hash_table
,
179 GHashNode
**node
, *dest
;
181 g_return_if_fail (hash_table
!= NULL
);
183 node
= g_hash_table_lookup_node (hash_table
, key
);
188 (*node
) = dest
->next
;
189 g_hash_node_destroy (dest
);
190 hash_table
->nnodes
--;
192 if (!hash_table
->frozen
)
193 g_hash_table_resize (hash_table
);
198 g_hash_table_lookup_extended (GHashTable
*hash_table
,
199 gconstpointer lookup_key
,
205 g_return_val_if_fail (hash_table
!= NULL
, FALSE
);
207 node
= *g_hash_table_lookup_node (hash_table
, lookup_key
);
212 *orig_key
= node
->key
;
214 *value
= node
->value
;
222 g_hash_table_freeze (GHashTable
*hash_table
)
224 g_return_if_fail (hash_table
!= NULL
);
226 hash_table
->frozen
++;
230 g_hash_table_thaw (GHashTable
*hash_table
)
232 g_return_if_fail (hash_table
!= NULL
);
234 if (hash_table
->frozen
)
235 if (!(--hash_table
->frozen
))
236 g_hash_table_resize (hash_table
);
240 g_hash_table_foreach_remove (GHashTable
*hash_table
,
244 GHashNode
*node
, *prev
;
248 g_return_val_if_fail (hash_table
!= NULL
, 0);
249 g_return_val_if_fail (func
!= NULL
, 0);
251 for (i
= 0; i
< hash_table
->size
; i
++)
257 for (node
= hash_table
->nodes
[i
]; node
; prev
= node
, node
= node
->next
)
259 if ((* func
) (node
->key
, node
->value
, user_data
))
263 hash_table
->nnodes
-= 1;
267 prev
->next
= node
->next
;
268 g_hash_node_destroy (node
);
273 hash_table
->nodes
[i
] = node
->next
;
274 g_hash_node_destroy (node
);
281 if (!hash_table
->frozen
)
282 g_hash_table_resize (hash_table
);
288 g_hash_table_foreach (GHashTable
*hash_table
,
295 g_return_if_fail (hash_table
!= NULL
);
296 g_return_if_fail (func
!= NULL
);
298 for (i
= 0; i
< hash_table
->size
; i
++)
299 for (node
= hash_table
->nodes
[i
]; node
; node
= node
->next
)
300 (* func
) (node
->key
, node
->value
, user_data
);
303 /* Returns the number of elements contained in the hash table. */
305 g_hash_table_size (GHashTable
*hash_table
)
307 g_return_val_if_fail (hash_table
!= NULL
, 0);
309 return hash_table
->nnodes
;
313 g_hash_table_resize (GHashTable
*hash_table
)
315 GHashNode
**new_nodes
;
318 gfloat nodes_per_list
;
323 nodes_per_list
= (gfloat
) hash_table
->nnodes
/ (gfloat
) hash_table
->size
;
325 if ((nodes_per_list
> 0.3 || hash_table
->size
<= HASH_TABLE_MIN_SIZE
) &&
326 (nodes_per_list
< 3.0 || hash_table
->size
>= HASH_TABLE_MAX_SIZE
))
329 new_size
= CLAMP(g_spaced_primes_closest (hash_table
->nnodes
),
331 HASH_TABLE_MAX_SIZE
);
332 new_nodes
= g_new0 (GHashNode
*, new_size
);
334 for (i
= 0; i
< hash_table
->size
; i
++)
335 for (node
= hash_table
->nodes
[i
]; node
; node
= next
)
339 hash_val
= (* hash_table
->hash_func
) (node
->key
) % new_size
;
341 node
->next
= new_nodes
[hash_val
];
342 new_nodes
[hash_val
] = node
;
345 g_free (hash_table
->nodes
);
346 hash_table
->nodes
= new_nodes
;
347 hash_table
->size
= new_size
;
351 g_hash_node_new (gpointer key
,
354 GHashNode
*hash_node
;
356 G_LOCK (g_hash_global
);
359 hash_node
= node_free_list
;
360 node_free_list
= node_free_list
->next
;
365 node_mem_chunk
= g_mem_chunk_new ("hash node mem chunk",
369 hash_node
= g_chunk_new (GHashNode
, node_mem_chunk
);
371 G_UNLOCK (g_hash_global
);
373 hash_node
->key
= key
;
374 hash_node
->value
= value
;
375 hash_node
->next
= NULL
;
381 g_hash_node_destroy (GHashNode
*hash_node
)
383 G_LOCK (g_hash_global
);
384 hash_node
->next
= node_free_list
;
385 node_free_list
= hash_node
;
386 G_UNLOCK (g_hash_global
);
390 g_hash_nodes_destroy (GHashNode
*hash_node
)
394 GHashNode
*node
= hash_node
;
399 G_LOCK (g_hash_global
);
400 node
->next
= node_free_list
;
401 node_free_list
= hash_node
;
402 G_UNLOCK (g_hash_global
);