]> Pileus Git - ~andy/gtk/blob - glib/ghash.c
ed736d4cb78843809ec2665ecbd91d9312b54225
[~andy/gtk] / 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 Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include "glib.h"
19
20
21 #define HASH_TABLE_MIN_SIZE 11
22 #define HASH_TABLE_MAX_SIZE 13845163
23
24
25 typedef struct _GHashNode      GHashNode;
26 typedef struct _GRealHashTable GRealHashTable;
27
28 struct _GHashNode
29 {
30   gpointer key;
31   gpointer value;
32   GHashNode *next;
33 };
34
35 struct _GRealHashTable
36 {
37   gint size;
38   gint nnodes;
39   gint 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 gint       g_hash_closest_prime (gint        num);
48 static GHashNode* g_hash_node_new      (gpointer    key,
49                                         gpointer    value);
50 static void       g_hash_node_destroy  (GHashNode  *hash_node);
51 static void       g_hash_nodes_destroy (GHashNode  *hash_node);
52
53
54 extern gint g_primes[];
55 extern gint g_nprimes;
56
57 static GMemChunk *node_mem_chunk = NULL;
58 static GHashNode *node_free_list = NULL;
59
60
61 GHashTable*
62 g_hash_table_new (GHashFunc    hash_func,
63                   GCompareFunc key_compare_func)
64 {
65   GRealHashTable *hash_table;
66
67   g_return_val_if_fail (hash_func != NULL, NULL);
68
69   hash_table = g_new (GRealHashTable, 1);
70   hash_table->size = 0;
71   hash_table->nnodes = 0;
72   hash_table->frozen = FALSE;
73   hash_table->nodes = NULL;
74   hash_table->hash_func = hash_func;
75   hash_table->key_compare_func = key_compare_func;
76
77   return ((GHashTable*) hash_table);
78 }
79
80 void
81 g_hash_table_destroy (GHashTable *hash_table)
82 {
83   GRealHashTable *rhash_table;
84   gint i;
85
86   if (hash_table)
87     {
88       rhash_table = (GRealHashTable*) hash_table;
89
90       for (i = 0; i < rhash_table->size; i++)
91         g_hash_nodes_destroy (rhash_table->nodes[i]);
92
93       if (rhash_table->nodes)
94         g_free (rhash_table->nodes);
95       g_free (rhash_table);
96     }
97 }
98
99 void
100 g_hash_table_insert (GHashTable *hash_table,
101                      gpointer    key,
102                      gpointer    value)
103 {
104   GRealHashTable *rhash_table;
105   GHashNode *node;
106   guint hash_val;
107
108   if (hash_table)
109     {
110       rhash_table = (GRealHashTable*) hash_table;
111
112       if (rhash_table->size == 0)
113         g_hash_table_resize (hash_table);
114
115       hash_val = (* rhash_table->hash_func) (key) % rhash_table->size;
116
117       node = rhash_table->nodes[hash_val];
118       while (node)
119         {
120           if ((rhash_table->key_compare_func &&
121                (* rhash_table->key_compare_func) (node->key, key)) ||
122               (node->key == key))
123             {
124               node->value = value;
125               return;
126             }
127           node = node->next;
128         }
129
130       node = g_hash_node_new (key, value);
131       node->next = rhash_table->nodes[hash_val];
132       rhash_table->nodes[hash_val] = node;
133
134       rhash_table->nnodes += 1;
135       g_hash_table_resize (hash_table);
136     }
137 }
138
139 void
140 g_hash_table_remove (GHashTable *hash_table,
141                      gpointer    key)
142 {
143   GRealHashTable *rhash_table;
144   GHashNode *node;
145   GHashNode *prev;
146   guint hash_val;
147
148   rhash_table = (GRealHashTable*) hash_table;
149   if (hash_table && rhash_table->size)
150     {
151       hash_val = (* rhash_table->hash_func) (key) % rhash_table->size;
152
153       prev = NULL;
154       node = rhash_table->nodes[hash_val];
155
156       while (node)
157         {
158           if ((rhash_table->key_compare_func &&
159                (* rhash_table->key_compare_func) (node->key, key)) ||
160               (node->key == key))
161             {
162               if (prev)
163                 prev->next = node->next;
164               if (node == rhash_table->nodes[hash_val])
165                 rhash_table->nodes[hash_val] = node->next;
166
167               g_hash_node_destroy (node);
168
169               rhash_table->nnodes -= 1;
170               g_hash_table_resize (hash_table);
171               break;
172             }
173
174           prev = node;
175           node = node->next;
176         }
177     }
178 }
179
180 gpointer
181 g_hash_table_lookup (GHashTable     *hash_table,
182                      const gpointer  key)
183 {
184   GRealHashTable *rhash_table;
185   GHashNode *node;
186   guint hash_val;
187
188   rhash_table = (GRealHashTable*) hash_table;
189   if (hash_table && rhash_table->size)
190     {
191       hash_val = (* rhash_table->hash_func) (key) % rhash_table->size;
192
193       node = rhash_table->nodes[hash_val];
194
195       /* Hash table lookup needs to be fast.
196        *  We therefore remove the extra conditional of testing
197        *  whether to call the key_compare_func or not from
198        *  the inner loop.
199        */
200       if (rhash_table->key_compare_func)
201         {
202           while (node)
203             {
204               if ((* rhash_table->key_compare_func) (node->key, key))
205                 return node->value;
206               node = node->next;
207             }
208         }
209       else
210         {
211           while (node)
212             {
213               if (node->key == key)
214                 return node->value;
215               node = node->next;
216             }
217         }
218     }
219
220   return NULL;
221 }
222
223 void
224 g_hash_table_freeze (GHashTable *hash_table)
225 {
226   GRealHashTable *rhash_table;
227
228   if (hash_table)
229     {
230       rhash_table = (GRealHashTable*) hash_table;
231       rhash_table->frozen = TRUE;
232     }
233 }
234
235 void
236 g_hash_table_thaw (GHashTable *hash_table)
237 {
238   GRealHashTable *rhash_table;
239
240   if (hash_table)
241     {
242       rhash_table = (GRealHashTable*) hash_table;
243       rhash_table->frozen = FALSE;
244
245       g_hash_table_resize (hash_table);
246     }
247 }
248
249 void
250 g_hash_table_foreach (GHashTable *hash_table,
251                       GHFunc      func,
252                       gpointer    user_data)
253 {
254   GRealHashTable *rhash_table;
255   GHashNode *node;
256   gint i;
257
258   if (hash_table)
259     {
260       rhash_table = (GRealHashTable*) hash_table;
261
262       for (i = 0; i < rhash_table->size; i++)
263         {
264           node = rhash_table->nodes[i];
265
266           while (node)
267             {
268               (* func) (node->key, node->value, user_data);
269               node = node->next;
270             }
271         }
272     }
273 }
274
275
276 static void
277 g_hash_table_resize (GHashTable *hash_table)
278 {
279   GRealHashTable *rhash_table;
280   GHashNode **new_nodes;
281   GHashNode *node;
282   GHashNode *next;
283   gfloat nodes_per_list;
284   guint hash_val;
285   gint new_size;
286   gint need_resize;
287   gint i;
288
289   if (hash_table)
290     {
291       rhash_table = (GRealHashTable*) hash_table;
292
293       if (rhash_table->size == 0)
294         {
295           rhash_table->size = HASH_TABLE_MIN_SIZE;
296           rhash_table->nodes = g_new (GHashNode*, rhash_table->size);
297
298           for (i = 0; i < rhash_table->size; i++)
299             rhash_table->nodes[i] = NULL;
300         }
301       else if (!rhash_table->frozen)
302         {
303           need_resize = FALSE;
304           nodes_per_list = (gfloat) rhash_table->nnodes / (gfloat) rhash_table->size;
305
306           if (nodes_per_list < 0.3)
307             {
308               if (rhash_table->size > HASH_TABLE_MIN_SIZE)
309                 need_resize = TRUE;
310             }
311           else if (nodes_per_list > 3.0)
312             {
313               if (rhash_table->size < HASH_TABLE_MAX_SIZE)
314                 need_resize = TRUE;
315             }
316
317           if (need_resize)
318             {
319               new_size = g_hash_closest_prime (rhash_table->nnodes);
320               if (new_size < HASH_TABLE_MIN_SIZE)
321                 new_size = HASH_TABLE_MIN_SIZE;
322               else if (new_size > HASH_TABLE_MAX_SIZE)
323                 new_size = HASH_TABLE_MAX_SIZE;
324
325               new_nodes = g_new (GHashNode*, new_size);
326
327               for (i = 0; i < new_size; i++)
328                 new_nodes[i] = NULL;
329
330               for (i = 0; i < rhash_table->size; i++)
331                 {
332                   node = rhash_table->nodes[i];
333
334                   while (node)
335                     {
336                       next = node->next;
337
338                       hash_val = (* rhash_table->hash_func) (node->key) % new_size;
339                       node->next = new_nodes[hash_val];
340                       new_nodes[hash_val] = node;
341
342                       node = next;
343                     }
344                 }
345
346               g_free (rhash_table->nodes);
347
348               rhash_table->nodes = new_nodes;
349               rhash_table->size = new_size;
350             }
351         }
352     }
353 }
354
355 static gint
356 g_hash_closest_prime (gint num)
357 {
358   gint i;
359
360   for (i = 0; i < g_nprimes; i++)
361     if ((g_primes[i] - num) > 0)
362       return g_primes[i];
363
364   return g_primes[g_nprimes - 1];
365 }
366
367 static GHashNode*
368 g_hash_node_new (gpointer key,
369                  gpointer value)
370 {
371   GHashNode *hash_node;
372
373   if (node_free_list)
374     {
375       hash_node = node_free_list;
376       node_free_list = node_free_list->next;
377     }
378   else
379     {
380       if (!node_mem_chunk)
381         node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
382                                           sizeof (GHashNode),
383                                           1024, G_ALLOC_ONLY);
384
385       hash_node = g_chunk_new (GHashNode, node_mem_chunk);
386     }
387
388   hash_node->key = key;
389   hash_node->value = value;
390   hash_node->next = NULL;
391
392   return hash_node;
393 }
394
395 static void
396 g_hash_node_destroy (GHashNode *hash_node)
397 {
398   if (hash_node)
399     {
400       hash_node->next = node_free_list;
401       node_free_list = hash_node;
402     }
403 }
404
405 static void
406 g_hash_nodes_destroy (GHashNode *hash_node)
407 {
408   GHashNode *node;
409
410   if (hash_node)
411     {
412       node = hash_node;
413       while (node->next)
414         node = node->next;
415       node->next = node_free_list;
416       node_free_list = hash_node;
417     }
418 }