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