applied patch from Andreas Persenius <ndap@swipnet.se> that updates the
[platform/upstream/glib.git] / glib / ghash.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /*
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/. 
25  */
26
27 /* 
28  * MT safe
29  */
30
31 #include "glib.h"
32
33
34 #define HASH_TABLE_MIN_SIZE 11
35 #define HASH_TABLE_MAX_SIZE 13845163
36
37
38 typedef struct _GHashNode      GHashNode;
39
40 struct _GHashNode
41 {
42   gpointer key;
43   gpointer value;
44   GHashNode *next;
45 };
46
47 struct _GHashTable
48 {
49   gint size;
50   gint nnodes;
51   GHashNode **nodes;
52   GHashFunc hash_func;
53   GCompareFunc key_compare_func;
54 };
55
56
57 static void             g_hash_table_resize      (GHashTable    *hash_table);
58 static GHashNode**      g_hash_table_lookup_node (GHashTable    *hash_table,
59                                                   gconstpointer  key);
60 static GHashNode*       g_hash_node_new          (gpointer       key,
61                                                   gpointer       value);
62 static void             g_hash_node_destroy      (GHashNode     *hash_node);
63 static void             g_hash_nodes_destroy     (GHashNode     *hash_node);
64
65
66 G_LOCK_DEFINE_STATIC (g_hash_global);
67
68 static GMemChunk *node_mem_chunk = NULL;
69 static GHashNode *node_free_list = NULL;
70
71
72 GHashTable*
73 g_hash_table_new (GHashFunc    hash_func,
74                   GCompareFunc key_compare_func)
75 {
76   GHashTable *hash_table;
77   guint i;
78   
79   hash_table = g_new (GHashTable, 1);
80   hash_table->size = HASH_TABLE_MIN_SIZE;
81   hash_table->nnodes = 0;
82   hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
83   hash_table->key_compare_func = key_compare_func;
84   hash_table->nodes = g_new (GHashNode*, hash_table->size);
85   
86   for (i = 0; i < hash_table->size; i++)
87     hash_table->nodes[i] = NULL;
88   
89   return hash_table;
90 }
91
92 void
93 g_hash_table_destroy (GHashTable *hash_table)
94 {
95   guint i;
96   
97   g_return_if_fail (hash_table != NULL);
98   
99   for (i = 0; i < hash_table->size; i++)
100     g_hash_nodes_destroy (hash_table->nodes[i]);
101   
102   g_free (hash_table->nodes);
103   g_free (hash_table);
104 }
105
106 static inline GHashNode**
107 g_hash_table_lookup_node (GHashTable    *hash_table,
108                           gconstpointer  key)
109 {
110   GHashNode **node;
111   
112   node = &hash_table->nodes
113     [(* hash_table->hash_func) (key) % hash_table->size];
114   
115   /* Hash table lookup needs to be fast.
116    *  We therefore remove the extra conditional of testing
117    *  whether to call the key_compare_func or not from
118    *  the inner loop.
119    */
120   if (hash_table->key_compare_func)
121     while (*node && !(*hash_table->key_compare_func) ((*node)->key, key))
122       node = &(*node)->next;
123   else
124     while (*node && (*node)->key != key)
125       node = &(*node)->next;
126   
127   return node;
128 }
129
130 gpointer
131 g_hash_table_lookup (GHashTable   *hash_table,
132                      gconstpointer key)
133 {
134   GHashNode *node;
135   
136   g_return_val_if_fail (hash_table != NULL, NULL);
137   
138   node = *g_hash_table_lookup_node (hash_table, key);
139   
140   return node ? node->value : NULL;
141 }
142
143 void
144 g_hash_table_insert (GHashTable *hash_table,
145                      gpointer    key,
146                      gpointer    value)
147 {
148   GHashNode **node;
149   
150   g_return_if_fail (hash_table != NULL);
151   
152   node = g_hash_table_lookup_node (hash_table, key);
153   
154   if (*node)
155     {
156       /* do not reset node->key in this place, keeping
157        * the old key might be intended.
158        * a g_hash_table_remove/g_hash_table_insert pair
159        * can be used otherwise.
160        *
161        * node->key = key; */
162       (*node)->value = value;
163     }
164   else
165     {
166       *node = g_hash_node_new (key, value);
167       hash_table->nnodes++;
168       g_hash_table_resize (hash_table);
169     }
170 }
171
172 void
173 g_hash_table_remove (GHashTable      *hash_table,
174                      gconstpointer    key)
175 {
176   GHashNode **node, *dest;
177   
178   g_return_if_fail (hash_table != NULL);
179   
180   node = g_hash_table_lookup_node (hash_table, key);
181
182   if (*node)
183     {
184       dest = *node;
185       (*node) = dest->next;
186       g_hash_node_destroy (dest);
187       hash_table->nnodes--;
188   
189       g_hash_table_resize (hash_table);
190     }
191 }
192
193 gboolean
194 g_hash_table_lookup_extended (GHashTable        *hash_table,
195                               gconstpointer      lookup_key,
196                               gpointer          *orig_key,
197                               gpointer          *value)
198 {
199   GHashNode *node;
200   
201   g_return_val_if_fail (hash_table != NULL, FALSE);
202   
203   node = *g_hash_table_lookup_node (hash_table, lookup_key);
204   
205   if (node)
206     {
207       if (orig_key)
208         *orig_key = node->key;
209       if (value)
210         *value = node->value;
211       return TRUE;
212     }
213   else
214     return FALSE;
215 }
216
217 void
218 g_hash_table_freeze (GHashTable *hash_table)
219 {
220 #ifdef G_ENABLE_DEBUG
221   static gboolean first_call = TRUE;
222
223   if (first_call)
224     {
225       g_warning("g_hash_table_freeze and g_hash_table_thaw are deprecated.");
226       first_call = FALSE;
227     }
228 #endif /* G_ENABLE_DEBUG */
229 }
230
231 void
232 g_hash_table_thaw (GHashTable *hash_table)
233 {
234 }
235
236 guint
237 g_hash_table_foreach_remove (GHashTable *hash_table,
238                              GHRFunc     func,
239                              gpointer    user_data)
240 {
241   GHashNode *node, *prev;
242   guint i;
243   guint deleted = 0;
244   
245   g_return_val_if_fail (hash_table != NULL, 0);
246   g_return_val_if_fail (func != NULL, 0);
247   
248   for (i = 0; i < hash_table->size; i++)
249     {
250     restart:
251       
252       prev = NULL;
253       
254       for (node = hash_table->nodes[i]; node; prev = node, node = node->next)
255         {
256           if ((* func) (node->key, node->value, user_data))
257             {
258               deleted += 1;
259               
260               hash_table->nnodes -= 1;
261               
262               if (prev)
263                 {
264                   prev->next = node->next;
265                   g_hash_node_destroy (node);
266                   node = prev;
267                 }
268               else
269                 {
270                   hash_table->nodes[i] = node->next;
271                   g_hash_node_destroy (node);
272                   goto restart;
273                 }
274             }
275         }
276     }
277   
278   g_hash_table_resize (hash_table);
279   
280   return deleted;
281 }
282
283 void
284 g_hash_table_foreach (GHashTable *hash_table,
285                       GHFunc      func,
286                       gpointer    user_data)
287 {
288   GHashNode *node;
289   gint i;
290   
291   g_return_if_fail (hash_table != NULL);
292   g_return_if_fail (func != NULL);
293   
294   for (i = 0; i < hash_table->size; i++)
295     for (node = hash_table->nodes[i]; node; node = node->next)
296       (* func) (node->key, node->value, user_data);
297 }
298
299 /* Returns the number of elements contained in the hash table. */
300 guint
301 g_hash_table_size (GHashTable *hash_table)
302 {
303   g_return_val_if_fail (hash_table != NULL, 0);
304   
305   return hash_table->nnodes;
306 }
307
308 static void
309 g_hash_table_resize (GHashTable *hash_table)
310 {
311   GHashNode **new_nodes;
312   GHashNode *node;
313   GHashNode *next;
314   gfloat nodes_per_list;
315   guint hash_val;
316   gint new_size;
317   gint i;
318   
319   nodes_per_list = (gfloat) hash_table->nnodes / (gfloat) hash_table->size;
320   
321   if ((nodes_per_list > 0.3 || hash_table->size <= HASH_TABLE_MIN_SIZE) &&
322       (nodes_per_list < 3.0 || hash_table->size >= HASH_TABLE_MAX_SIZE))
323     return;
324   
325   new_size = CLAMP(g_spaced_primes_closest (hash_table->nnodes),
326                    HASH_TABLE_MIN_SIZE,
327                    HASH_TABLE_MAX_SIZE);
328   new_nodes = g_new0 (GHashNode*, new_size);
329   
330   for (i = 0; i < hash_table->size; i++)
331     for (node = hash_table->nodes[i]; node; node = next)
332       {
333         next = node->next;
334
335         hash_val = (* hash_table->hash_func) (node->key) % new_size;
336
337         node->next = new_nodes[hash_val];
338         new_nodes[hash_val] = node;
339       }
340   
341   g_free (hash_table->nodes);
342   hash_table->nodes = new_nodes;
343   hash_table->size = new_size;
344 }
345
346 static GHashNode*
347 g_hash_node_new (gpointer key,
348                  gpointer value)
349 {
350   GHashNode *hash_node;
351   
352   G_LOCK (g_hash_global);
353   if (node_free_list)
354     {
355       hash_node = node_free_list;
356       node_free_list = node_free_list->next;
357     }
358   else
359     {
360       if (!node_mem_chunk)
361         node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
362                                           sizeof (GHashNode),
363                                           1024, G_ALLOC_ONLY);
364       
365       hash_node = g_chunk_new (GHashNode, node_mem_chunk);
366     }
367   G_UNLOCK (g_hash_global);
368   
369   hash_node->key = key;
370   hash_node->value = value;
371   hash_node->next = NULL;
372   
373   return hash_node;
374 }
375
376 static void
377 g_hash_node_destroy (GHashNode *hash_node)
378 {
379
380 #ifdef ENABLE_GC_FRIENDLY
381   hash_node->key = NULL;
382   hash_node->value = NULL;
383 #endif /* ENABLE_GC_FRIENDLY */
384
385   G_LOCK (g_hash_global);
386   hash_node->next = node_free_list;
387   node_free_list = hash_node;
388   G_UNLOCK (g_hash_global);
389 }
390
391 static void
392 g_hash_nodes_destroy (GHashNode *hash_node)
393 {
394   if (hash_node)
395     {
396       GHashNode *node = hash_node;
397   
398       while (node->next)
399         {
400 #ifdef ENABLE_GC_FRIENDLY
401           node->key = NULL;
402           node->value = NULL;
403 #endif /* ENABLE_GC_FRIENDLY */
404           node = node->next;
405         }
406
407 #ifdef ENABLE_GC_FRIENDLY
408       node->key = NULL;
409       node->value = NULL;
410 #endif /* ENABLE_GC_FRIENDLY */
411  
412       G_LOCK (g_hash_global);
413       node->next = node_free_list;
414       node_free_list = hash_node;
415       G_UNLOCK (g_hash_global);
416     }
417 }