2006-08-16 Miguel de Icaza <miguel@novell.com>
authorMiguel de Icaza <miguel@gnome.org>
Wed, 16 Aug 2006 04:19:14 +0000 (04:19 -0000)
committerMiguel de Icaza <miguel@gnome.org>
Wed, 16 Aug 2006 04:19:14 +0000 (04:19 -0000)
* test: Add test driver, and initial tests.

2006-08-15  Miguel de Icaza  <miguel@novell.com>

* src/ghashtable.c: Implement most of this, it is completely
untested at this point.

svn path=/trunk/mono/; revision=63798

eglib/ChangeLog
eglib/configure.in
eglib/src/Makefile
eglib/src/Makefile.am
eglib/src/Makefile.in
eglib/src/ghashtable.c
eglib/src/glib.h
eglib/test/Makefile.am
eglib/test/driver.c [new file with mode: 0644]
eglib/test/hash.c [new file with mode: 0644]
eglib/test/test.h [new file with mode: 0644]

index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..518a4c3eae11e9eebf861d56d9f42d6f5c9c3241 100644 (file)
@@ -0,0 +1,9 @@
+2006-08-16  Miguel de Icaza  <miguel@novell.com>
+
+       * test: Add test driver, and initial tests. 
+
+2006-08-15  Miguel de Icaza  <miguel@novell.com>
+
+       * src/ghashtable.c: Implement most of this, it is completely
+       untested at this point.
+
index cf266756397d8b0099994ae8a6a5c1938f4a7876..fd7313fd49d50bbf078fde319ad8bf40bafc32a5 100644 (file)
@@ -7,6 +7,8 @@ AM_MAINTAINER_MODE
 
 AC_PROG_CC
 AM_PROG_LIBTOOL
+CFLAGS='-g -O0'
+AC_SUBST(CFLAGS)
 
 AC_OUTPUT([
 Makefile
index abeb3078b5e3fa1728b03bae7b4c5ceefa4e14d1..28ad3850de5af961821a64c9f273c8e2eedafad5 100644 (file)
@@ -47,7 +47,7 @@ mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 LTLIBRARIES = $(noinst_LTLIBRARIES)
-libeglib_la_LIBADD =
+libeglib_la_DEPENDENCIES =
 am_libeglib_la_OBJECTS = ghashtable.lo
 libeglib_la_OBJECTS = $(am_libeglib_la_OBJECTS)
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
@@ -77,7 +77,7 @@ AUTOMAKE = ${SHELL} /cvs/mono/missing --run automake-1.9
 AWK = gawk
 CC = gcc
 CCDEPMODE = depmode=gcc3
-CFLAGS = -g -O2
+CFLAGS = -g -O0
 CPP = gcc -E
 CPPFLAGS = 
 CXX = g++
@@ -174,6 +174,7 @@ libeglib_la_SOURCES = \
        ghashtable.c
 
 INCLUDES = -I$(srcdir)
+libeglib_la_LIBADD = -lm
 all: all-am
 
 .SUFFIXES:
index 3046a38fc655bfc2ee2e74cad9cfa2dab21fe52e..5070e91f941ced2e93691628c0811a83518f69c3 100644 (file)
@@ -3,4 +3,6 @@ noinst_LTLIBRARIES = libeglib.la
 libeglib_la_SOURCES = \
        ghashtable.c
 
-INCLUDES = -I$(srcdir)
\ No newline at end of file
+INCLUDES = -I$(srcdir)
+
+libeglib_la_LIBADD = -lm
\ No newline at end of file
index fb4543c486a60f716de73a42d59a3537120d740c..3ec4799808db08777b96474f96404599f51a378c 100644 (file)
@@ -47,7 +47,7 @@ mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/config.h
 CONFIG_CLEAN_FILES =
 LTLIBRARIES = $(noinst_LTLIBRARIES)
-libeglib_la_LIBADD =
+libeglib_la_DEPENDENCIES =
 am_libeglib_la_OBJECTS = ghashtable.lo
 libeglib_la_OBJECTS = $(am_libeglib_la_OBJECTS)
 DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
@@ -174,6 +174,7 @@ libeglib_la_SOURCES = \
        ghashtable.c
 
 INCLUDES = -I$(srcdir)
+libeglib_la_LIBADD = -lm
 all: all-am
 
 .SUFFIXES:
index 67a275a43d80f929e56ca2b8aca1774523b550cd..b1aac570cfd8ff330fd86104b24bf9456ced8d09 100644 (file)
@@ -4,29 +4,31 @@
  * Author:
  *   Miguel de Icaza (miguel@novell.com)
  *
- * This is based on the System.Collections.Hashtable from Mono
- * implemented originally by Sergey Chaban (serge@wildwestsoftware.com)
- *
  * (C) 2006 Novell, Inc.
  */
 #include <stdio.h>
-#include <glib.h>
 #include <math.h>
+#include <glib.h>
+
+typedef struct _Slot Slot;
 
-typedef struct {
+struct _Slot {
        gpointer key;
        gpointer value;
-} Slot;
+       Slot    *next;
+};
+
+static gpointer KEYMARKER_REMOVED = &KEYMARKER_REMOVED;
 
 struct _GHashTable {
-       GHashFunc     hash_func;
-       GEqualFunc    key_equal_func;
+       GHashFunc      hash_func;
+       GEqualFunc     key_equal_func;
 
-       float load_factor;
-       Slot *table;
+       Slot **table;
        int   table_size;
-       int   threshold;
        int   in_use;
+       int   threshold;
+       GDestroyNotify value_destroy_func, key_destroy_func;
 };
 
 static int prime_tbl[] = {
@@ -81,13 +83,15 @@ adjust_threshold (GHashTable *hash)
 {
        int size = hash->table_size;
 
-       hash->threshold = (int) hash->table_size * hash->load_factor;
+       hash->threshold = (int) hash->table_size * 0.75;
        if (hash->threshold >= hash->table_size)
                hash->threshold = hash->table_size-1;
+       if (hash->threshold == 0)
+               hash->threshold = 1;
 }
        
 static void
-set_table (GHashTable *hash, Slot *table)
+set_table (GHashTable *hash, Slot **table)
 {
        hash->table = table;
        adjust_threshold (hash);
@@ -96,42 +100,272 @@ set_table (GHashTable *hash, Slot *table)
 GHashTable *
 g_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func)
 {
-       GHashTable *table = g_new0 (GHashTable, 1);
+       GHashTable *hash;
+
+       g_return_val_if_fail (hash_func != NULL, NULL);
+       g_return_val_if_fail (key_equal_func != NULL, NULL);
+                         
+       hash = g_new0 (GHashTable, 1);
+
+       hash->hash_func = hash_func;
+       hash->key_equal_func = key_equal_func;
 
-       table->hash_func = hash_func;
-       table->key_equal_func = key_equal_func;
+       hash->table_size = to_prime (1);
+       hash->table = g_new0 (Slot *, hash->table_size);
+       adjust_threshold (hash);
+       
+       return hash;
+}
 
-       table->load_factor = 0.75;
+GHashTable *
+g_hash_table_new_full (GHashFunc hash_func, GEqualFunc key_equal_func,
+                      GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
+{
+       GHashTable *hash = g_hash_table_new (hash_func, key_equal_func);
+       if (hash == NULL)
+               return NULL;
        
-       table->table_size = to_prime (1);
-       set_table (table, g_new0 (Slot, table->table_size));
+       hash->key_destroy_func = key_destroy_func;
+       hash->value_destroy_func = value_destroy_func;
        
-       return table;
+       return hash;
 }
 
 void
-g_hash_table_insert (GHashTable *hash, gpointer key, gpointer value)
+rehash (GHashTable *hash)
 {
-       Slot *table, *entry;
-       guint size, spot, step;
-       int h, free_index, i;
+       /* FIXME */
+}
+
+void
+g_hash_table_insert_replace (GHashTable *hash, gpointer key, gpointer value, gboolean replace)
+{
+       guint hashcode;
+       Slot *s;
+       GEqualFunc equal;
        
        g_return_if_fail (hash != NULL);
-       
+
+       equal = hash->key_equal_func;
        if (hash->in_use >= hash->threshold)
                rehash (hash);
 
-       size = (guint) hash->table_size;
-       h = get_hash (key) & G_MAXINT32;
-       spot = (guint) h;
-       step = (guint) ((spot>>5)+1) % (size-1)+1;
+       hashcode = ((*hash->hash_func) (key)) % hash->table_size;
+       for (s = hash->table [hashcode]; s != NULL; s = s->next){
+               if ((*equal) (s->key, key)){
+                       if (replace){
+                               if (hash->key_destroy_func != NULL)
+                                       (*hash->key_destroy_func)(s->key);
+                               s->key = key;
+                       }
+                       if (hash->value_destroy_func != NULL)
+                               (*hash->value_destroy_func) (s->value);
+                       s->value = value;
+                       return;
+               }
+       }
+       s = g_new (Slot, 1);
+       s->key = key;
+       s->value = value;
+       s->next = hash->table [hashcode];
+       hash->table [hashcode] = s;
+       hash->in_use++;
+}
+
+guint
+g_hash_table_size (GHashTable *hash)
+{
+       g_return_if_fail (hash != NULL);
+       
+       return hash->in_use;
+}
+
+gpointer
+g_hash_table_lookup (GHashTable *hash, gconstpointer key)
+{
+       gpointer orig_key, value;
+       
+       if (g_hash_table_lookup_extended (hash, key, &orig_key, &value))
+               return value;
+       else
+               return NULL;
+}
+
+gboolean
+g_hash_table_lookup_extended (GHashTable *hash, gconstpointer key, gpointer *orig_key, gpointer *value)
+{
+       GEqualFunc equal;
+       Slot *s;
+       guint hashcode;
+       
+       g_return_if_fail (hash != NULL);
+       equal = hash->key_equal_func;
+
+       hashcode = ((*hash->hash_func) (key)) % hash->table_size;       
+       for (s = hash->table [hashcode]; s != NULL; s = s->next){
+               if ((*equal)(s->key, key)){
+                       *orig_key = s->key;
+                       *value = s->value;
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+void
+g_hash_table_foreach (GHashTable *hash, GHFunc func, gpointer user_data)
+{
+       int i;
+       
+       g_return_if_fail (hash != NULL);
+       g_return_if_fail (func != NULL);
+
+       for (i = 0; i < hash->table_size; i++){
+               Slot *s;
+
+               for (s = hash->table [i]; s != NULL; s = s->next)
+                       (*func)(s->key, s->value, user_data);
+       }
+}
+
+gpointer
+g_hash_table_find (GHashTable *hash, GHRFunc predicate, gpointer user_data)
+{
+       int i;
+       
+       g_return_if_fail (hash != NULL);
+       g_return_if_fail (predicate != NULL);
+
+       for (i = 0; i < hash->table_size; i++){
+               Slot *s;
+
+               for (s = hash->table [i]; s != NULL; s = s->next)
+                       if ((*predicate)(s->key, s->value, user_data))
+                               return;
+       }
+}
+
+gboolean
+g_hash_table_remove (GHashTable *hash, gconstpointer key)
+{
+       GEqualFunc equal;
+       Slot *s, **last;
+       guint hashcode;
+       
+       g_return_val_if_fail (hash != NULL, FALSE);
+       equal = hash->key_equal_func;
+
+       hashcode = ((*hash->hash_func)(key)) % hash->table_size;
+       last = &hash->table [hashcode];
+       for (s = *last; s != NULL; s = s->next){
+               if ((*equal)(s->key, key)){
+                       if (hash->key_destroy_func != NULL)
+                               (*hash->key_destroy_func)(s->key);
+                       if (hash->value_destroy_func != NULL)
+                               (*hash->value_destroy_func)(s->value);
+                       *last = s->next;
+                       g_free (s);
+                       hash->in_use--;
+                       return TRUE;
+               }
+               last = &s;
+       }
+       return FALSE;
+}
+
+guint
+g_hash_table_foreach_remove (GHashTable *hash, GHRFunc func, gpointer user_data)
+{
+       int i;
+       int count = 0;
+       
+       g_return_val_if_fail (hash != NULL, 0);
+       g_return_val_if_fail (func != NULL, 0);
+
+       for (i = 0; i < hash->table_size; i++){
+               Slot *s, **last;
+
+               last = &hash->table [i];
+               for (s = hash->table [i]; s != NULL; s = s->next){
+                       if ((*func)(s->key, s->value, user_data)){
+                               if (hash->key_destroy_func != NULL)
+                                       (*hash->key_destroy_func)(s->key);
+                               if (hash->value_destroy_func != NULL)
+                                       (*hash->value_destroy_func)(s->value);
+                               *last = s->next;
+                               g_free (s);
+                               hash->in_use--;
+                               count++;
+                       }
+               }
+       }
+       if (count > 0)
+               rehash (hash);
+}
+
+void
+g_hash_table_destroy (GHashTable *hash)
+{
+       int i;
+       
+       g_return_if_fail (hash != NULL);
 
-       table = hash->table;
-       free_index = -1;
+       for (i = 0; i < hash->table_size; i++){
+               Slot *s, *next;
 
-       for (i = 0; i < size; i++){
-               int indx = (int) (spot % size);
-               entry = &table [indx];
+               for (s = hash->table [i]; s != NULL; s = next){
+                       next = s->next;
+                       
+                       if (hash->key_destroy_func != NULL)
+                               (*hash->key_destroy_func)(s->key);
+                       if (hash->value_destroy_func != NULL)
+                               (*hash->value_destroy_func)(s->value);
+                       g_free (s);
+               }
        }
+       g_free (hash->table);
        
+       g_free (hash);
+}
+
+gboolean
+g_direct_equal (gconstpointer v1, gconstpointer v2)
+{
+       return v1 == v2;
+}
+
+guint
+g_direct_hash (gconstpointer v1)
+{
+       return GPOINTER_TO_UINT (v1);
+}
+
+gboolean
+g_int_equal (gconstpointer v1, gconstpointer v2)
+{
+       return GPOINTER_TO_INT (v1) == GPOINTER_TO_INT (v2);
+}
+
+guint
+g_int_hash (gconstpointer v1)
+{
+       return GPOINTER_TO_UINT(v1);
+}
+
+gboolean
+g_str_equal (gconstpointer v1, gconstpointer v2)
+{
+       return strcmp (v1, v2) == 0;
+}
+
+guint
+g_str_hash (gconstpointer v1)
+{
+       guint hash = 0;
+       char *p = (char *) v1;
+
+       while (*p++)
+               hash = (hash << 5) - (hash + *p);
+
+       return hash;
 }
index e9bdb5b189586ab89df8a872d3bd81501208c973..24d552465a96e327869bfffa6f71ed7d82a7fc87 100644 (file)
 #define G_MAXINT32           0xf7777777
 #define G_MININT32           0x80000000
 
+#define GPOINTER_TO_INT(ptr)   ((int)(ptr))
+#define GPOINTER_TO_UINT(ptr)  ((uint)(ptr))
+#define GINT_TO_POINTER(v)     ((gpointer) (v))
+#define GUINT_TO_POINTER(v)    ((gpointer) (v))
+
 /*
  * Allocation
  */
@@ -40,15 +45,37 @@ typedef unsigned char  guchar;
  * Precondition macros
  */
 #define g_return_if_fail(x)  do { if (!(x)) { printf ("%s:%d: assertion %s failed", __FILE__, __LINE__, #x); return; } } while (0) ;
+#define g_return_val_if_fail(x,e)  do { if (!(x)) { printf ("%s:%d: assertion %s failed", __FILE__, __LINE__, #x); return (e); } } while (0) ;
 
 /*
  * Hashtables
  */
 typedef struct _GHashTable GHashTable;
+typedef void     (*GHFunc)         (gpointer key, gpointer value, gpointer user_data);
+typedef gboolean (*GHRFunc)        (gpointer key, gpointer value, gpointer user_data);
+typedef void     (*GDestroyNotify) (gpointer data);
+typedef guint    (*GHashFunc)      (gconstpointer key);
+typedef gboolean (*GEqualFunc)     (gconstpointer a, gconstpointer b);
+
+GHashTable     *g_hash_table_new             (GHashFunc hash_func, GEqualFunc key_equal_func);
+void            g_hash_table_insert_replace  (GHashTable *hash, gpointer key, gpointer value, gboolean replace);
+guint           g_hash_table_size            (GHashTable *hash);
+gpointer        g_hash_table_lookup          (GHashTable *hash, gconstpointer key);
+gboolean        g_hash_table_lookup_extended (GHashTable *hash, gconstpointer key, gpointer *orig_key, gpointer *value);
+void            g_hash_table_foreach         (GHashTable *hash, GHFunc func, gpointer user_data);
+gpointer        g_hash_table_find            (GHashTable *hash, GHRFunc predicate, gpointer user_data);
+gboolean        g_hash_table_remove          (GHashTable *hash, gconstpointer key);
+guint           g_hash_table_foreach_remove  (GHashTable *hash, GHRFunc func, gpointer user_data);
+void            g_hash_table_destroy         (GHashTable *hash);
 
-typedef guint    (*GHashFunc)  (gconstpointer key);
-typedef gboolean (*GEqualFunc) (gconstpointer a, gconstpointer b);
+#define g_hash_table_insert(h,k,v)    g_hash_table_insert_replace ((h),(k),(v),FALSE)
+#define g_hash_table_replace(h,k,v)   g_hash_table_insert_replace ((h),(k),(v),TRUE)
 
-GHashTable     *g_hash_table_new (GHashFunc hash_func, GEqualFunc key_equal_func);
+gboolean g_direct_equal (gconstpointer v1, gconstpointer v2);
+guint    g_direct_hash  (gconstpointer v1);
+gboolean g_int_equal    (gconstpointer v1, gconstpointer v2);
+guint    g_int_hash     (gconstpointer v1);
+gboolean g_str_equal    (gconstpointer v1, gconstpointer v2);
+guint    g_str_hash     (gconstpointer v1);
 
 #endif
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..97e680edd5520929c30ccb0c0247257fd611b1d8 100644 (file)
@@ -0,0 +1,10 @@
+noinst_PROGRAMS = test
+
+test_SOURCES = \
+       test.h          \
+       driver.c        \
+       hash.c
+
+INCLUDES = -I../src
+
+test_LDADD = -L../src -leglib
\ No newline at end of file
diff --git a/eglib/test/driver.c b/eglib/test/driver.c
new file mode 100644 (file)
index 0000000..3ff386c
--- /dev/null
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+#include "test.h"
+
+int main ()
+{
+       printf ("hashtable\n");
+       test ("t1", hash_t1);
+}
diff --git a/eglib/test/hash.c b/eglib/test/hash.c
new file mode 100644 (file)
index 0000000..a68b583
--- /dev/null
@@ -0,0 +1,48 @@
+#include <stdio.h>
+#include <glib.h>
+#include "test.h"
+
+int foreach_count = 0;
+int foreach_fail = 0;
+
+void foreach (gpointer key, gpointer value, gpointer user_data)
+{
+       foreach_count++;
+       if (GPOINTER_TO_INT (user_data) != 'a')
+               foreach_fail = 1;
+}
+
+char *hash_t1 (void)
+{
+       GHashTable *t = g_hash_table_new (g_str_hash, g_str_equal);
+
+       g_hash_table_insert (t, "hello", "world");
+       g_hash_table_insert (t, "my", "god");
+
+       g_hash_table_foreach (t, foreach, GINT_TO_POINTER('a'));
+       if (foreach_count != 2)
+               return "did not find all keys";
+       if (foreach_fail)
+               return "failed to pass the user-data to foreach";
+       
+       if (!g_hash_table_remove (t, "my"))
+               return "did not find known key";
+       if (g_hash_table_size (t) != 1)
+               return "unexpected size";
+       g_hash_table_insert(t, "hello", "moon");
+       if (strcmp (g_hash_table_lookup (t, "hello"), "moon") != 0)
+               return "did not replace world with moon";
+               
+       if (!g_hash_table_remove (t, "hello"))
+               return "did not find known key";
+       if (g_hash_table_size (t) != 0)
+               return "unexpected size";
+       g_hash_table_destroy (t);
+
+       return NULL;
+}
+
+char *hash_t2 (void)
+{
+       
+}
diff --git a/eglib/test/test.h b/eglib/test/test.h
new file mode 100644 (file)
index 0000000..e9d5ca6
--- /dev/null
@@ -0,0 +1 @@
+#define test(name,func) do { char *r; printf ("  test: %s: ", name); fflush (stdout);r = func (); if (r)printf ("failure (%s)\n",r); else printf ("OK\n");} while (0);