New test.
[mono.git] / mono / utils / mono-hash.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 /*
32  * Imported in mono cvs from version 1.32 of gnome cvs.
33  */
34
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include <glib.h>
40 #include "mono-hash.h"
41 #include "metadata/gc-internal.h"
42
43 #define HASH_TABLE_MIN_SIZE 11
44 #define HASH_TABLE_MAX_SIZE 13845163
45
46 typedef struct _MonoGHashNode      MonoGHashNode;
47
48 struct _MonoGHashNode
49 {
50   gpointer   key;
51   gpointer   value;
52   MonoGHashNode *next;
53 };
54
55 struct _MonoGHashTable
56 {
57   gint             size;
58   gint             nnodes;
59   MonoGHashNode      **nodes;
60   GHashFunc        hash_func;
61   GEqualFunc       key_equal_func;
62   GDestroyNotify   key_destroy_func;
63   GDestroyNotify   value_destroy_func;
64   MonoGHashGCType  gc_type;
65 };
66
67 #define G_HASH_TABLE_RESIZE(hash_table)                         \
68    G_STMT_START {                                               \
69      if ((hash_table->size >= 3 * hash_table->nnodes &&         \
70           hash_table->size > HASH_TABLE_MIN_SIZE) ||            \
71          (3 * hash_table->size <= hash_table->nnodes &&         \
72           hash_table->size < HASH_TABLE_MAX_SIZE))              \
73            g_hash_table_resize (hash_table);                    \
74    } G_STMT_END
75
76 static void             g_hash_table_resize       (MonoGHashTable         *hash_table);
77 static MonoGHashNode**  g_hash_table_lookup_node  (MonoGHashTable     *hash_table,
78                                                    gconstpointer   key);
79 static MonoGHashNode*   g_hash_node_new           (gint            gc_type);
80 static void             g_hash_node_destroy       (MonoGHashNode          *hash_node,
81                                                    MonoGHashGCType type,
82                                                    GDestroyNotify  key_destroy_func,
83                                                    GDestroyNotify  value_destroy_func);
84 static void             g_hash_nodes_destroy      (MonoGHashNode          *hash_node,
85                                                    MonoGHashGCType type,
86                                                   GDestroyNotify   key_destroy_func,
87                                                   GDestroyNotify   value_destroy_func);
88 static guint g_hash_table_foreach_remove_or_steal (MonoGHashTable     *hash_table,
89                                                    GHRFunc         func,
90                                                    gpointer        user_data,
91                                                    gboolean        notify);
92
93 #ifdef HAVE_SGEN_GC
94 static void mono_g_hash_mark (void *addr, MonoGCCopyFunc mark_func);
95 #endif
96
97 G_LOCK_DEFINE_STATIC (g_hash_global);
98
99 #if defined(HAVE_NULL_GC)
100 static GMemChunk *node_mem_chunk = NULL;
101 static MonoGHashNode *node_free_list = NULL;
102 #elif defined(HAVE_SGEN_GC)
103 static MonoGHashNode *node_free_lists [4] = {NULL};
104 static void *hash_descr = NULL;
105 static GMemChunk *node_mem_chunk = NULL;
106 #else
107 static void *node_gc_descs [4] = {NULL};
108 static MonoGHashNode *node_free_lists [4] = {NULL};
109 #endif
110
111 #define SET_NODE_KEY(node, gc_type, val) do { (node)->key = (val); } while (0)
112 #define SET_NODE_VALUE(node, gc_type, val) do { (node)->value = (val); } while (0)
113
114 /**
115  * g_hash_table_new:
116  * @hash_func: a function to create a hash value from a key.
117  *   Hash values are used to determine where keys are stored within the
118  *   #GHashTable data structure. The g_direct_hash(), g_int_hash() and 
119  *   g_str_hash() functions are provided for some common types of keys. 
120  *   If hash_func is %NULL, g_direct_hash() is used.
121  * @key_equal_func: a function to check two keys for equality.  This is
122  *   used when looking up keys in the #GHashTable.  The g_direct_equal(),
123  *   g_int_equal() and g_str_equal() functions are provided for the most
124  *   common types of keys. If @key_equal_func is %NULL, keys are compared
125  *   directly in a similar fashion to g_direct_equal(), but without the
126  *   overhead of a function call.
127  *
128  * Creates a new #GHashTable.
129  * 
130  * Return value: a new #GHashTable.
131  **/
132 MonoGHashTable*
133 mono_g_hash_table_new (GHashFunc    hash_func,
134                   GEqualFunc   key_equal_func)
135 {
136   return mono_g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL);
137 }
138
139 MonoGHashTable*
140 mono_g_hash_table_new_type (GHashFunc    hash_func,
141                   GEqualFunc   key_equal_func,
142                   MonoGHashGCType type)
143 {
144   MonoGHashTable *table = mono_g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL);
145   if (type == MONO_HASH_KEY_GC || type == MONO_HASH_KEY_VALUE_GC)
146           g_assert (hash_func);
147   table->gc_type = type;
148 #if defined(HAVE_SGEN_GC)
149   if (type < 0 || type > MONO_HASH_KEY_VALUE_GC)
150           g_error ("wrong type for gc hashtable");
151   /* 
152    * We use a user defined marking function to avoid having to register a GC root for
153    * each hash node.
154    */
155   if (!hash_descr)
156           hash_descr = mono_gc_make_root_descr_user (mono_g_hash_mark);
157   if (type != MONO_HASH_CONSERVATIVE_GC)
158           mono_gc_register_root_wbarrier ((char*)table, sizeof (MonoGHashTable), hash_descr);
159 #elif defined(HAVE_BOEHM_GC)
160   if (type < 0 || type > MONO_HASH_KEY_VALUE_GC)
161           g_error ("wrong type for gc hashtable");
162   if (!node_gc_descs [type] && type > MONO_HASH_CONSERVATIVE_GC) {
163           gsize bmap = 0;
164           if (type & MONO_HASH_KEY_GC)
165                   bmap |= 1; /* the first field in the node is the key */
166           if (type & MONO_HASH_VALUE_GC)
167                   bmap |= 2; /* the second is the value */
168           bmap |= 4; /* next */
169           node_gc_descs [type] = mono_gc_make_descr_from_bitmap (&bmap, 3);
170
171           MONO_GC_REGISTER_ROOT (node_free_lists [type]);
172   }
173 #endif
174   return table;
175 }
176
177
178
179 /**
180  * g_hash_table_new_full:
181  * @hash_func: a function to create a hash value from a key.
182  * @key_equal_func: a function to check two keys for equality.
183  * @key_destroy_func: a function to free the memory allocated for the key 
184  *   used when removing the entry from the #GHashTable or %NULL if you 
185  *   don't want to supply such a function.
186  * @value_destroy_func: a function to free the memory allocated for the 
187  *   value used when removing the entry from the #GHashTable or %NULL if 
188  *   you don't want to supply such a function.
189  * 
190  * Creates a new #GHashTable like g_hash_table_new() and allows to specify
191  * functions to free the memory allocated for the key and value that get 
192  * called when removing the entry from the #GHashTable.
193  * 
194  * Return value: a new #GHashTable.
195  **/
196 MonoGHashTable*
197 mono_g_hash_table_new_full (GHashFunc       hash_func,
198                        GEqualFunc      key_equal_func,
199                        GDestroyNotify  key_destroy_func,
200                        GDestroyNotify  value_destroy_func)
201 {
202   MonoGHashTable *hash_table;
203 #if HAVE_BOEHM_GC
204   static gboolean inited = FALSE;
205   if (!inited) {
206           MONO_GC_REGISTER_ROOT (node_free_lists [0]);
207           inited = TRUE;
208   }
209   
210   hash_table = GC_MALLOC (sizeof (MonoGHashTable));
211 #else
212   hash_table = g_new (MonoGHashTable, 1);
213 #endif
214   hash_table->size               = HASH_TABLE_MIN_SIZE;
215   hash_table->nnodes             = 0;
216   hash_table->hash_func          = hash_func ? hash_func : g_direct_hash;
217   hash_table->key_equal_func     = key_equal_func == g_direct_equal ? NULL : key_equal_func;
218   hash_table->key_destroy_func   = key_destroy_func;
219   hash_table->value_destroy_func = value_destroy_func;
220 #if HAVE_BOEHM_GC
221   hash_table->nodes              = GC_MALLOC (sizeof (MonoGHashNode*) * hash_table->size);
222 #else
223   hash_table->nodes              = g_new0 (MonoGHashNode*, hash_table->size);
224 #endif
225   hash_table->gc_type            = 0;
226   
227   return hash_table;
228 }
229
230 /**
231  * g_hash_table_destroy:
232  * @hash_table: a #GHashTable.
233  * 
234  * Destroys the #GHashTable. If keys and/or values are dynamically 
235  * allocated, you should either free them first or create the #GHashTable
236  * using g_hash_table_new_full(). In the latter case the destroy functions 
237  * you supplied will be called on all keys and values before destroying 
238  * the #GHashTable.
239  **/
240 void
241 mono_g_hash_table_destroy (MonoGHashTable *hash_table)
242 {
243   guint i;
244   
245   g_return_if_fail (hash_table != NULL);
246   
247   for (i = 0; i < hash_table->size; i++)
248     g_hash_nodes_destroy (hash_table->nodes[i], hash_table->gc_type,
249                           hash_table->key_destroy_func,
250                           hash_table->value_destroy_func);
251
252 #if HAVE_BOEHM_GC
253 #else
254 #if HAVE_SGEN_GC
255   mono_gc_deregister_root ((char*)hash_table);
256 #endif
257   g_free (hash_table->nodes);
258   g_free (hash_table);
259 #endif
260 }
261
262 static inline MonoGHashNode**
263 g_hash_table_lookup_node (MonoGHashTable        *hash_table,
264                           gconstpointer  key)
265 {
266   MonoGHashNode **node;
267   
268   node = &hash_table->nodes
269     [(* hash_table->hash_func) (key) % hash_table->size];
270   
271   /* Hash table lookup needs to be fast.
272    *  We therefore remove the extra conditional of testing
273    *  whether to call the key_equal_func or not from
274    *  the inner loop.
275    */
276   if (hash_table->key_equal_func)
277     while (*node && !(*hash_table->key_equal_func) ((*node)->key, key))
278       node = &(*node)->next;
279   else
280     while (*node && (*node)->key != key)
281       node = &(*node)->next;
282   
283   return node;
284 }
285
286 /**
287  * g_hash_table_lookup:
288  * @hash_table: a #GHashTable.
289  * @key: the key to look up.
290  * 
291  * Looks up a key in a #GHashTable.
292  * 
293  * Return value: the associated value, or %NULL if the key is not found.
294  **/
295 gpointer
296 mono_g_hash_table_lookup (MonoGHashTable          *hash_table,
297                      gconstpointer key)
298 {
299   MonoGHashNode *node;
300   
301   g_return_val_if_fail (hash_table != NULL, NULL);
302   
303   node = *g_hash_table_lookup_node (hash_table, key);
304   
305   return node ? node->value : NULL;
306 }
307
308 /**
309  * g_hash_table_lookup_extended:
310  * @hash_table: a #GHashTable.
311  * @lookup_key: the key to look up.
312  * @orig_key: returns the original key.
313  * @value: returns the value associated with the key.
314  * 
315  * Looks up a key in the #GHashTable, returning the original key and the
316  * associated value and a #gboolean which is %TRUE if the key was found. This 
317  * is useful if you need to free the memory allocated for the original key, 
318  * for example before calling g_hash_table_remove().
319  * 
320  * Return value: %TRUE if the key was found in the #GHashTable.
321  **/
322 gboolean
323 mono_g_hash_table_lookup_extended (MonoGHashTable    *hash_table,
324                               gconstpointer  lookup_key,
325                               gpointer      *orig_key,
326                               gpointer      *value)
327 {
328   MonoGHashNode *node;
329   
330   g_return_val_if_fail (hash_table != NULL, FALSE);
331   
332   node = *g_hash_table_lookup_node (hash_table, lookup_key);
333   
334   if (node)
335     {
336       if (orig_key)
337         *orig_key = node->key;
338       if (value)
339         *value = node->value;
340       return TRUE;
341     }
342   else
343     return FALSE;
344 }
345
346 static inline MonoGHashNode*
347 g_hash_node_new (gint gc_type)
348 {
349   MonoGHashNode *hash_node = NULL;
350
351 #if HAVE_BOEHM_GC
352   if (node_free_lists [gc_type]) {
353           G_LOCK (g_hash_global);
354
355           if (node_free_lists [gc_type]) {
356                   hash_node = node_free_lists [gc_type];
357                   node_free_lists [gc_type] = node_free_lists [gc_type]->next;
358           }
359           G_UNLOCK (g_hash_global);
360   }
361   if (!hash_node) {
362           if (gc_type != MONO_HASH_CONSERVATIVE_GC) {
363                   //hash_node = GC_MALLOC (sizeof (MonoGHashNode));
364                   hash_node = GC_MALLOC_EXPLICITLY_TYPED (sizeof (MonoGHashNode), (GC_descr)node_gc_descs [gc_type]);
365           } else {
366                   hash_node = GC_MALLOC (sizeof (MonoGHashNode));
367           }
368   }
369 #elif defined(HAVE_SGEN_GC)
370   if (node_free_lists [gc_type]) {
371           G_LOCK (g_hash_global);
372
373           if (node_free_lists [gc_type]) {
374                   hash_node = node_free_lists [gc_type];
375                   node_free_lists [gc_type] = node_free_lists [gc_type]->next;
376           }
377           G_UNLOCK (g_hash_global);
378   }
379   if (!hash_node) {
380           if (gc_type != MONO_HASH_CONSERVATIVE_GC) {
381                   /* 
382                    * Marking is handled by the marker function, no need to allocate GC visible
383                    * memory.
384                    */
385                   if (!node_mem_chunk)
386                           node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
387                                                                                                 sizeof (MonoGHashNode),
388                                                                                                 1024, G_ALLOC_ONLY);
389                   hash_node = g_chunk_new (MonoGHashNode, node_mem_chunk);
390           } else {
391                   hash_node = mono_gc_alloc_fixed (sizeof (MonoGHashNode), NULL);
392           }
393   }
394 #else
395 #ifdef _EGLIB_MAJOR
396   G_LOCK (g_hash_global);
397   if (node_free_list)
398     {
399       hash_node = node_free_list;
400       node_free_list = node_free_list->next;
401     }
402   else
403     {
404       hash_node = g_new0 (MonoGHashNode, 1);
405     }
406   G_UNLOCK (g_hash_global);
407 #else
408   G_LOCK (g_hash_global);
409   if (node_free_list)
410     {
411       hash_node = node_free_list;
412       node_free_list = node_free_list->next;
413     }
414   else
415     {
416       if (!node_mem_chunk)
417         node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
418                                           sizeof (MonoGHashNode),
419                                           1024, G_ALLOC_ONLY);
420       
421       hash_node = g_chunk_new (MonoGHashNode, node_mem_chunk);
422     }
423   G_UNLOCK (g_hash_global);
424   #endif
425 #endif
426
427   hash_node->key = NULL;
428   hash_node->value = NULL;
429   hash_node->next = NULL;
430   
431   return hash_node;
432 }
433
434 /**
435  * g_hash_table_insert:
436  * @hash_table: a #GHashTable.
437  * @key: a key to insert.
438  * @value: the value to associate with the key.
439  * 
440  * Inserts a new key and value into a #GHashTable.
441  * 
442  * If the key already exists in the #GHashTable its current value is replaced
443  * with the new value. If you supplied a @value_destroy_func when creating the 
444  * #GHashTable, the old value is freed using that function. If you supplied
445  * a @key_destroy_func when creating the #GHashTable, the passed key is freed 
446  * using that function.
447  **/
448 void
449 mono_g_hash_table_insert (MonoGHashTable *hash_table,
450                      gpointer    key,
451                      gpointer    value)
452 {
453   MonoGHashNode **node;
454   
455   g_return_if_fail (hash_table != NULL);
456   
457   node = g_hash_table_lookup_node (hash_table, key);
458   
459   if (*node)
460     {
461       /* do not reset node->key in this place, keeping
462        * the old key is the intended behaviour. 
463        * g_hash_table_replace() can be used instead.
464        */
465
466       /* free the passed key */
467       if (hash_table->key_destroy_func)
468         hash_table->key_destroy_func (key);
469       
470       if (hash_table->value_destroy_func)
471         hash_table->value_destroy_func ((*node)->value);
472
473           SET_NODE_VALUE ((*node), hash_table->gc_type, value);
474     }
475   else
476     {
477             gint gc_type = hash_table->gc_type;
478             *node = g_hash_node_new (gc_type);
479             SET_NODE_KEY (*node, gc_type, key);
480             SET_NODE_VALUE (*node, gc_type, value);
481             hash_table->nnodes++;
482             G_HASH_TABLE_RESIZE (hash_table);
483     }
484 }
485
486 /**
487  * g_hash_table_replace:
488  * @hash_table: a #GHashTable.
489  * @key: a key to insert.
490  * @value: the value to associate with the key.
491  * 
492  * Inserts a new key and value into a #GHashTable similar to 
493  * g_hash_table_insert(). The difference is that if the key already exists 
494  * in the #GHashTable, it gets replaced by the new key. If you supplied a 
495  * @value_destroy_func when creating the #GHashTable, the old value is freed 
496  * using that function. If you supplied a @key_destroy_func when creating the 
497  * #GHashTable, the old key is freed using that function. 
498  **/
499 void
500 mono_g_hash_table_replace (MonoGHashTable *hash_table,
501                       gpointer    key,
502                       gpointer    value)
503 {
504   MonoGHashNode **node;
505   
506   g_return_if_fail (hash_table != NULL);
507   
508   node = g_hash_table_lookup_node (hash_table, key);
509   
510   if (*node)
511     {
512       if (hash_table->key_destroy_func)
513         hash_table->key_destroy_func ((*node)->key);
514       
515       if (hash_table->value_destroy_func)
516         hash_table->value_destroy_func ((*node)->value);
517
518           SET_NODE_KEY ((*node), hash_table->gc_type, key);
519           SET_NODE_VALUE ((*node), hash_table->gc_type, value);
520     }
521   else
522     {
523             gint gc_type = hash_table->gc_type;
524             *node = g_hash_node_new (gc_type);
525             SET_NODE_KEY (*node, gc_type, key);
526             SET_NODE_VALUE (*node, gc_type, value);
527             hash_table->nnodes++;
528             G_HASH_TABLE_RESIZE (hash_table);
529     }
530 }
531
532 /**
533  * g_hash_table_remove:
534  * @hash_table: a #GHashTable.
535  * @key: the key to remove.
536  * 
537  * Removes a key and its associated value from a #GHashTable.
538  *
539  * If the #GHashTable was created using g_hash_table_new_full(), the
540  * key and value are freed using the supplied destroy functions, otherwise
541  * you have to make sure that any dynamically allocated values are freed 
542  * yourself.
543  * 
544  * Return value: %TRUE if the key was found and removed from the #GHashTable.
545  **/
546 gboolean
547 mono_g_hash_table_remove (MonoGHashTable           *hash_table,
548                      gconstpointer  key)
549 {
550   MonoGHashNode **node, *dest;
551   
552   g_return_val_if_fail (hash_table != NULL, FALSE);
553   
554   node = g_hash_table_lookup_node (hash_table, key);
555   if (*node)
556     {
557       dest = *node;
558       (*node) = dest->next;
559       g_hash_node_destroy (dest, hash_table->gc_type,
560                            hash_table->key_destroy_func,
561                            hash_table->value_destroy_func);
562       hash_table->nnodes--;
563   
564       G_HASH_TABLE_RESIZE (hash_table);
565
566       return TRUE;
567     }
568
569   return FALSE;
570 }
571
572 /**
573  * g_hash_table_steal:
574  * @hash_table: a #GHashTable.
575  * @key: the key to remove.
576  * 
577  * Removes a key and its associated value from a #GHashTable without
578  * calling the key and value destroy functions.
579  *
580  * Return value: %TRUE if the key was found and removed from the #GHashTable.
581  **/
582 gboolean
583 mono_g_hash_table_steal (MonoGHashTable    *hash_table,
584                     gconstpointer  key)
585 {
586   MonoGHashNode **node, *dest;
587   
588   g_return_val_if_fail (hash_table != NULL, FALSE);
589   
590   node = g_hash_table_lookup_node (hash_table, key);
591   if (*node)
592     {
593       dest = *node;
594       (*node) = dest->next;
595       g_hash_node_destroy (dest, hash_table->gc_type, NULL, NULL);
596       hash_table->nnodes--;
597   
598       G_HASH_TABLE_RESIZE (hash_table);
599
600       return TRUE;
601     }
602
603   return FALSE;
604 }
605
606 /**
607  * g_hash_table_foreach_remove:
608  * @hash_table: a #GHashTable.
609  * @func: the function to call for each key/value pair.
610  * @user_data: user data to pass to the function.
611  * 
612  * Calls the given function for each key/value pair in the #GHashTable.
613  * If the function returns %TRUE, then the key/value pair is removed from the
614  * #GHashTable. If you supplied key or value destroy functions when creating
615  * the #GHashTable, they are used to free the memory allocated for the removed
616  * keys and values.
617  * 
618  * Return value: the number of key/value pairs removed.
619  **/
620 guint
621 mono_g_hash_table_foreach_remove (MonoGHashTable        *hash_table,
622                              GHRFunc     func,
623                              gpointer    user_data)
624 {
625   g_return_val_if_fail (hash_table != NULL, 0);
626   g_return_val_if_fail (func != NULL, 0);
627   
628   return g_hash_table_foreach_remove_or_steal (hash_table, func, user_data, TRUE);
629 }
630
631 /**
632  * g_hash_table_foreach_steal:
633  * @hash_table: a #GHashTable.
634  * @func: the function to call for each key/value pair.
635  * @user_data: user data to pass to the function.
636  * 
637  * Calls the given function for each key/value pair in the #GHashTable.
638  * If the function returns %TRUE, then the key/value pair is removed from the
639  * #GHashTable, but no key or value destroy functions are called.
640  * 
641  * Return value: the number of key/value pairs removed.
642  **/
643 guint
644 mono_g_hash_table_foreach_steal (MonoGHashTable *hash_table,
645                             GHRFunc     func,
646                             gpointer    user_data)
647 {
648   g_return_val_if_fail (hash_table != NULL, 0);
649   g_return_val_if_fail (func != NULL, 0);
650   
651   return g_hash_table_foreach_remove_or_steal (hash_table, func, user_data, FALSE);
652 }
653
654 static guint
655 g_hash_table_foreach_remove_or_steal (MonoGHashTable *hash_table,
656                                       GHRFunc     func,
657                                       gpointer    user_data,
658                                       gboolean    notify)
659 {
660   MonoGHashNode *node, *prev;
661   guint i;
662   guint deleted = 0;
663   
664   for (i = 0; i < hash_table->size; i++)
665     {
666     restart:
667       
668       prev = NULL;
669       
670       for (node = hash_table->nodes[i]; node; prev = node, node = node->next)
671         {
672           if ((* func) (node->key, node->value, user_data))
673             {
674               deleted += 1;
675               
676               hash_table->nnodes -= 1;
677               
678               if (prev)
679                 {
680                   prev->next = node->next;
681                   g_hash_node_destroy (node, hash_table->gc_type,
682                                        notify ? hash_table->key_destroy_func : NULL,
683                                        notify ? hash_table->value_destroy_func : NULL);
684                   node = prev;
685                 }
686               else
687                 {
688                   hash_table->nodes[i] = node->next;
689                   g_hash_node_destroy (node, hash_table->gc_type,
690                                        notify ? hash_table->key_destroy_func : NULL,
691                                        notify ? hash_table->value_destroy_func : NULL);
692                   goto restart;
693                 }
694             }
695         }
696     }
697   
698   G_HASH_TABLE_RESIZE (hash_table);
699   
700   return deleted;
701 }
702
703 /**
704  * g_hash_table_foreach:
705  * @hash_table: a #GHashTable.
706  * @func: the function to call for each key/value pair.
707  * @user_data: user data to pass to the function.
708  * 
709  * Calls the given function for each of the key/value pairs in the
710  * #GHashTable.  The function is passed the key and value of each
711  * pair, and the given @user_data parameter.  The hash table may not
712  * be modified while iterating over it (you can't add/remove
713  * items). To remove all items matching a predicate, use
714  * g_hash_table_remove().
715  **/
716 void
717 mono_g_hash_table_foreach (MonoGHashTable *hash_table,
718                       GHFunc      func,
719                       gpointer    user_data)
720 {
721   MonoGHashNode *node;
722   gint i;
723   
724   g_return_if_fail (hash_table != NULL);
725   g_return_if_fail (func != NULL);
726   
727   for (i = 0; i < hash_table->size; i++)
728     for (node = hash_table->nodes[i]; node; node = node->next)
729       (* func) (node->key, node->value, user_data);
730 }
731
732 gpointer
733 mono_g_hash_table_find (MonoGHashTable *hash_table, GHRFunc predicate, gpointer user_data)
734 {
735         int i;
736         MonoGHashNode *node;
737         
738         g_return_val_if_fail (hash_table != NULL, NULL);
739         g_return_val_if_fail (predicate != NULL, NULL);
740
741         for (i = 0; i < hash_table->size; i++){
742                 for (node = hash_table->nodes[i]; node; node = node->next)
743                         if ((*predicate)(node->key, node->value, user_data))
744                                 return node->value;
745         }
746         return NULL;
747 }
748
749 /**
750  * g_hash_table_size:
751  * @hash_table: a #GHashTable.
752  * 
753  * Returns the number of elements contained in the #GHashTable.
754  * 
755  * Return value: the number of key/value pairs in the #GHashTable.
756  **/
757 guint
758 mono_g_hash_table_size (MonoGHashTable *hash_table)
759 {
760   g_return_val_if_fail (hash_table != NULL, 0);
761   
762   return hash_table->nnodes;
763 }
764
765 /**
766  * mono_g_hash_table_remap:
767  * 
768  *  Calls the given function for each key-value pair in the hash table, 
769  * and replaces the value stored in the hash table by the value returned by 
770  * the function.
771  * 
772  **/
773 void        
774 mono_g_hash_table_remap (MonoGHashTable *hash_table,
775                                                  MonoGRemapperFunc func,
776                                                  gpointer user_data)
777 {
778   MonoGHashNode *node;
779   gint i;
780   
781   g_return_if_fail (hash_table != NULL);
782   g_return_if_fail (func != NULL);
783   
784   for (i = 0; i < hash_table->size; i++)
785           for (node = hash_table->nodes[i]; node; node = node->next) {
786                   gpointer new_val = (* func) (node->key, node->value, user_data);
787                   SET_NODE_VALUE (node, hash_table->gc_type, new_val);
788           }
789 }
790
791 typedef struct {
792         MonoGHashTable *hash;
793         gint new_size;
794         MonoGHashNode **nodes;
795 } ResizeData;
796
797 static void*
798 do_resize (void *_data)
799 {
800   ResizeData *data = _data;
801   MonoGHashTable *hash_table = data->hash;
802   MonoGHashNode **new_nodes = data->nodes;
803   MonoGHashNode *node;
804   MonoGHashNode *next;
805   guint hash_val;
806   gint new_size = data->new_size;
807   gint i;
808   void *old_nodes = hash_table->nodes;
809
810   for (i = 0; i < hash_table->size; i++)
811     for (node = hash_table->nodes[i]; node; node = next)
812       {
813         next = node->next;
814
815         hash_val = (* hash_table->hash_func) (node->key) % new_size;
816
817         node->next = new_nodes[hash_val];
818         new_nodes[hash_val] = node;
819       }
820
821   hash_table->nodes = new_nodes;
822   hash_table->size = new_size;
823
824   return old_nodes;
825 }
826
827 static void
828 g_hash_table_resize (MonoGHashTable *hash_table)
829 {
830         ResizeData data;
831         void *old_nodes;
832
833         data.hash = hash_table;
834         data.new_size = g_spaced_primes_closest (hash_table->nnodes);
835         data.new_size = CLAMP (data.new_size, HASH_TABLE_MIN_SIZE, HASH_TABLE_MAX_SIZE);
836
837 #if HAVE_BOEHM_GC
838         data.nodes = GC_MALLOC (sizeof (MonoGHashNode*) * data.new_size);
839 #else
840         data.nodes = g_new0 (MonoGHashNode*, data.new_size);
841 #endif
842
843         old_nodes = mono_gc_invoke_with_gc_lock (do_resize, &data);
844
845 #if HAVE_BOEHM_GC
846 #else
847         g_free (old_nodes);
848 #endif
849 }
850
851 static void
852 g_hash_node_destroy (MonoGHashNode      *hash_node,
853                      MonoGHashGCType type,
854                      GDestroyNotify  key_destroy_func,
855                      GDestroyNotify  value_destroy_func)
856 {
857   if (key_destroy_func)
858     key_destroy_func (hash_node->key);
859   if (value_destroy_func)
860     value_destroy_func (hash_node->value);
861   
862   hash_node->key = NULL;
863   hash_node->value = NULL;
864
865   G_LOCK (g_hash_global);
866 #if defined(HAVE_SGEN_GC) || defined(HAVE_BOEHM_GC)
867   hash_node->next = node_free_lists [type];
868   node_free_lists [type] = hash_node;
869 #else
870   hash_node->next = node_free_list;
871   node_free_list = hash_node;
872 #endif
873   G_UNLOCK (g_hash_global);
874 }
875
876 static void
877 g_hash_nodes_destroy (MonoGHashNode *hash_node,
878                       MonoGHashGCType type,
879                       GFreeFunc  key_destroy_func,
880                       GFreeFunc  value_destroy_func)
881 {
882   if (hash_node)
883     {
884       MonoGHashNode *node = hash_node;
885   
886       while (node->next)
887         {
888           if (key_destroy_func)
889             key_destroy_func (node->key);
890           if (value_destroy_func)
891             value_destroy_func (node->value);
892
893           node->key = NULL;
894           node->value = NULL;
895
896           node = node->next;
897         }
898
899   if (key_destroy_func)
900     key_destroy_func (node->key);
901   if (value_destroy_func)
902     value_destroy_func (node->value);
903
904       node->key = NULL;
905       node->value = NULL;
906  
907       G_LOCK (g_hash_global);
908 #if defined(HAVE_SGEN_GC) || defined(HAVE_BOEHM_GC)
909       node->next = node_free_lists [type];
910       node_free_lists [type] = hash_node;
911 #else
912       node->next = node_free_list;
913       node_free_list = hash_node;
914 #endif
915       G_UNLOCK (g_hash_global);
916     }
917 }
918
919 #ifdef HAVE_SGEN_GC
920
921 /* GC marker function */
922 static void
923 mono_g_hash_mark (void *addr, MonoGCCopyFunc mark_func)
924 {
925         MonoGHashTable *table = (MonoGHashTable*)addr;
926         MonoGHashNode *node;
927         int i;
928
929         if (table->gc_type == MONO_HASH_KEY_GC) {
930                 for (i = 0; i < table->size; i++) {
931                         for (node = table->nodes [i]; node; node = node->next) {
932                                 if (node->key)
933                                         node->key = mark_func (node->key);
934                         }
935                 }
936         } else if (table->gc_type == MONO_HASH_VALUE_GC) {
937                 for (i = 0; i < table->size; i++) {
938                         for (node = table->nodes [i]; node; node = node->next) {
939                                 if (node->value)
940                                         node->value = mark_func (node->value);
941                         }
942                 }
943         } else if (table->gc_type == MONO_HASH_KEY_VALUE_GC) {
944                 for (i = 0; i < table->size; i++) {
945                         for (node = table->nodes [i]; node; node = node->next) {
946                                 if (node->key)
947                                         node->key = mark_func (node->key);
948                                 if (node->value)
949                                         node->value = mark_func (node->value);
950                         }
951                 }
952         }
953 }
954
955 #endif