Bladeren bron

2006-08-16 Miguel de Icaza <[email protected]>

	* test: Add test driver, and initial tests. 

2006-08-15  Miguel de Icaza  <[email protected]>

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


svn path=/trunk/mono/; revision=63798
Miguel de Icaza 19 jaren geleden
bovenliggende
commit
62de47ab09
11 gewijzigde bestanden met toevoegingen van 385 en 41 verwijderingen
  1. 9 0
      eglib/ChangeLog
  2. 2 0
      eglib/configure.in
  3. 3 2
      eglib/src/Makefile
  4. 3 1
      eglib/src/Makefile.am
  5. 2 1
      eglib/src/Makefile.in
  6. 268 34
      eglib/src/ghashtable.c
  7. 30 3
      eglib/src/glib.h
  8. 10 0
      eglib/test/Makefile.am
  9. 9 0
      eglib/test/driver.c
  10. 48 0
      eglib/test/hash.c
  11. 1 0
      eglib/test/test.h

+ 9 - 0
eglib/ChangeLog

@@ -0,0 +1,9 @@
+2006-08-16  Miguel de Icaza  <[email protected]>
+
+	* test: Add test driver, and initial tests. 
+
+2006-08-15  Miguel de Icaza  <[email protected]>
+
+	* src/ghashtable.c: Implement most of this, it is completely
+	untested at this point.
+

+ 2 - 0
eglib/configure.in

@@ -7,6 +7,8 @@ AM_MAINTAINER_MODE
 
 AC_PROG_CC
 AM_PROG_LIBTOOL
+CFLAGS='-g -O0'
+AC_SUBST(CFLAGS)
 
 AC_OUTPUT([
 Makefile

+ 3 - 2
eglib/src/Makefile

@@ -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:

+ 3 - 1
eglib/src/Makefile.am

@@ -3,4 +3,6 @@ noinst_LTLIBRARIES = libeglib.la
 libeglib_la_SOURCES = \
 	ghashtable.c
 
-INCLUDES = -I$(srcdir)
+INCLUDES = -I$(srcdir)
+
+libeglib_la_LIBADD = -lm

+ 2 - 1
eglib/src/Makefile.in

@@ -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:

+ 268 - 34
eglib/src/ghashtable.c

@@ -4,29 +4,31 @@
  * Author:
  *   Miguel de Icaza ([email protected])
  *
- * This is based on the System.Collections.Hashtable from Mono
- * implemented originally by Sergey Chaban ([email protected])
- *
  * (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;
 }

+ 30 - 3
eglib/src/glib.h

@@ -14,6 +14,11 @@
 #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

+ 10 - 0
eglib/test/Makefile.am

@@ -0,0 +1,10 @@
+noinst_PROGRAMS = test
+
+test_SOURCES = \
+	test.h		\
+	driver.c	\
+	hash.c
+
+INCLUDES = -I../src
+
+test_LDADD = -L../src -leglib

+ 9 - 0
eglib/test/driver.c

@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+#include "test.h"
+
+int main ()
+{
+	printf ("hashtable\n");
+	test ("t1", hash_t1);
+}

+ 48 - 0
eglib/test/hash.c

@@ -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)
+{
+	
+}

+ 1 - 0
eglib/test/test.h

@@ -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);