2007-08-06 Rodrigo Kumpera <rkumpera@novell.com>
[mono.git] / mono / utils / mono-hash.c
index 4dbba68f3e8cf64f336bba37107a69d2dddb1c59..70cce73b0d35a26348012a35c13cccea6f2a0668 100644 (file)
 #endif
 
 #include <glib.h>
-#if HAVE_BOEHM_GC
-#include <gc/gc.h>
-#endif
+#include <mono/os/gc_wrapper.h>
 #include "mono-hash.h"
+#include "metadata/gc-internal.h"
 
 #define HASH_TABLE_MIN_SIZE 11
 #define HASH_TABLE_MAX_SIZE 13845163
 
-
 typedef struct _MonoGHashNode      MonoGHashNode;
 
 struct _MonoGHashNode
@@ -64,6 +62,7 @@ struct _MonoGHashTable
   GEqualFunc       key_equal_func;
   GDestroyNotify   key_destroy_func;
   GDestroyNotify   value_destroy_func;
+  MonoGHashGCType  gc_type;
 };
 
 #define G_HASH_TABLE_RESIZE(hash_table)                                \
@@ -79,11 +78,14 @@ static void         g_hash_table_resize       (MonoGHashTable         *hash_table);
 static MonoGHashNode** g_hash_table_lookup_node  (MonoGHashTable     *hash_table,
                                                    gconstpointer   key);
 static MonoGHashNode*  g_hash_node_new           (gpointer        key,
-                                                   gpointer        value);
+                                                   gpointer        value,
+                                                  gint            gc_type);
 static void            g_hash_node_destroy       (MonoGHashNode          *hash_node,
+                                                  MonoGHashGCType type,
                                                    GDestroyNotify  key_destroy_func,
                                                    GDestroyNotify  value_destroy_func);
 static void            g_hash_nodes_destroy      (MonoGHashNode          *hash_node,
+                                                  MonoGHashGCType type,
                                                  GDestroyNotify   key_destroy_func,
                                                  GDestroyNotify   value_destroy_func);
 static guint g_hash_table_foreach_remove_or_steal (MonoGHashTable     *hash_table,
@@ -94,10 +96,15 @@ static guint g_hash_table_foreach_remove_or_steal (MonoGHashTable     *hash_tabl
 
 G_LOCK_DEFINE_STATIC (g_hash_global);
 
-#if !HAVE_BOEHM_GC
+#if defined(HAVE_NULL_GC)
 static GMemChunk *node_mem_chunk = NULL;
 #endif
+#if defined(HAVE_SGEN_GC)
+static void *node_gc_descs [4] = {NULL};
+static MonoGHashNode *node_free_lists [4] = {NULL};
+#else
 static MonoGHashNode *node_free_list = NULL;
+#endif
 
 /**
  * g_hash_table_new:
@@ -124,6 +131,29 @@ mono_g_hash_table_new (GHashFunc    hash_func,
   return mono_g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL);
 }
 
+MonoGHashTable*
+mono_g_hash_table_new_type (GHashFunc    hash_func,
+                 GEqualFunc   key_equal_func,
+                 MonoGHashGCType type)
+{
+  MonoGHashTable *table = mono_g_hash_table_new_full (hash_func, key_equal_func, NULL, NULL);
+  table->gc_type = type;
+#if defined(HAVE_SGEN_GC)
+  if (type < 0 || type > MONO_HASH_KEY_VALUE_GC)
+         g_error ("wrong type for gc hashtable");
+  if (!node_gc_descs [type] && type > MONO_HASH_CONSERVATIVE_GC) {
+         gsize bmap = 0;
+         if (type & MONO_HASH_KEY_GC)
+                 bmap |= 1; /* the first field in the node is the key */
+         if (type & MONO_HASH_VALUE_GC)
+                 bmap |= 2; /* the second is the value */
+         node_gc_descs [type] = mono_gc_make_descr_from_bitmap (&bmap, 2);
+  }
+#endif
+  return table;
+}
+
+
 
 /**
  * g_hash_table_new_full:
@@ -149,23 +179,29 @@ mono_g_hash_table_new_full (GHashFunc       hash_func,
                       GDestroyNotify  value_destroy_func)
 {
   MonoGHashTable *hash_table;
-  
 #if HAVE_BOEHM_GC
-  hash_table = GC_malloc (sizeof (MonoGHashTable));
+  static gboolean inited = FALSE;
+  if (!inited) {
+    MONO_GC_REGISTER_ROOT (node_free_list);
+    inited = TRUE;
+  }
+  
+  hash_table = GC_MALLOC (sizeof (MonoGHashTable));
 #else
   hash_table = g_new (MonoGHashTable, 1);
 #endif
   hash_table->size               = HASH_TABLE_MIN_SIZE;
   hash_table->nnodes             = 0;
   hash_table->hash_func          = hash_func ? hash_func : g_direct_hash;
-  hash_table->key_equal_func     = key_equal_func;
+  hash_table->key_equal_func     = key_equal_func == g_direct_equal ? NULL : key_equal_func;
   hash_table->key_destroy_func   = key_destroy_func;
   hash_table->value_destroy_func = value_destroy_func;
 #if HAVE_BOEHM_GC
-  hash_table->nodes              = GC_malloc (sizeof (MonoGHashNode*) * hash_table->size);
+  hash_table->nodes              = GC_MALLOC (sizeof (MonoGHashNode*) * hash_table->size);
 #else
   hash_table->nodes              = g_new0 (MonoGHashNode*, hash_table->size);
 #endif
+  hash_table->gc_type            = 0;
   
   return hash_table;
 }
@@ -188,7 +224,7 @@ mono_g_hash_table_destroy (MonoGHashTable *hash_table)
   g_return_if_fail (hash_table != NULL);
   
   for (i = 0; i < hash_table->size; i++)
-    g_hash_nodes_destroy (hash_table->nodes[i], 
+    g_hash_nodes_destroy (hash_table->nodes[i], hash_table->gc_type,
                          hash_table->key_destroy_func,
                          hash_table->value_destroy_func);
 
@@ -283,6 +319,63 @@ mono_g_hash_table_lookup_extended (MonoGHashTable    *hash_table,
     return FALSE;
 }
 
+static inline MonoGHashNode*
+g_hash_node_new (gpointer key,
+                gpointer value,
+                gint gc_type)
+{
+  MonoGHashNode *hash_node = NULL;
+
+#if HAVE_BOEHM_GC
+  if (node_free_list) {
+         G_LOCK (g_hash_global);
+
+         if (node_free_list) {
+                 hash_node = node_free_list;
+                 node_free_list = node_free_list->next;
+         }
+         G_UNLOCK (g_hash_global);
+  }
+  if (!hash_node)
+      hash_node = GC_MALLOC (sizeof (MonoGHashNode));
+#elif defined(HAVE_SGEN_GC)
+  if (node_free_lists [gc_type]) {
+         G_LOCK (g_hash_global);
+
+         if (node_free_lists [gc_type]) {
+                 hash_node = node_free_lists [gc_type];
+                 node_free_lists [gc_type] = node_free_lists [gc_type]->next;
+         }
+         G_UNLOCK (g_hash_global);
+  }
+  if (!hash_node)
+      hash_node = mono_gc_alloc_fixed (sizeof (MonoGHashNode), node_gc_descs [gc_type]);
+#else
+  G_LOCK (g_hash_global);
+  if (node_free_list)
+    {
+      hash_node = node_free_list;
+      node_free_list = node_free_list->next;
+    }
+  else
+    {
+      if (!node_mem_chunk)
+       node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
+                                         sizeof (MonoGHashNode),
+                                         1024, G_ALLOC_ONLY);
+      
+      hash_node = g_chunk_new (MonoGHashNode, node_mem_chunk);
+    }
+  G_UNLOCK (g_hash_global);
+#endif
+  
+  hash_node->key = key;
+  hash_node->value = value;
+  hash_node->next = NULL;
+  
+  return hash_node;
+}
+
 /**
  * g_hash_table_insert:
  * @hash_table: a #GHashTable.
@@ -326,7 +419,7 @@ mono_g_hash_table_insert (MonoGHashTable *hash_table,
     }
   else
     {
-      *node = g_hash_node_new (key, value);
+      *node = g_hash_node_new (key, value, hash_table->gc_type);
       hash_table->nnodes++;
       G_HASH_TABLE_RESIZE (hash_table);
     }
@@ -369,7 +462,7 @@ mono_g_hash_table_replace (MonoGHashTable *hash_table,
     }
   else
     {
-      *node = g_hash_node_new (key, value);
+      *node = g_hash_node_new (key, value, hash_table->gc_type);
       hash_table->nnodes++;
       G_HASH_TABLE_RESIZE (hash_table);
     }
@@ -402,7 +495,7 @@ mono_g_hash_table_remove (MonoGHashTable       *hash_table,
     {
       dest = *node;
       (*node) = dest->next;
-      g_hash_node_destroy (dest, 
+      g_hash_node_destroy (dest, hash_table->gc_type,
                           hash_table->key_destroy_func,
                           hash_table->value_destroy_func);
       hash_table->nnodes--;
@@ -438,7 +531,7 @@ mono_g_hash_table_steal (MonoGHashTable    *hash_table,
     {
       dest = *node;
       (*node) = dest->next;
-      g_hash_node_destroy (dest, NULL, NULL);
+      g_hash_node_destroy (dest, hash_table->gc_type, NULL, NULL);
       hash_table->nnodes--;
   
       G_HASH_TABLE_RESIZE (hash_table);
@@ -524,7 +617,7 @@ g_hash_table_foreach_remove_or_steal (MonoGHashTable *hash_table,
              if (prev)
                {
                  prev->next = node->next;
-                 g_hash_node_destroy (node,
+                 g_hash_node_destroy (node, hash_table->gc_type,
                                       notify ? hash_table->key_destroy_func : NULL,
                                       notify ? hash_table->value_destroy_func : NULL);
                  node = prev;
@@ -532,7 +625,7 @@ g_hash_table_foreach_remove_or_steal (MonoGHashTable *hash_table,
              else
                {
                  hash_table->nodes[i] = node->next;
-                 g_hash_node_destroy (node,
+                 g_hash_node_destroy (node, hash_table->gc_type,
                                       notify ? hash_table->key_destroy_func : NULL,
                                       notify ? hash_table->value_destroy_func : NULL);
                  goto restart;
@@ -591,6 +684,30 @@ mono_g_hash_table_size (MonoGHashTable *hash_table)
   return hash_table->nnodes;
 }
 
+/**
+ * mono_g_hash_table_remap:
+ * 
+ *  Calls the given function for each key-value pair in the hash table, 
+ * and replaces the value stored in the hash table by the value returned by 
+ * the function.
+ * 
+ **/
+void        
+mono_g_hash_table_remap (MonoGHashTable *hash_table,
+                                                MonoGRemapperFunc func,
+                                                gpointer user_data)
+{
+  MonoGHashNode *node;
+  gint i;
+  
+  g_return_if_fail (hash_table != NULL);
+  g_return_if_fail (func != NULL);
+  
+  for (i = 0; i < hash_table->size; i++)
+    for (node = hash_table->nodes[i]; node; node = node->next)
+      node->value = (* func) (node->key, node->value, user_data);
+}
+
 static void
 g_hash_table_resize (MonoGHashTable *hash_table)
 {
@@ -605,7 +722,7 @@ g_hash_table_resize (MonoGHashTable *hash_table)
   new_size = CLAMP (new_size, HASH_TABLE_MIN_SIZE, HASH_TABLE_MAX_SIZE);
  
 #if HAVE_BOEHM_GC
-  new_nodes              = GC_malloc (sizeof (MonoGHashNode*) * new_size);
+  new_nodes              = GC_MALLOC (sizeof (MonoGHashNode*) * new_size);
 #else
   new_nodes              = g_new0 (MonoGHashNode*, new_size);
 #endif
@@ -629,42 +746,9 @@ g_hash_table_resize (MonoGHashTable *hash_table)
   hash_table->size = new_size;
 }
 
-static MonoGHashNode*
-g_hash_node_new (gpointer key,
-                gpointer value)
-{
-  MonoGHashNode *hash_node;
-  
-  G_LOCK (g_hash_global);
-  if (node_free_list)
-    {
-      hash_node = node_free_list;
-      node_free_list = node_free_list->next;
-    }
-  else
-    {
-#if HAVE_BOEHM_GC
-      hash_node = GC_malloc (sizeof (MonoGHashNode));
-#else
-      if (!node_mem_chunk)
-       node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
-                                         sizeof (MonoGHashNode),
-                                         1024, G_ALLOC_ONLY);
-      
-      hash_node = g_chunk_new (MonoGHashNode, node_mem_chunk);
-#endif
-    }
-  G_UNLOCK (g_hash_global);
-  
-  hash_node->key = key;
-  hash_node->value = value;
-  hash_node->next = NULL;
-  
-  return hash_node;
-}
-
 static void
 g_hash_node_destroy (MonoGHashNode      *hash_node,
+                    MonoGHashGCType type,
                     GDestroyNotify  key_destroy_func,
                     GDestroyNotify  value_destroy_func)
 {
@@ -677,13 +761,19 @@ g_hash_node_destroy (MonoGHashNode      *hash_node,
   hash_node->value = NULL;
 
   G_LOCK (g_hash_global);
+#if defined(HAVE_SGEN_GC)
+  hash_node->next = node_free_lists [type];
+  node_free_lists [type] = hash_node;
+#else
   hash_node->next = node_free_list;
   node_free_list = hash_node;
+#endif
   G_UNLOCK (g_hash_global);
 }
 
 static void
 g_hash_nodes_destroy (MonoGHashNode *hash_node,
+                     MonoGHashGCType type,
                      GFreeFunc  key_destroy_func,
                      GFreeFunc  value_destroy_func)
 {
@@ -713,8 +803,13 @@ g_hash_nodes_destroy (MonoGHashNode *hash_node,
       node->value = NULL;
  
       G_LOCK (g_hash_global);
+#if defined(HAVE_SGEN_GC)
+      node->next = node_free_lists [type];
+      node_free_lists [type] = hash_node;
+#else
       node->next = node_free_list;
       node_free_list = hash_node;
+#endif
       G_UNLOCK (g_hash_global);
     }
 }