2002-11-23 Havoc Pennington <hp@pobox.com>
authorHavoc Pennington <hp@redhat.com>
Sat, 23 Nov 2002 06:53:37 +0000 (06:53 +0000)
committerHavoc Pennington <hp@redhat.com>
Sat, 23 Nov 2002 06:53:37 +0000 (06:53 +0000)
* configure.in: pile on more warning flags if using gcc

* Doxyfile.in (EXTRACT_STATIC): set to NO, so we don't have
to document static functions

* configure.in: add summary to end of configure so it
looks nice and attractive

* dbus/dbus-hash.c: finish implementation and write unit
tests and docs

* configure.in: add --enable-tests to enable unit tests

* dbus/dbus-test.c: test program to run unit tests
for all files in dbus/*, initially runs a test for
dbus-hash.c

* dbus/dbus-internals.h: file to hold some internal utility stuff

15 files changed:
ChangeLog
Doxyfile.in
configure.in
dbus/Makefile.am
dbus/dbus-hash.c
dbus/dbus-hash.h
dbus/dbus-macros.h
dbus/dbus-memory.c
dbus/dbus-memory.h
dbus/dbus-message.c
dbus/dbus-test.c [new file with mode: 0644]
dbus/dbus-test.h [new file with mode: 0644]
dbus/dbus-types.h
dbus/dbus.h
test/Makefile.am

index 170a60d..9ad9573 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2002-11-23  Havoc Pennington  <hp@pobox.com>
+
+       * configure.in: pile on more warning flags if using gcc
+
+       * Doxyfile.in (EXTRACT_STATIC): set to NO, so we don't have 
+       to document static functions
+
+       * configure.in: add summary to end of configure so it 
+       looks nice and attractive
+
+       * dbus/dbus-hash.c: finish implementation and write unit 
+       tests and docs
+
+       * configure.in: add --enable-tests to enable unit tests
+
+       * dbus/dbus-test.c: test program to run unit tests 
+       for all files in dbus/*, initially runs a test for 
+       dbus-hash.c
+       
+       * dbus/dbus-internals.h: file to hold some internal utility stuff
+
 2002-11-22  Havoc Pennington  <hp@redhat.com>
 
        * dbus/dbus-hash.c: copy in Tcl hash table, not yet 
index d7b7019..c686f4b 100644 (file)
@@ -9,7 +9,7 @@ OUTPUT_DIRECTORY       = doc/api
 OUTPUT_LANGUAGE        = English
 EXTRACT_ALL            = NO
 EXTRACT_PRIVATE        = NO
-EXTRACT_STATIC         = YES
+EXTRACT_STATIC         = NO
 HIDE_UNDOC_MEMBERS     = NO
 HIDE_UNDOC_CLASSES     = NO
 BRIEF_MEMBER_DESC      = YES
index c663825..d8ba3c6 100644 (file)
@@ -21,8 +21,17 @@ AC_ISC_POSIX
 AC_HEADER_STDC
 AM_PROG_LIBTOOL
 
-AC_ARG_ENABLE(qt,   [  --disable-qt      disable Qt-friendly client library],enable_qt=no,enable_qt=yes)
-AC_ARG_ENABLE(glib, [  --disable-glib    disable GLib-friendly client library],enable_glib=no,enable_glib=yes)
+AC_ARG_ENABLE(qt,      [  --disable-qt      disable Qt-friendly client library],enable_qt=no,enable_qt=yes)
+AC_ARG_ENABLE(glib,    [  --disable-glib    disable GLib-friendly client library],enable_glib=no,enable_glib=yes)
+AC_ARG_ENABLE(tests,   [  --enable-tests enable unit test code],enable_tests=yes,enable_tests=no)
+AC_ARG_ENABLE(ansi,    [  --enable-ansi enable -ansi -pedantic gcc flags],enable_ansi=yes,enable_ansi=no)
+
+dnl DBUS_BUILD_TESTS controls unit tests built in to .c files 
+dnl and also some stuff in the test/ subdir
+AM_CONDITIONAL(DBUS_BUILD_TESTS, test x$enable_tests = xyes)
+if test x$enable_tests = xyes; then
+    AC_DEFINE(DBUS_BUILD_TESTS,1,[Build test code])
+fi
 
 changequote(,)dnl
 if test "x$GCC" = "xyes"; then
@@ -30,6 +39,53 @@ if test "x$GCC" = "xyes"; then
   *[\ \        ]-Wall[\ \      ]*) ;;
   *) CFLAGS="$CFLAGS -Wall" ;;
   esac
+
+  case " $CFLAGS " in
+  *[\ \        ]-Wchar-subscripts[\ \  ]*) ;;
+  *) CFLAGS="$CFLAGS -Wchar-subscripts" ;;
+  esac
+
+  case " $CFLAGS " in
+  *[\ \        ]-Wmissing-declarations[\ \     ]*) ;;
+  *) CFLAGS="$CFLAGS -Wmissing-declarations" ;;
+  esac
+
+  case " $CFLAGS " in
+  *[\ \        ]-Wmissing-prototypes[\ \       ]*) ;;
+  *) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
+  esac
+
+  case " $CFLAGS " in
+  *[\ \        ]-Wnested-externs[\ \   ]*) ;;
+  *) CFLAGS="$CFLAGS -Wnested-externs" ;;
+  esac
+
+  case " $CFLAGS " in
+  *[\ \        ]-Wpointer-arith[\ \    ]*) ;;
+  *) CFLAGS="$CFLAGS -Wpointer-arith" ;;
+  esac
+
+  case " $CFLAGS " in
+  *[\ \        ]-Wcast-align[\ \       ]*) ;;
+  *) CFLAGS="$CFLAGS -Wcast-align" ;;
+  esac
+
+  case " $CFLAGS " in
+  *[\ \        ]-Wsign-compare[\ \     ]*) ;;
+  *) CFLAGS="$CFLAGS -Wsign-compare" ;;
+  esac
+
+  if test "x$enable_ansi" = "xyes"; then
+    case " $CFLAGS " in
+    *[\ \      ]-ansi[\ \      ]*) ;;
+    *) CFLAGS="$CFLAGS -ansi" ;;
+    esac
+
+    case " $CFLAGS " in
+    *[\ \      ]-pedantic[\ \  ]*) ;;
+    *) CFLAGS="$CFLAGS -pedantic" ;;
+    esac
+  fi
 fi
 changequote([,])dnl
 
@@ -65,3 +121,28 @@ test/Makefile
 doc/Makefile
 dbus-1.0.pc
 ])
+
+dnl ==========================================================================
+echo "
+                    D-BUS $VERSION
+                  ==============
+
+       prefix:                 ${prefix}
+       source code location:   ${srcdir}
+       compiler:               ${CC}
+
+        Building unit tests:    ${enable_tests}
+        Building Qt bindings:   ${enable_qt}
+        Building GLib bindings: ${enable_glib}
+"
+
+if test x$enable_tests = xyes; then
+        echo "NOTE: building with unit tests increases the size of the installed library"
+fi
+if test x$enable_qt = xyes; then
+        echo "NOTE: Qt bindings don't actually exist yet"
+fi
+if test x$enable_glib = xyes; then
+        echo "NOTE: GLib bindings don't actually exist yet"
+fi
+
index 489eedf..56b519e 100644 (file)
@@ -24,9 +24,24 @@ noinst_LTLIBRARIES=libdbus-convenience.la
 
 libdbus_convenience_la_SOURCES=                        \
        dbus-hash.c                             \
-       dbus-hash.h
+       dbus-hash.h                             \
+       dbus-internals.c                        \
+       dbus-internals.h
 
 libdbus_1_la_LIBADD=  $(DBUS_CLIENT_LIBS) libdbus-convenience.la
 ## don't export symbols that start with "_" (we use this 
 ## convention for internal symbols)
 libdbus_1_la_LDFLAGS= -export-symbols-regex "^[[^_]].*"
+
+if DBUS_BUILD_TESTS
+noinst_PROGRAMS=dbus-test
+
+dbus_test_SOURCES=                             \
+       dbus-test.c                             \
+       dbus-test.h
+
+dbus_test_LDADD= $(DBUS_CLIENT_LIBS) libdbus-convenience.la libdbus-1.la
+
+## so that "make check" works
+TESTS=dbus-test
+endif
index a8a231e..1527251 100644 (file)
  * accordance with the terms specified in this license.
  */
 
-#include "dbus-memory.h"
+#include "dbus-hash.h"
+#include "dbus-internals.h"
 
-typedef struct DBusHashEntry DBusHashEntry;
-
-
-#if 0
-
-/*
- * Forward declaration of Tcl_HashTable.  Needed by some C++ compilers
- * to prevent errors when the forward reference to Tcl_HashTable is
- * encountered in the Tcl_HashEntry structure.
+/**
+ * @defgroup DBusHashTable Hash table
+ * @ingroup  DBusInternals
+ * @brief DBusHashTable data structure
+ *
+ * Types and functions related to DBusHashTable.
  */
 
-#ifdef __cplusplus
-struct Tcl_HashTable;
-#endif
+/**
+ * @defgroup DBusHashTableInternals Hash table implementation details
+ * @ingroup  DBusInternals
+ * @brief DBusHashTable implementation details
+ *
+ * The guts of DBusHashTable.
+ *
+ * @{
+ */
 
-/*
- * Structure definition for an entry in a hash table.  No-one outside
- * Tcl should access any of these fields directly;  use the macros
- * defined below.
+/**
+ * When there are this many entries per bucket, on average, rebuild
+ * the hash table to make it larger.
+ */
+#define REBUILD_MULTIPLIER  3
+
+/**
+ * Takes a preliminary integer hash value and produces an index into a
+ * hash tables bucket list.  The idea is to make it so that
+ * preliminary values that are arbitrarily similar will end up in
+ * different buckets.  The hash function was taken from a
+ * random-number generator. (This is used to hash integers.)
  */
+#define RANDOM_INDEX(table, i) \
+    (((((long) (i))*1103515245) >> (table)->down_shift) & (table)->mask)
 
-typedef struct Tcl_HashEntry {
-    struct Tcl_HashEntry *nextPtr;     /* Pointer to next entry in this
-                                        * hash bucket, or NULL for end of
-                                        * chain. */
-    struct Tcl_HashTable *tablePtr;    /* Pointer to table containing entry. */
-    struct Tcl_HashEntry **bucketPtr;  /* Pointer to bucket that points to
-                                        * first entry in this entry's chain:
-                                        * used for deleting the entry. */
-    ClientData clientData;             /* Application stores something here
-                                        * with Tcl_SetHashValue. */
-    union {                            /* Key has one of these forms: */
-       char *oneWordValue;             /* One-word value for key. */
-       int words[1];                   /* Multiple integer words for key.
-                                        * The actual size will be as large
-                                        * as necessary for this table's
-                                        * keys. */
-       char string[4];                 /* String for key.  The actual size
-                                        * will be as large as needed to hold
-                                        * the key. */
-    } key;                             /* MUST BE LAST FIELD IN RECORD!! */
-} Tcl_HashEntry;
-
-/*
- * Structure definition for a hash table.  Must be in tcl.h so clients
- * can allocate space for these structures, but clients should never
- * access any fields in this structure.
+/**
+ * Initial number of buckets in hash table (hash table statically
+ * allocates its buckets for this size and below).
  */
+#define DBUS_SMALL_HASH_TABLE 4
 
-#define TCL_SMALL_HASH_TABLE 4
-typedef struct Tcl_HashTable {
-    Tcl_HashEntry **buckets;           /* Pointer to bucket array.  Each
-                                        * element points to first entry in
-                                        * bucket's hash chain, or NULL. */
-    Tcl_HashEntry *staticBuckets[TCL_SMALL_HASH_TABLE];
-                                       /* Bucket array used for small tables
-                                        * (to avoid mallocs and frees). */
-    int numBuckets;                    /* Total number of buckets allocated
-                                        * at **bucketPtr. */
-    int numEntries;                    /* Total number of entries present
-                                        * in table. */
-    int rebuildSize;                   /* Enlarge table when numEntries gets
-                                        * to be this large. */
-    int downShift;                     /* Shift count used in hashing
-                                        * function.  Designed to use high-
-                                        * order bits of randomized keys. */
-    int mask;                          /* Mask value used in hashing
-                                        * function. */
-    int keyType;                       /* Type of keys used in this table. 
-                                        * It's either TCL_STRING_KEYS,
-                                        * TCL_ONE_WORD_KEYS, or an integer
-                                        * giving the number of ints that
-                                         * is the size of the key.
-                                        */
-    Tcl_HashEntry *(*findProc) _ANSI_ARGS_((struct Tcl_HashTable *tablePtr,
-           CONST char *key));
-    Tcl_HashEntry *(*createProc) _ANSI_ARGS_((struct Tcl_HashTable *tablePtr,
-           CONST char *key, int *newPtr));
-} Tcl_HashTable;
-
-/*
- * Structure definition for information used to keep track of searches
- * through hash tables:
+/**
+ * Typedef for DBusHashEntry
  */
+typedef struct DBusHashEntry DBusHashEntry;
 
-typedef struct Tcl_HashSearch {
-    Tcl_HashTable *tablePtr;           /* Table being searched. */
-    int nextIndex;                     /* Index of next bucket to be
-                                        * enumerated after present one. */
-    Tcl_HashEntry *nextEntryPtr;       /* Next entry to be enumerated in the
-                                        * the current bucket. */
-} Tcl_HashSearch;
+/**
+ * A single entry (key-value pair) in the hash table.
+ * Internal to hash table implementation.
+ */
+struct DBusHashEntry
+{
+  DBusHashEntry *next;    /**< Pointer to next entry in this
+                           * hash bucket, or #NULL for end of
+                           * chain.
+                           */
+  void *key;              /**< Hash key */
+  void *value;            /**< Hash value */
+};
+
+/**
+ * Function used to find and optionally create a hash entry.
+ */
+typedef DBusHashEntry* (* DBusFindEntryFunction) (DBusHashTable   *table,
+                                                  void            *key,
+                                                  dbus_bool_t      create_if_not_found,
+                                                  DBusHashEntry ***bucket);
 
+/**
+ * Hash table internal members.
+ */
+struct DBusHashTable {
+  int refcount;                       /**< Reference count */
+  
+  DBusHashEntry **buckets;            /**< Pointer to bucket array.  Each
+                                       * element points to first entry in
+                                       * bucket's hash chain, or #NULL.
+                                       */
+  DBusHashEntry *static_buckets[DBUS_SMALL_HASH_TABLE];
+                                       /**< Bucket array used for small tables
+                                        * (to avoid mallocs and frees).
+                                        */
+  int n_buckets;                       /**< Total number of buckets allocated
+                                        * at **buckets.
+                                        */
+  int n_entries;                       /**< Total number of entries present
+                                        * in table.
+                                        */
+  int rebuild_size;                    /**< Enlarge table when numEntries gets
+                                        * to be this large.
+                                        */
+  int down_shift;                      /**< Shift count used in hashing
+                                        * function.  Designed to use high-
+                                        * order bits of randomized keys.
+                                        */
+  int mask;                            /**< Mask value used in hashing
+                                        * function.
+                                        */
+  DBusHashType key_type;               /**< Type of keys used in this table */
+
+
+  DBusFindEntryFunction find_function; /**< Function for finding entries */
+
+  DBusFreeFunction free_key_function;   /**< Function to free keys */
+  DBusFreeFunction free_value_function; /**< Function to free values */
+};
+
+/**
+ * Internals of DBusHashIter.
+ */
+typedef struct
+{
+  DBusHashTable *table;     /**< Pointer to table containing entry. */
+  DBusHashEntry **bucket;   /**< Pointer to bucket that points to
+                             * first entry in this entry's chain:
+                             * used for deleting the entry.
+                             */
+  DBusHashEntry *entry;      /**< Current hash entry */
+  DBusHashEntry *next_entry; /**< Next entry to be iterated onto in current bucket */
+  int next_bucket;           /**< index of next bucket */
+  int n_entries_on_init;     /**< used to detect table resize since initialization */
+} DBusRealHashIter;
+
+static DBusHashEntry* find_int_function    (DBusHashTable   *table,
+                                            void            *key,
+                                            dbus_bool_t      create_if_not_found,
+                                            DBusHashEntry ***bucket);
+static DBusHashEntry* find_string_function (DBusHashTable   *table,
+                                            void            *key,
+                                            dbus_bool_t      create_if_not_found,
+                                            DBusHashEntry ***bucket);
+static unsigned int   string_hash          (const char      *str);
+static void           rebuild_table        (DBusHashTable   *table);
+static DBusHashEntry* alloc_entry          (DBusHashTable   *table);
+static void           remove_entry         (DBusHashTable   *table,
+                                            DBusHashEntry  **bucket,
+                                            DBusHashEntry   *entry);
+static void           free_entry           (DBusHashTable   *table,
+                                            DBusHashEntry   *entry);
+
+/** }@ */
+
+/**
+ * @addtogroup DBusHashTable
+ * @{
+ */
 
-/*
- * When there are this many entries per bucket, on average, rebuild
- * the hash table to make it larger.
+/**
+ * @typedef DBusHashIter
+ *
+ * Public opaque hash table iterator object.
  */
 
-#define REBUILD_MULTIPLIER     3
+/**
+ * @typedef DBusHashTable
+ *
+ * Public opaque hash table object.
+ */
 
+/**
+ * @typedef DBusHashType
+ *
+ * Indicates the type of a key in the hash table.
+ */
 
-/*
- * The following macro takes a preliminary integer hash value and
- * produces an index into a hash tables bucket list.  The idea is
- * to make it so that preliminary values that are arbitrarily similar
- * will end up in different buckets.  The hash function was taken
- * from a random-number generator.
+/**
+ * Constructs a new hash table. Should be freed with
+ * _dbus_hash_table_unref(). If memory cannot be
+ * allocated for the hash table, returns #NULL.
+ *
+ * @param type the type of hash key to use.
+ * @param key_free_function function to free hash keys.
+ * @param value_free_function function to free hash values.
+ * @returns a new DBusHashTable or #NULL if no memory.
  */
+DBusHashTable*
+_dbus_hash_table_new (DBusHashType     type,
+                      DBusFreeFunction key_free_function,
+                      DBusFreeFunction value_free_function)
+{
+  DBusHashTable *table;
+
+  table = dbus_new0 (DBusHashTable, 1);
+  if (table == NULL)
+    return NULL;
+  
+  table->refcount = 1;
+
+  _dbus_assert (DBUS_SMALL_HASH_TABLE == _DBUS_N_ELEMENTS (table->static_buckets));
+  
+  table->buckets = table->static_buckets;  
+  table->n_buckets = DBUS_SMALL_HASH_TABLE;
+  table->n_entries = 0;
+  table->rebuild_size = DBUS_SMALL_HASH_TABLE * REBUILD_MULTIPLIER;
+  table->down_shift = 28;
+  table->mask = 3;
+  table->key_type = type;
+  
+  switch (table->key_type)
+    {
+    case DBUS_HASH_INT:
+      table->find_function = find_int_function;
+      break;
+    case DBUS_HASH_STRING:
+      table->find_function = find_string_function;
+      break;
+    default:
+      _dbus_assert_not_reached ("Unknown hash table type");
+      break;
+    }
 
-#define RANDOM_INDEX(tablePtr, i) \
-    (((((long) (i))*1103515245) >> (tablePtr)->downShift) & (tablePtr)->mask)
+  table->free_key_function = key_free_function;
+  table->free_value_function = value_free_function;
 
-/*
- * Procedure prototypes for static procedures in this file:
- */
+  return table;
+}
 
-static Tcl_HashEntry * ArrayFind (Tcl_HashTable *tablePtr,
-                                   CONST char *key);
-static Tcl_HashEntry * ArrayCreate (Tcl_HashTable *tablePtr,
-                                     CONST char *key, int *newPtr);
-static Tcl_HashEntry * BogusFind (Tcl_HashTable *tablePtr,
-                                   CONST char *key);
-static Tcl_HashEntry * BogusCreate (Tcl_HashTable *tablePtr,
-                                     CONST char *key, int *newPtr);
-static unsigned int    HashString (CONST char *string);
-static void            RebuildTable (Tcl_HashTable *tablePtr);
-static Tcl_HashEntry * StringFind (Tcl_HashTable *tablePtr,
-                                    CONST char *key);
-static Tcl_HashEntry * StringCreate (Tcl_HashTable *tablePtr,
-                                      CONST char *key, int *newPtr);
-static Tcl_HashEntry * OneWordFind (Tcl_HashTable *tablePtr,
-                                     CONST char *key);
-static Tcl_HashEntry * OneWordCreate (Tcl_HashTable *tablePtr,
-                                       CONST char *key, int *newPtr);
-\f
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_InitHashTable --
- *
- *     Given storage for a hash table, set up the fields to prepare
- *     the hash table for use.
- *
- * Results:
- *     None.
- *
- * Side effects:
- *     TablePtr is now ready to be passed to Tcl_FindHashEntry and
- *     Tcl_CreateHashEntry.
+
+/**
+ * Increments the reference count for a hash table.
  *
- *----------------------------------------------------------------------
+ * @param table the hash table to add a reference to.
  */
-
 void
-Tcl_InitHashTable(tablePtr, keyType)
-     register Tcl_HashTable *tablePtr; /* Pointer to table record, which
-                                        * is supplied by the caller. */
-     int keyType;                      /* Type of keys to use in table:
-                                        * TCL_STRING_KEYS, TCL_ONE_WORD_KEYS,
-                                        * or an integer >= 2. */
+_dbus_hash_table_ref (DBusHashTable *table)
 {
-#if (TCL_SMALL_HASH_TABLE != 4) 
-  panic("Tcl_InitHashTable: TCL_SMALL_HASH_TABLE is %d, not 4\n",
-        TCL_SMALL_HASH_TABLE);
-#endif
-    
-  tablePtr->buckets = tablePtr->staticBuckets;
-  tablePtr->staticBuckets[0] = tablePtr->staticBuckets[1] = 0;
-  tablePtr->staticBuckets[2] = tablePtr->staticBuckets[3] = 0;
-  tablePtr->numBuckets = TCL_SMALL_HASH_TABLE;
-  tablePtr->numEntries = 0;
-  tablePtr->rebuildSize = TCL_SMALL_HASH_TABLE*REBUILD_MULTIPLIER;
-  tablePtr->downShift = 28;
-  tablePtr->mask = 3;
-  tablePtr->keyType = keyType;
-  if (keyType == TCL_STRING_KEYS) {
-    tablePtr->findProc = StringFind;
-    tablePtr->createProc = StringCreate;
-  } else if (keyType == TCL_ONE_WORD_KEYS) {
-    tablePtr->findProc = OneWordFind;
-    tablePtr->createProc = OneWordCreate;
-  } else {
-    tablePtr->findProc = ArrayFind;
-    tablePtr->createProc = ArrayCreate;
-  };
+  table->refcount += 1;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DeleteHashEntry --
- *
- *     Remove a single entry from a hash table.
- *
- * Results:
- *     None.
- *
- * Side effects:
- *     The entry given by entryPtr is deleted from its table and
- *     should never again be used by the caller.  It is up to the
- *     caller to free the clientData field of the entry, if that
- *     is relevant.
+
+/**
+ * Decrements the reference count for a hash table,
+ * freeing the hash table if the count reaches zero.
  *
- *----------------------------------------------------------------------
+ * @param table the hash table to remove a reference from.
  */
-
 void
-Tcl_DeleteHashEntry(entryPtr)
-     Tcl_HashEntry *entryPtr;
+_dbus_hash_table_unref (DBusHashTable *table)
 {
-  register Tcl_HashEntry *prevPtr;
-
-  if (*entryPtr->bucketPtr == entryPtr) {
-    *entryPtr->bucketPtr = entryPtr->nextPtr;
-  } else {
-    for (prevPtr = *entryPtr->bucketPtr; ; prevPtr = prevPtr->nextPtr) {
-      if (prevPtr == NULL) {
-        panic("malformed bucket chain in Tcl_DeleteHashEntry");
-      }
-      if (prevPtr->nextPtr == entryPtr) {
-        prevPtr->nextPtr = entryPtr->nextPtr;
-        break;
-      }
+  table->refcount -= 1;
+
+  if (table->refcount == 0)
+    {
+      DBusHashEntry *entry;
+      DBusHashEntry *next;
+      int i;
+
+      /* Free the entries in the table. */
+      
+      for (i = 0; i < table->n_buckets; i++)
+        {
+          entry = table->buckets[i];
+          while (entry != NULL)
+            {
+              next = entry->next;
+
+              free_entry (table, entry);
+              
+              entry = next;
+            }
+        }
+
+      /* Free the bucket array, if it was dynamically allocated. */
+      if (table->buckets != table->static_buckets)
+        dbus_free (table->buckets);
+
+      dbus_free (table);
     }
-  }
-  entryPtr->tablePtr->numEntries--;
-  ckfree((char *) entryPtr);
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_DeleteHashTable --
- *
- *     Free up everything associated with a hash table except for
- *     the record for the table itself.
- *
- * Results:
- *     None.
+
+static DBusHashEntry*
+alloc_entry (DBusHashTable *table)
+{
+  DBusHashEntry *entry;
+
+  entry = dbus_new0 (DBusHashEntry, 1);
+
+  return entry;
+}
+
+static void
+free_entry (DBusHashTable  *table,
+            DBusHashEntry  *entry)
+{
+  if (table->free_key_function)
+    (* table->free_key_function) (entry->key);
+  if (table->free_value_function)
+    (* table->free_value_function) (entry->value);
+              
+  dbus_free (entry);
+}
+
+static void
+remove_entry (DBusHashTable  *table,
+              DBusHashEntry **bucket,
+              DBusHashEntry  *entry)
+{
+  _dbus_assert (table != NULL);
+  _dbus_assert (bucket != NULL);
+  _dbus_assert (*bucket != NULL);  
+  _dbus_assert (entry != NULL);
+  
+  if (*bucket == entry)
+    *bucket = entry->next;
+  else
+    {
+      DBusHashEntry *prev;
+      prev = *bucket;
+
+      while (prev->next != entry)
+        prev = prev->next;      
+      
+      _dbus_assert (prev != NULL);
+
+      prev->next = entry->next;
+    }
+  
+  table->n_entries -= 1;
+  free_entry (table, entry);
+}
+
+/**
+ * Initializes a hash table iterator. To iterate over all entries in a
+ * hash table, use the following code (the printf assumes a hash
+ * from strings to strings obviously):
+ *
+ * @code
+ * DBusHashIter iter;
+ *
+ * _dbus_hash_iter_init (table, &iter);
+ * while (_dbus_hash_iter_next (&iter))
+ *   {
+ *      printf ("The first key is %s and value is %s\n",
+ *              _dbus_hash_iter_get_string_key (&iter),
+ *              _dbus_hash_iter_get_value (&iter));
+ *   }
+ * 
+ * 
+ * @endcode
  *
- * Side effects:
- *     The hash table is no longer useable.
+ * The iterator is initialized pointing "one before" the first hash
+ * entry. The first call to _dbus_hash_iter_next() moves it onto
+ * the first valid entry or returns #FALSE if the hash table is
+ * empty. Subsequent calls move to the next valid entry or return
+ * #FALSE if there are no more entries.
  *
- *----------------------------------------------------------------------
+ * Note that it is guaranteed to be safe to remove a hash entry during
+ * iteration, but it is not safe to add a hash entry.
+ * 
+ * @param table the hash table to iterate over.
+ * @param iter the iterator to initialize.
  */
-
 void
-Tcl_DeleteHashTable(tablePtr)
-     register Tcl_HashTable *tablePtr;         /* Table to delete. */
+_dbus_hash_iter_init (DBusHashTable *table,
+                      DBusHashIter  *iter)
 {
-  register Tcl_HashEntry *hPtr, *nextPtr;
-  int i;
+  DBusRealHashIter *real;
+  
+  _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+  
+  real = (DBusRealHashIter*) iter;
+
+  real->table = table;
+  real->bucket = NULL;
+  real->entry = NULL;
+  real->next_entry = NULL;
+  real->next_bucket = 0;
+  real->n_entries_on_init = table->n_entries;
+}
 
-  /*
-   * Free up all the entries in the table.
+/**
+ * Move the hash iterator forward one step, to the next hash entry.
+ * The documentation for _dbus_hash_iter_init() explains in more
+ * detail.
+ *
+ * @param iter the iterator to move forward.
+ * @returns #FALSE if there are no more entries to move to.
+ */
+dbus_bool_t
+_dbus_hash_iter_next (DBusHashIter  *iter)
+{
+  DBusRealHashIter *real;
+  
+  _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+  
+  real = (DBusRealHashIter*) iter;
+
+  /* if this assertion failed someone probably added hash entries
+   * during iteration, which is bad.
    */
+  _dbus_assert (real->n_entries_on_init >= real->table->n_entries);
+  
+  /* Remember that real->entry may have been deleted */
+  
+  while (real->next_entry == NULL)
+    {
+      if (real->next_bucket >= real->table->n_buckets)
+        {
+          /* invalidate iter and return false */
+          real->entry = NULL;
+          real->table = NULL;
+          real->bucket = NULL;
+          return FALSE;
+        }
 
-  for (i = 0; i < tablePtr->numBuckets; i++) {
-    hPtr = tablePtr->buckets[i];
-    while (hPtr != NULL) {
-      nextPtr = hPtr->nextPtr;
-      ckfree((char *) hPtr);
-      hPtr = nextPtr;
+      real->bucket = &(real->table->buckets[real->next_bucket]);
+      real->next_entry = *(real->bucket);
+      real->next_bucket += 1;
     }
-  }
 
-  /*
-   * Free up the bucket array, if it was dynamically allocated.
-   */
+  _dbus_assert (real->next_entry != NULL);
+  _dbus_assert (real->bucket != NULL);
+  
+  real->entry = real->next_entry;
+  real->next_entry = real->entry->next;
+  
+  return TRUE;
+}
 
-  if (tablePtr->buckets != tablePtr->staticBuckets) {
-    ckfree((char *) tablePtr->buckets);
-  }
+/**
+ * Removes the current entry from the hash table.
+ * If a key_free_function or value_free_function
+ * was provided to _dbus_hash_table_new(),
+ * frees the key and/or value for this entry.
+ *
+ * @param iter the hash table iterator.
+ */
+void
+_dbus_hash_iter_remove_entry (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
 
-  /*
-   * Arrange for panics if the table is used again without
-   * re-initialization.
-   */
+  real = (DBusRealHashIter*) iter;
 
-  tablePtr->findProc = BogusFind;
-  tablePtr->createProc = BogusCreate;
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+  _dbus_assert (real->bucket != NULL);
+  
+  remove_entry (real->table, real->bucket, real->entry);
+
+  real->entry = NULL; /* make it crash if you try to use this entry */
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_FirstHashEntry --
- *
- *     Locate the first entry in a hash table and set up a record
- *     that can be used to step through all the remaining entries
- *     of the table.
- *
- * Results:
- *     The return value is a pointer to the first entry in tablePtr,
- *     or NULL if tablePtr has no entries in it.  The memory at
- *     *searchPtr is initialized so that subsequent calls to
- *     Tcl_NextHashEntry will return all of the entries in the table,
- *     one at a time.
- *
- * Side effects:
- *     None.
+
+/**
+ * Gets the value of the current entry.
  *
- *----------------------------------------------------------------------
+ * @param iter the hash table iterator.
  */
-
-Tcl_HashEntry *
-Tcl_FirstHashEntry(tablePtr, searchPtr)
-     Tcl_HashTable *tablePtr;          /* Table to search. */
-     Tcl_HashSearch *searchPtr;                /* Place to store information about
-                                        * progress through the table. */
+void*
+_dbus_hash_iter_get_value (DBusHashIter *iter)
 {
-  searchPtr->tablePtr = tablePtr;
-  searchPtr->nextIndex = 0;
-  searchPtr->nextEntryPtr = NULL;
-  return Tcl_NextHashEntry(searchPtr);
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  return real->entry->value;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_NextHashEntry --
- *
- *     Once a hash table enumeration has been initiated by calling
- *     Tcl_FirstHashEntry, this procedure may be called to return
- *     successive elements of the table.
- *
- * Results:
- *     The return value is the next entry in the hash table being
- *     enumerated, or NULL if the end of the table is reached.
- *
- * Side effects:
- *     None.
+
+/**
+ * Sets the value of the current entry.
+ * If the hash table has a value_free_function
+ * it will be used to free the previous value.
+ * The hash table will own the passed-in value
+ * (it will not be copied).
  *
- *----------------------------------------------------------------------
+ * @param iter the hash table iterator.
+ * @param value the new value.
  */
-
-Tcl_HashEntry *
-Tcl_NextHashEntry(searchPtr)
-     register Tcl_HashSearch *searchPtr;       /* Place to store information about
-                                                 * progress through the table.  Must
-                                                 * have been initialized by calling
-                                                 * Tcl_FirstHashEntry. */
+void
+_dbus_hash_iter_set_value (DBusHashIter *iter,
+                           void         *value)
 {
-  Tcl_HashEntry *hPtr;
+  DBusRealHashIter *real;
 
-  while (searchPtr->nextEntryPtr == NULL) {
-    if (searchPtr->nextIndex >= searchPtr->tablePtr->numBuckets) {
-      return NULL;
-    }
-    searchPtr->nextEntryPtr =
-      searchPtr->tablePtr->buckets[searchPtr->nextIndex];
-    searchPtr->nextIndex++;
-  }
-  hPtr = searchPtr->nextEntryPtr;
-  searchPtr->nextEntryPtr = hPtr->nextPtr;
-  return hPtr;
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  if (real->table->free_value_function && value != real->entry->value)    
+    (* real->table->free_value_function) (real->entry->value);
+  
+  real->entry->value = value;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * Tcl_HashStats --
- *
- *     Return statistics describing the layout of the hash table
- *     in its hash buckets.
- *
- * Results:
- *     The return value is a malloc-ed string containing information
- *     about tablePtr.  It is the caller's responsibility to free
- *     this string.
- *
- * Side effects:
- *     None.
+
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_INT.
  *
- *----------------------------------------------------------------------
+ * @param iter the hash table iterator.
  */
-
-char *
-Tcl_HashStats(tablePtr)
-     Tcl_HashTable *tablePtr;          /* Table for which to produce stats. */
+int
+_dbus_hash_iter_get_int_key (DBusHashIter *iter)
 {
-#define NUM_COUNTERS 10
-  int count[NUM_COUNTERS], overflow, i, j;
-  double average, tmp;
-  register Tcl_HashEntry *hPtr;
-  char *result, *p;
+  DBusRealHashIter *real;
 
-  /*
-   * Compute a histogram of bucket usage.
-   */
+  real = (DBusRealHashIter*) iter;
 
-  for (i = 0; i < NUM_COUNTERS; i++) {
-    count[i] = 0;
-  }
-  overflow = 0;
-  average = 0.0;
-  for (i = 0; i < tablePtr->numBuckets; i++) {
-    j = 0;
-    for (hPtr = tablePtr->buckets[i]; hPtr != NULL; hPtr = hPtr->nextPtr) {
-      j++;
-    }
-    if (j < NUM_COUNTERS) {
-      count[j]++;
-    } else {
-      overflow++;
-    }
-    tmp = j;
-    average += (tmp+1.0)*(tmp/tablePtr->numEntries)/2.0;
-  }
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
 
-  /*
-   * Print out the histogram and a few other pieces of information.
-   */
+  return _DBUS_POINTER_TO_INT (real->entry->key);
+}
 
-  result = (char *) ckalloc((unsigned) ((NUM_COUNTERS*60) + 300));
-  sprintf(result, "%d entries in table, %d buckets\n",
-          tablePtr->numEntries, tablePtr->numBuckets);
-  p = result + strlen(result);
-  for (i = 0; i < NUM_COUNTERS; i++) {
-    sprintf(p, "number of buckets with %d entries: %d\n",
-            i, count[i]);
-    p += strlen(p);
-  }
-  sprintf(p, "number of buckets with %d or more entries: %d\n",
-          NUM_COUNTERS, overflow);
-  p += strlen(p);
-  sprintf(p, "average search distance for entry: %.1f", average);
-  return result;
+/**
+ * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_STRING
+ * @param iter the hash table iterator.
+ */
+const char*
+_dbus_hash_iter_get_string_key (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  return real->entry->key;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * HashString --
+
+/**
+ * A low-level but efficient interface for manipulating the hash
+ * table.  It's efficient because you can get, set, and optionally
+ * create the hash entry while only running the hash function one
+ * time.
  *
- *     Compute a one-word summary of a text string, which can be
- *     used to generate a hash index.
+ * Note that while calling _dbus_hash_iter_next() on the iterator
+ * filled in by this function may work, it's completely
+ * undefined which entries are after this iter and which
+ * are before it. So it would be silly to iterate using this
+ * iterator.
  *
- * Results:
- *     The return value is a one-word summary of the information in
- *     string.
+ * If the hash entry is created, its value will be initialized
+ * to all bits zero.
  *
- * Side effects:
- *     None.
+ * #FALSE may be returned due to memory allocation failure, or
+ * because create_if_not_found was #FALSE and the entry
+ * did not exist.
  *
- *----------------------------------------------------------------------
+ * For a hash table of type #DBUS_HASH_INT, cast the int
+ * key to the key parameter using #_DBUS_INT_TO_POINTER().
+ * 
+ * @param table the hash table.
+ * @param key the hash key.
+ * @param create_if_not_found if #TRUE, create the entry if it didn't exist.
+ * @param iter the iterator to initialize.
+ * @returns #TRUE if the hash entry now exists (and the iterator is thus valid).
  */
+dbus_bool_t
+_dbus_hash_iter_lookup (DBusHashTable *table,
+                        void          *key,
+                        dbus_bool_t    create_if_not_found,
+                        DBusHashIter  *iter)
+{
+  DBusRealHashIter *real;
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (sizeof (DBusHashIter) == sizeof (DBusRealHashIter));
+  
+  real = (DBusRealHashIter*) iter;
+
+  entry = (* table->find_function) (table, key, create_if_not_found, &bucket);
+
+  if (entry == NULL)
+    return FALSE;
+  
+  real->table = table;
+  real->bucket = bucket;
+  real->entry = entry;
+  real->next_entry = entry->next;
+  real->next_bucket = (bucket - table->buckets) + 1;
+  real->n_entries_on_init = table->n_entries; 
+
+  _dbus_assert (&(table->buckets[real->next_bucket-1]) == real->bucket);
+  
+  return TRUE;
+}
 
+static DBusHashEntry*
+add_entry (DBusHashTable   *table, 
+           unsigned int     idx,
+           void            *key,
+           DBusHashEntry ***bucket)
+{
+  DBusHashEntry  *entry;
+  DBusHashEntry **b;
+  
+  entry = alloc_entry (table);
+  if (entry == NULL)
+    {
+      if (bucket)
+        *bucket = NULL;
+      return NULL;
+    }
+  
+  entry->key = key;
+  
+  b = &(table->buckets[idx]);
+  entry->next = *b;
+  *b = entry;
+
+  if (bucket)
+    *bucket = b;
+  
+  table->n_entries += 1;
+  
+  if (table->n_entries >= table->rebuild_size)
+    rebuild_table (table);
+
+  return entry;
+}
+           
 static unsigned int
-HashString(string)
-     register CONST char *string;/* String from which to compute hash value. */
+string_hash (const char *str)
 {
   register unsigned int result;
   register int c;
@@ -549,535 +716,669 @@ HashString(string)
    */
 
   result = 0;
-  while (1) {
-    c = *string;
-    string++;
-    if (c == 0) {
-      break;
+  while (TRUE)
+    {
+      c = *str;
+      str++;
+      if (c == 0)
+        break;
+      
+      result += (result << 3) + c;
     }
-    result += (result<<3) + c;
-  }
+  
   return result;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * StringFind --
- *
- *     Given a hash table with string keys, and a string key, find
- *     the entry with a matching key.
- *
- * Results:
- *     The return value is a token for the matching entry in the
- *     hash table, or NULL if there was no matching entry.
- *
- * Side effects:
- *     None.
- *
- *----------------------------------------------------------------------
- */
 
-static Tcl_HashEntry *
-StringFind(tablePtr, key)
-     Tcl_HashTable *tablePtr;  /* Table in which to lookup entry. */
-     CONST char *key;          /* Key to use to find matching entry. */
+static DBusHashEntry*
+find_string_function (DBusHashTable   *table,
+                      void            *key,
+                      dbus_bool_t      create_if_not_found,
+                      DBusHashEntry ***bucket)
 {
-  register Tcl_HashEntry *hPtr;
-  register CONST char *p1, *p2;
-  int index;
-
-  index = HashString(key) & tablePtr->mask;
+  DBusHashEntry *entry;
+  unsigned int idx;
+
+  if (bucket)
+    *bucket = NULL;
+  
+  idx = string_hash (key) & table->mask;
+
+  /* Search all of the entries in this bucket. */
+  entry = table->buckets[idx];
+  while (entry != NULL)
+    {
+      if (strcmp (key, entry->key) == 0)
+        {
+          if (bucket)
+            *bucket = &(table->buckets[idx]);
+          return entry;
+        }
+      
+      entry = entry->next;
+    }
 
-  /*
-   * Search all of the entries in the appropriate bucket.
-   */
+  if (create_if_not_found)
+    entry = add_entry (table, idx, key, bucket);
 
-  for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
-       hPtr = hPtr->nextPtr) {
-    for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
-      if (*p1 != *p2) {
-        break;
-      }
-      if (*p1 == '\0') {
-        return hPtr;
-      }
-    }
-  }
-  return NULL;
+  return entry;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * StringCreate --
- *
- *     Given a hash table with string keys, and a string key, find
- *     the entry with a matching key.  If there is no matching entry,
- *     then create a new entry that does match.
- *
- * Results:
- *     The return value is a pointer to the matching entry.  If this
- *     is a newly-created entry, then *newPtr will be set to a non-zero
- *     value;  otherwise *newPtr will be set to 0.  If this is a new
- *     entry the value stored in the entry will initially be 0.
- *
- * Side effects:
- *     A new entry may be added to the hash table.
- *
- *----------------------------------------------------------------------
- */
 
-static Tcl_HashEntry *
-StringCreate(tablePtr, key, newPtr)
-     Tcl_HashTable *tablePtr;  /* Table in which to lookup entry. */
-     CONST char *key;          /* Key to use to find or create matching
-                                * entry. */
-     int *newPtr;              /* Store info here telling whether a new
-                                * entry was created. */
+static DBusHashEntry*
+find_int_function (DBusHashTable   *table,
+                   void            *key,
+                   dbus_bool_t      create_if_not_found,
+                   DBusHashEntry ***bucket)
 {
-  register Tcl_HashEntry *hPtr;
-  register CONST char *p1, *p2;
-  int index;
+  DBusHashEntry *entry;
+  unsigned int idx;
+
+  if (bucket)
+    *bucket = NULL;
+  
+  idx = RANDOM_INDEX (table, key) & table->mask;
+
+  /* Search all of the entries in this bucket. */
+  entry = table->buckets[idx];
+  while (entry != NULL)
+    {
+      if (key == entry->key)
+        {
+          if (bucket)
+            *bucket = &(table->buckets[idx]);
+          return entry;
+        }
+      
+      entry = entry->next;
+    }
 
-  index = HashString(key) & tablePtr->mask;
+  /* Entry not found.  Add a new one to the bucket. */
+  if (create_if_not_found)
+    entry = add_entry (table, idx, key, bucket);
 
-  /*
-   * Search all of the entries in this bucket.
-   */
+  return entry;
+}
 
-  for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
-       hPtr = hPtr->nextPtr) {
-    for (p1 = key, p2 = hPtr->key.string; ; p1++, p2++) {
-      if (*p1 != *p2) {
-        break;
-      }
-      if (*p1 == '\0') {
-        *newPtr = 0;
-        return hPtr;
-      }
-    }
-  }
+static void
+rebuild_table (DBusHashTable *table)
+{
+  int old_size;
+  DBusHashEntry **old_buckets;
+  DBusHashEntry **old_chain;
+  DBusHashEntry *entry;
 
   /*
-   * Entry not found.  Add a new one to the bucket.
+   * Allocate and initialize the new bucket array, and set up
+   * hashing constants for new array size.
    */
+  
+  old_size = table->n_buckets;
+  old_buckets = table->buckets;
+
+  table->n_buckets *= 4;
+  table->buckets = dbus_new0 (DBusHashEntry*, table->n_buckets);  
+  if (table->buckets == NULL)
+    {
+      /* out of memory, yay - just don't reallocate, the table will
+       * still work, albeit more slowly.
+       */
+      table->n_buckets /= 4;
+      table->buckets = old_buckets;
+      return;
+    }
 
-  *newPtr = 1;
-  hPtr = (Tcl_HashEntry *) ckalloc((unsigned)
-                                   (sizeof(Tcl_HashEntry) + strlen(key) - (sizeof(hPtr->key) -1)));
-  hPtr->tablePtr = tablePtr;
-  hPtr->bucketPtr = &(tablePtr->buckets[index]);
-  hPtr->nextPtr = *hPtr->bucketPtr;
-  hPtr->clientData = 0;
-  strcpy(hPtr->key.string, key);
-  *hPtr->bucketPtr = hPtr;
-  tablePtr->numEntries++;
+  table->rebuild_size *= 4;
+  table->down_shift -= 2;
+  table->mask = (table->mask << 2) + 3;
 
   /*
-   * If the table has exceeded a decent size, rebuild it with many
-   * more buckets.
+   * Rehash all of the existing entries into the new bucket array.
    */
 
-  if (tablePtr->numEntries >= tablePtr->rebuildSize) {
-    RebuildTable(tablePtr);
-  }
-  return hPtr;
+  for (old_chain = old_buckets; old_size > 0; old_size--, old_chain++)
+    {
+      for (entry = *old_chain; entry != NULL; entry = *old_chain)
+        {
+          unsigned int idx;
+          DBusHashEntry **bucket;
+          
+          *old_chain = entry->next;
+          switch (table->key_type)
+            {
+            case DBUS_HASH_STRING:
+              idx = string_hash (entry->key) & table->mask;
+              break;
+            case DBUS_HASH_INT:
+              idx = RANDOM_INDEX (table, entry->key);
+              break;
+            default:
+              idx = 0;
+              _dbus_assert_not_reached ("Unknown hash table type");
+              break;
+            }
+
+          bucket = &(table->buckets[idx]);
+          entry->next = *bucket;
+          *bucket = entry;
+        }
+    }
+  
+  /* Free the old bucket array, if it was dynamically allocated. */
+
+  if (old_buckets != table->static_buckets)
+    dbus_free (old_buckets);
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * OneWordFind --
- *
- *     Given a hash table with one-word keys, and a one-word key, find
- *     the entry with a matching key.
- *
- * Results:
- *     The return value is a token for the matching entry in the
- *     hash table, or NULL if there was no matching entry.
- *
- * Side effects:
- *     None.
- *
- *----------------------------------------------------------------------
- */
 
-static Tcl_HashEntry *
-OneWordFind(tablePtr, key)
-     Tcl_HashTable *tablePtr;  /* Table in which to lookup entry. */
-     register CONST char *key; /* Key to use to find matching entry. */
+/**
+ * Looks up the value for a given string in a hash table
+ * of type #DBUS_HASH_STRING. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the string to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_string (DBusHashTable *table,
+                                const char    *key)
 {
-  register Tcl_HashEntry *hPtr;
-  int index;
+  DBusHashEntry *entry;
 
-  index = RANDOM_INDEX(tablePtr, key);
+  _dbus_assert (table->key_type == DBUS_HASH_STRING);
+  
+  entry = (* table->find_function) (table, (char*) key, FALSE, NULL);
 
-  /*
-   * Search all of the entries in the appropriate bucket.
-   */
+  if (entry)
+    return entry->value;
+  else
+    return NULL;
+}
 
-  for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
-       hPtr = hPtr->nextPtr) {
-    if (hPtr->key.oneWordValue == key) {
-      return hPtr;
-    }
-  }
-  return NULL;
+/**
+ * Looks up the value for a given integer in a hash table
+ * of type #DBUS_HASH_INT. Returns %NULL if the value
+ * is not present. (A not-present entry is indistinguishable
+ * from an entry with a value of %NULL.)
+ * @param table the hash table.
+ * @param key the integer to look up.
+ * @returns the value of the hash entry.
+ */
+void*
+_dbus_hash_table_lookup_int (DBusHashTable *table,
+                             int            key)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_INT);
+  
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, NULL);
+
+  if (entry)
+    return entry->value;
+  else
+    return NULL;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * OneWordCreate --
- *
- *     Given a hash table with one-word keys, and a one-word key, find
- *     the entry with a matching key.  If there is no matching entry,
- *     then create a new entry that does match.
- *
- * Results:
- *     The return value is a pointer to the matching entry.  If this
- *     is a newly-created entry, then *newPtr will be set to a non-zero
- *     value;  otherwise *newPtr will be set to 0.  If this is a new
- *     entry the value stored in the entry will initially be 0.
- *
- * Side effects:
- *     A new entry may be added to the hash table.
+
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
  *
- *----------------------------------------------------------------------
+ * @param table the hash table.
+ * @param key the hash key.
  */
-
-static Tcl_HashEntry *
-OneWordCreate(tablePtr, key, newPtr)
-     Tcl_HashTable *tablePtr;  /* Table in which to lookup entry. */
-     register CONST char *key; /* Key to use to find or create matching
-                                * entry. */
-     int *newPtr;              /* Store info here telling whether a new
-                                * entry was created. */
+void
+_dbus_hash_table_remove_string (DBusHashTable *table,
+                                const char    *key)
 {
-  register Tcl_HashEntry *hPtr;
-  int index;
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (table->key_type == DBUS_HASH_STRING);
+  
+  entry = (* table->find_function) (table, (char*) key, FALSE, &bucket);
+
+  if (entry)
+    remove_entry (table, bucket, entry);
+}
 
-  index = RANDOM_INDEX(tablePtr, key);
+/**
+ * Removes the hash entry for the given key. If no hash entry
+ * for the key exists, does nothing.
+ *
+ * @param table the hash table.
+ * @param key the hash key.
+ */
+void
+_dbus_hash_table_remove_int (DBusHashTable *table,
+                             int            key)
+{
+  DBusHashEntry *entry;
+  DBusHashEntry **bucket;
+  
+  _dbus_assert (table->key_type == DBUS_HASH_INT);
+  
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, &bucket);
+  
+  if (entry)
+    remove_entry (table, bucket, entry);
+}
 
-  /*
-   * Search all of the entries in this bucket.
-   */
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
+ *
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ * 
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
+ */
+dbus_bool_t
+_dbus_hash_table_insert_string (DBusHashTable *table,
+                                char          *key,
+                                void          *value)
+{
+  DBusHashEntry *entry;
 
-  for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
-       hPtr = hPtr->nextPtr) {
-    if (hPtr->key.oneWordValue == key) {
-      *newPtr = 0;
-      return hPtr;
-    }
-  }
+  _dbus_assert (table->key_type == DBUS_HASH_STRING);
+  
+  entry = (* table->find_function) (table, key, TRUE, NULL);
 
-  /*
-   * Entry not found.  Add a new one to the bucket.
-   */
+  if (entry == NULL)
+    return FALSE; /* no memory */
 
-  *newPtr = 1;
-  hPtr = (Tcl_HashEntry *) ckalloc(sizeof(Tcl_HashEntry));
-  hPtr->tablePtr = tablePtr;
-  hPtr->bucketPtr = &(tablePtr->buckets[index]);
-  hPtr->nextPtr = *hPtr->bucketPtr;
-  hPtr->clientData = 0;
-  hPtr->key.oneWordValue = (char *) key;       /* CONST XXXX */
-  *hPtr->bucketPtr = hPtr;
-  tablePtr->numEntries++;
+  if (table->free_key_function && entry->key != key)
+    (* table->free_key_function) (entry->key);
 
-  /*
-   * If the table has exceeded a decent size, rebuild it with many
-   * more buckets.
-   */
+  if (table->free_value_function && entry->value != value)
+    (* table->free_value_function) (entry->value);
+      
+  entry->key = key;
+  entry->value = value;
 
-  if (tablePtr->numEntries >= tablePtr->rebuildSize) {
-    RebuildTable(tablePtr);
-  }
-  return hPtr;
+  return TRUE;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * ArrayFind --
- *
- *     Given a hash table with array-of-int keys, and a key, find
- *     the entry with a matching key.
- *
- * Results:
- *     The return value is a token for the matching entry in the
- *     hash table, or NULL if there was no matching entry.
- *
- * Side effects:
- *     None.
+
+/**
+ * Creates a hash entry with the given key and value.
+ * The key and value are not copied; they are stored
+ * in the hash table by reference. If an entry with the
+ * given key already exists, the previous key and value
+ * are overwritten (and freed if the hash table has
+ * a key_free_function and/or value_free_function).
  *
- *----------------------------------------------------------------------
+ * Returns #FALSE if memory for the new hash entry
+ * can't be allocated.
+ * 
+ * @param table the hash table.
+ * @param key the hash entry key.
+ * @param value the hash entry value.
  */
-
-static Tcl_HashEntry *
-ArrayFind(tablePtr, key)
-     Tcl_HashTable *tablePtr;  /* Table in which to lookup entry. */
-     CONST char *key;          /* Key to use to find matching entry. */
+dbus_bool_t
+_dbus_hash_table_insert_int (DBusHashTable *table,
+                             int            key,
+                             void          *value)
 {
-  register Tcl_HashEntry *hPtr;
-  int *arrayPtr = (int *) key;
-  register int *iPtr1, *iPtr2;
-  int index, count;
+  DBusHashEntry *entry;
 
-  for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;
-       count > 0; count--, iPtr1++) {
-    index += *iPtr1;
-  }
-  index = RANDOM_INDEX(tablePtr, index);
+  _dbus_assert (table->key_type == DBUS_HASH_INT);
+  
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), TRUE, NULL);
 
-  /*
-   * Search all of the entries in the appropriate bucket.
-   */
+  if (entry == NULL)
+    return FALSE; /* no memory */
 
-  for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
-       hPtr = hPtr->nextPtr) {
-    for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,
-           count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
-      if (count == 0) {
-        return hPtr;
-      }
-      if (*iPtr1 != *iPtr2) {
-        break;
-      }
-    }
-  }
-  return NULL;
+  if (table->free_key_function && entry->key != _DBUS_INT_TO_POINTER (key))
+    (* table->free_key_function) (entry->key);
+  
+  if (table->free_value_function && entry->value != value)
+    (* table->free_value_function) (entry->value);
+  
+  entry->key = _DBUS_INT_TO_POINTER (key);
+  entry->value = value;
+
+  return TRUE;
 }
-\f
-/*
- *----------------------------------------------------------------------
- *
- * ArrayCreate --
- *
- *     Given a hash table with one-word keys, and a one-word key, find
- *     the entry with a matching key.  If there is no matching entry,
- *     then create a new entry that does match.
- *
- * Results:
- *     The return value is a pointer to the matching entry.  If this
- *     is a newly-created entry, then *newPtr will be set to a non-zero
- *     value;  otherwise *newPtr will be set to 0.  If this is a new
- *     entry the value stored in the entry will initially be 0.
- *
- * Side effects:
- *     A new entry may be added to the hash table.
+
+/**
+ * Gets the number of hash entries in a hash table.
  *
- *----------------------------------------------------------------------
+ * @param table the hash table.
+ * @returns the number of entries in the table.
  */
+int
+_dbus_hash_table_get_n_entries (DBusHashTable *table)
+{
+  return table->n_entries;
+}
 
-static Tcl_HashEntry *
-ArrayCreate(tablePtr, key, newPtr)
-     Tcl_HashTable *tablePtr;  /* Table in which to lookup entry. */
-     register CONST char *key; /* Key to use to find or create matching
-                                * entry. */
-     int *newPtr;              /* Store info here telling whether a new
-                                * entry was created. */
+/** }@ */
+
+#ifdef DBUS_BUILD_TESTS
+#include "dbus-test.h"
+#include <stdio.h>
+
+static int
+count_entries (DBusHashTable *table)
 {
-  register Tcl_HashEntry *hPtr;
-  int *arrayPtr = (int *) key;
-  register int *iPtr1, *iPtr2;
-  int index, count;
+  DBusHashIter iter;
+  int count;
 
-  for (index = 0, count = tablePtr->keyType, iPtr1 = arrayPtr;
-       count > 0; count--, iPtr1++) {
-    index += *iPtr1;
-  }
-  index = RANDOM_INDEX(tablePtr, index);
+  count = 0;
+  _dbus_hash_iter_init (table, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    ++count;
 
-  /*
-   * Search all of the entries in the appropriate bucket.
+  _dbus_assert (count == _dbus_hash_table_get_n_entries (table));
+  
+  return count;
+}
+
+/**
+ * @ingroup DBusHashTableInternals
+ * Unit test for DBusHashTable
+ * @returns #TRUE on success.
+ */
+dbus_bool_t
+_dbus_hash_test (void)
+{
+  int i;
+  DBusHashTable *table1;
+  DBusHashTable *table2;
+  DBusHashIter iter;
+  
+  table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                 dbus_free, dbus_free);
+  if (table1 == NULL)
+    return FALSE;
+
+  table2 = _dbus_hash_table_new (DBUS_HASH_INT,
+                                 NULL, dbus_free);
+  if (table2 == NULL)
+    return FALSE;
+
+  /* Insert and remove a bunch of stuff, counting the table in between
+   * to be sure it's not broken and that iteration works
    */
+  i = 0;
+  while (i < 3000)
+    {
+      char buf[256];
+      sprintf (buf, "Hash key %d", i);
+      void *value;
+      char *key;
+
+      key = _dbus_strdup (buf);
+      if (key == NULL)
+        return FALSE;
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        return FALSE;
+      
+      if (!_dbus_hash_table_insert_string (table1,
+                                           key, value))
+        return FALSE;
+
+      value = _dbus_strdup (buf);
+      if (value == NULL)
+        return FALSE;
+      
+      if (!_dbus_hash_table_insert_int (table2,
+                                        i, value))
+        return FALSE;
+      
+      _dbus_assert (count_entries (table1) == i + 1);
+      _dbus_assert (count_entries (table2) == i + 1);
+
+      value = _dbus_hash_table_lookup_string (table1, buf);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, "Value!") == 0);
+
+      value = _dbus_hash_table_lookup_int (table2, i);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, buf) == 0);
+      
+      ++i;
+    }
 
-  for (hPtr = tablePtr->buckets[index]; hPtr != NULL;
-       hPtr = hPtr->nextPtr) {
-    for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words,
-           count = tablePtr->keyType; ; count--, iPtr1++, iPtr2++) {
-      if (count == 0) {
-        *newPtr = 0;
-        return hPtr;
-      }
-      if (*iPtr1 != *iPtr2) {
-        break;
-      }
+  --i;
+  while (i >= 0)
+    {
+      char buf[256];
+      sprintf (buf, "Hash key %d", i);
+      
+      _dbus_hash_table_remove_string (table1,
+                                      buf);
+
+      _dbus_hash_table_remove_int (table2, i);
+
+      _dbus_assert (count_entries (table1) == i);
+      _dbus_assert (count_entries (table2) == i);
+
+      --i;
     }
-  }
 
-  /*
-   * Entry not found.  Add a new one to the bucket.
-   */
+  _dbus_hash_table_ref (table1);
+  _dbus_hash_table_ref (table2);
+  _dbus_hash_table_unref (table1);
+  _dbus_hash_table_unref (table2);
+  _dbus_hash_table_unref (table1);
+  _dbus_hash_table_unref (table2);
 
-  *newPtr = 1;
-  hPtr = (Tcl_HashEntry *) ckalloc((unsigned) (sizeof(Tcl_HashEntry)
-                                               + (tablePtr->keyType*sizeof(int)) - 4));
-  hPtr->tablePtr = tablePtr;
-  hPtr->bucketPtr = &(tablePtr->buckets[index]);
-  hPtr->nextPtr = *hPtr->bucketPtr;
-  hPtr->clientData = 0;
-  for (iPtr1 = arrayPtr, iPtr2 = hPtr->key.words, count = tablePtr->keyType;
-       count > 0; count--, iPtr1++, iPtr2++) {
-    *iPtr2 = *iPtr1;
-  }
-  *hPtr->bucketPtr = hPtr;
-  tablePtr->numEntries++;
 
-  /*
-   * If the table has exceeded a decent size, rebuild it with many
-   * more buckets.
+  /* Insert a bunch of stuff then check
+   * that iteration works correctly (finds the right
+   * values, iter_set_value works, etc.)
    */
+  table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                 dbus_free, dbus_free);
+  if (table1 == NULL)
+    return FALSE;
+  
+  table2 = _dbus_hash_table_new (DBUS_HASH_INT,
+                                 NULL, dbus_free);
+  if (table2 == NULL)
+    return FALSE;
+  
+  i = 0;
+  while (i < 5000)
+    {
+      char buf[256];
+      sprintf (buf, "Hash key %d", i);
+      char *key;
+      void *value;      
+      
+      key = _dbus_strdup (buf);
+      if (key == NULL)
+        return FALSE;
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        return FALSE;
+      
+      if (!_dbus_hash_table_insert_string (table1,
+                                           key, value))
+        return FALSE;
+
+      value = _dbus_strdup (buf);
+      if (value == NULL)
+        return FALSE;
+      
+      if (!_dbus_hash_table_insert_int (table2,
+                                        i, value))
+        return FALSE;
+      
+      _dbus_assert (count_entries (table1) == i + 1);
+      _dbus_assert (count_entries (table2) == i + 1);
+      
+      ++i;
+    }
 
-  if (tablePtr->numEntries >= tablePtr->rebuildSize) {
-    RebuildTable(tablePtr);
-  }
-  return hPtr;
-}
-\f
-/*
- *----------------------------------------------------------------------
- *
- * BogusFind --
- *
- *     This procedure is invoked when an Tcl_FindHashEntry is called
- *     on a table that has been deleted.
- *
- * Results:
- *     If panic returns (which it shouldn't) this procedure returns
- *     NULL.
- *
- * Side effects:
- *     Generates a panic.
- *
- *----------------------------------------------------------------------
- */
+  _dbus_hash_iter_init (table1, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      const char *key;
+      void *value;
 
-/* ARGSUSED */
-static Tcl_HashEntry *
-BogusFind(tablePtr, key)
-     Tcl_HashTable *tablePtr;  /* Table in which to lookup entry. */
-     CONST char *key;          /* Key to use to find matching entry. */
-{
-  panic("called Tcl_FindHashEntry on deleted table");
-  return NULL;
-}
-\f
-/*
- *----------------------------------------------------------------------
- *
- * BogusCreate --
- *
- *     This procedure is invoked when an Tcl_CreateHashEntry is called
- *     on a table that has been deleted.
- *
- * Results:
- *     If panic returns (which it shouldn't) this procedure returns
- *     NULL.
- *
- * Side effects:
- *     Generates a panic.
- *
- *----------------------------------------------------------------------
- */
+      key = _dbus_hash_iter_get_string_key (&iter);
+      value = _dbus_hash_iter_get_value (&iter);
 
-/* ARGSUSED */
-static Tcl_HashEntry *
-BogusCreate(tablePtr, key, newPtr)
-     Tcl_HashTable *tablePtr;  /* Table in which to lookup entry. */
-     CONST char *key;          /* Key to use to find or create matching
-                                * entry. */
-     int *newPtr;              /* Store info here telling whether a new
-                                * entry was created. */
-{
-  panic("called Tcl_CreateHashEntry on deleted table");
-  return NULL;
-}
-\f
-/*
- *----------------------------------------------------------------------
- *
- * RebuildTable --
- *
- *     This procedure is invoked when the ratio of entries to hash
- *     buckets becomes too large.  It creates a new table with a
- *     larger bucket array and moves all of the entries into the
- *     new table.
- *
- * Results:
- *     None.
- *
- * Side effects:
- *     Memory gets reallocated and entries get re-hashed to new
- *     buckets.
- *
- *----------------------------------------------------------------------
- */
+      _dbus_assert (_dbus_hash_table_lookup_string (table1, key) == value);
 
-static void
-RebuildTable(tablePtr)
-     register Tcl_HashTable *tablePtr; /* Table to enlarge. */
-{
-  int oldSize, count, index;
-  Tcl_HashEntry **oldBuckets;
-  register Tcl_HashEntry **oldChainPtr, **newChainPtr;
-  register Tcl_HashEntry *hPtr;
+      value = _dbus_strdup ("Different value!");
+      if (value == NULL)
+        return FALSE;
+      
+      _dbus_hash_iter_set_value (&iter, value);
 
-  oldSize = tablePtr->numBuckets;
-  oldBuckets = tablePtr->buckets;
+      _dbus_assert (_dbus_hash_table_lookup_string (table1, key) == value);
+    }
+  
+  _dbus_hash_iter_init (table1, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      _dbus_hash_iter_remove_entry (&iter);
+      _dbus_assert (count_entries (table1) == i - 1);
+      --i;
+    }
 
-  /*
-   * Allocate and initialize the new bucket array, and set up
-   * hashing constants for new array size.
-   */
+  _dbus_hash_iter_init (table2, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      int key;
+      void *value;
 
-  tablePtr->numBuckets *= 4;
-  tablePtr->buckets = (Tcl_HashEntry **) ckalloc((unsigned)
-                                                 (tablePtr->numBuckets * sizeof(Tcl_HashEntry *)));
-  for (count = tablePtr->numBuckets, newChainPtr = tablePtr->buckets;
-       count > 0; count--, newChainPtr++) {
-    *newChainPtr = NULL;
-  }
-  tablePtr->rebuildSize *= 4;
-  tablePtr->downShift -= 2;
-  tablePtr->mask = (tablePtr->mask << 2) + 3;
+      key = _dbus_hash_iter_get_int_key (&iter);
+      value = _dbus_hash_iter_get_value (&iter);
 
-  /*
-   * Rehash all of the existing entries into the new bucket array.
-   */
+      _dbus_assert (_dbus_hash_table_lookup_int (table2, key) == value);
 
-  for (oldChainPtr = oldBuckets; oldSize > 0; oldSize--, oldChainPtr++) {
-    for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = *oldChainPtr) {
-      *oldChainPtr = hPtr->nextPtr;
-      if (tablePtr->keyType == TCL_STRING_KEYS) {
-        index = HashString(hPtr->key.string) & tablePtr->mask;
-      } else if (tablePtr->keyType == TCL_ONE_WORD_KEYS) {
-        index = RANDOM_INDEX(tablePtr, hPtr->key.oneWordValue);
-      } else {
-        register int *iPtr;
-        int count;
-
-        for (index = 0, count = tablePtr->keyType,
-               iPtr = hPtr->key.words; count > 0; count--, iPtr++) {
-          index += *iPtr;
-        }
-        index = RANDOM_INDEX(tablePtr, index);
-      }
-      hPtr->bucketPtr = &(tablePtr->buckets[index]);
-      hPtr->nextPtr = *hPtr->bucketPtr;
-      *hPtr->bucketPtr = hPtr;
+      value = _dbus_strdup ("Different value!");
+      if (value == NULL)
+        return FALSE;
+      
+      _dbus_hash_iter_set_value (&iter, value);
+
+      _dbus_assert (_dbus_hash_table_lookup_int (table2, key) == value);
     }
-  }
 
-  /*
-   * Free up the old bucket array, if it was dynamically allocated.
+  i = count_entries (table2);
+  _dbus_hash_iter_init (table2, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      _dbus_hash_iter_remove_entry (&iter);
+      _dbus_assert (count_entries (table2) + 1 == i);
+      --i;
+    }
+  
+  _dbus_hash_table_unref (table1);
+  _dbus_hash_table_unref (table2);
+
+
+  /* Now do a bunch of things again using _dbus_hash_iter_lookup() to
+   * be sure that interface works.
    */
+  table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
+                                 dbus_free, dbus_free);
+  if (table1 == NULL)
+    return FALSE;
+  
+  table2 = _dbus_hash_table_new (DBUS_HASH_INT,
+                                 NULL, dbus_free);
+  if (table2 == NULL)
+    return FALSE;
+  
+  i = 0;
+  while (i < 3000)
+    {
+      char buf[256];
+      sprintf (buf, "Hash key %d", i);
+      void *value;
+      char *key;
+
+      key = _dbus_strdup (buf);
+      if (key == NULL)
+        return FALSE;
+      value = _dbus_strdup ("Value!");
+      if (value == NULL)
+        return FALSE;
+      
+      if (!_dbus_hash_iter_lookup (table1,
+                                   key, TRUE, &iter))
+        return FALSE;
+      _dbus_assert (_dbus_hash_iter_get_value (&iter) == NULL);
+      _dbus_hash_iter_set_value (&iter, value);
+
+      value = _dbus_strdup (buf);
+      if (value == NULL)
+        return FALSE;
+
+      if (!_dbus_hash_iter_lookup (table2,
+                                   _DBUS_INT_TO_POINTER (i), TRUE, &iter))
+        return FALSE;
+      _dbus_assert (_dbus_hash_iter_get_value (&iter) == NULL);
+      _dbus_hash_iter_set_value (&iter, value); 
+      
+      _dbus_assert (count_entries (table1) == i + 1);
+      _dbus_assert (count_entries (table2) == i + 1);
+
+      if (!_dbus_hash_iter_lookup (table1, buf, FALSE, &iter))
+        return FALSE;
+      
+      value = _dbus_hash_iter_get_value (&iter);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, "Value!") == 0);
+
+      /* Iterate just to be sure it works, though
+       * it's a stupid thing to do
+       */
+      while (_dbus_hash_iter_next (&iter))
+        ;
+      
+      if (!_dbus_hash_iter_lookup (table2, _DBUS_INT_TO_POINTER (i), FALSE, &iter))
+        return FALSE;
+
+      value = _dbus_hash_iter_get_value (&iter);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, buf) == 0);
+
+      /* Iterate just to be sure it works, though
+       * it's a stupid thing to do
+       */
+      while (_dbus_hash_iter_next (&iter))
+        ;
+      
+      ++i;
+    }
+
+  --i;
+  while (i >= 0)
+    {
+      char buf[256];
+      sprintf (buf, "Hash key %d", i);
+
+      if (!_dbus_hash_iter_lookup (table1, buf, FALSE, &iter))
+        _dbus_assert_not_reached ("hash entry should have existed");
+      _dbus_hash_iter_remove_entry (&iter);
+      
+      if (!_dbus_hash_iter_lookup (table2, _DBUS_INT_TO_POINTER (i), FALSE, &iter))
+        _dbus_assert_not_reached ("hash entry should have existed");
+      _dbus_hash_iter_remove_entry (&iter);
+
+      _dbus_assert (count_entries (table1) == i);
+      _dbus_assert (count_entries (table2) == i);
+
+      --i;
+    }
+
+  _dbus_hash_table_unref (table1);
+  _dbus_hash_table_unref (table2);
 
-  if (oldBuckets != tablePtr->staticBuckets) {
-    ckfree((char *) oldBuckets);
-  }
+  
+  return TRUE;
 }
 
 #endif
index c8c6eab..cc6fb19 100644 (file)
 #ifndef DBUS_HASH_H
 #define DBUS_HASH_H
 
-DBUS_BEGIN_DECLS
-
 #include <dbus/dbus-memory.h>
 #include <dbus/dbus-types.h>
 
-typedef struct DBusHashTable DBusHashTable;
-typedef struct DBusHashIter  DBusHashIter;
+DBUS_BEGIN_DECLS
 
 /* The iterator is on the stack, but its real fields are
  * hidden privately.
@@ -38,18 +35,25 @@ typedef struct DBusHashIter  DBusHashIter;
 struct DBusHashIter
 {
   void *dummy1;
-  int   dummy2;
-  void *dummy3;  
+  void *dummy2;
+  void *dummy3;
+  void *dummy4;
+  int   dummy5;
+  int   dummy6;
 };
 
+typedef struct DBusHashTable DBusHashTable;
+typedef struct DBusHashIter  DBusHashIter;
+
 /* Allowing an arbitrary function as with GLib
- * would probably be nicer, but this is internal API so
- * who cares
+ * would be nicer for a public API, but for
+ * an internal API this saves typing, we can add
+ * more whenever we feel like it.
  */
 typedef enum
 {
-  DBUS_HASH_STRING,
-  DBUS_HASH_INT
+  DBUS_HASH_STRING, /**< Hash keys are strings. */
+  DBUS_HASH_INT     /**< Hash keys are integers. */
 } DBusHashType;
 
 DBusHashTable* _dbus_hash_table_new   (DBusHashType     type,
@@ -58,31 +62,37 @@ DBusHashTable* _dbus_hash_table_new   (DBusHashType     type,
 void           _dbus_hash_table_ref   (DBusHashTable   *table);
 void           _dbus_hash_table_unref (DBusHashTable   *table);
 
-/* usage "while (_dbus_hash_table_iterate (table, &iter)) { }" */
-dbus_bool_t _dbus_hash_table_iterate (DBusHashTable *table,
-                                      DBusHashIter  *iter);
+void        _dbus_hash_iter_init (DBusHashTable *table,
+                                  DBusHashIter  *iter);
+dbus_bool_t _dbus_hash_iter_next (DBusHashIter  *iter);
 
-void*       _dbus_hash_iter_get_value      (DBusHashEntry *iter);
-void        _dbus_hash_iter_set_value      (DBusHashEntry *iter,
-                                            void          *value);
-int         _dbus_hash_iter_get_int_key    (DBusHashIter  *iter);
-const char* _dbus_hash_iter_get_string_key (DBusHashIter  *iter);
+void        _dbus_hash_iter_remove_entry   (DBusHashIter *iter);
+void*       _dbus_hash_iter_get_value      (DBusHashIter *iter);
+void        _dbus_hash_iter_set_value      (DBusHashIter *iter,
+                                            void         *value);
+int         _dbus_hash_iter_get_int_key    (DBusHashIter *iter);
+const char* _dbus_hash_iter_get_string_key (DBusHashIter *iter);
 
-void* _dbus_hash_table_lookup_string (DBusHashTable *table,
-                                      const char    *key);
-void* _dbus_hash_table_lookup_int    (DBusHashTable *table,
-                                      int            key);
-void  _dbus_hash_table_remove_string (DBusHashTable *table,
-                                      const char    *key);
-void  _dbus_hash_table_remove_int    (DBusHashTable *table,
-                                      int            key);
-void  _dbus_hash_table_insert_string (DBusHashTable *table,
-                                      const char    *key,
-                                      void          *value);
-void  _dbus_hash_table_insert_int    (DBusHashTable *table,
-                                      int            key,
-                                      void          *value);
+dbus_bool_t _dbus_hash_iter_lookup         (DBusHashTable *table,
+                                            void          *key,
+                                            dbus_bool_t    create_if_not_found,
+                                            DBusHashIter  *iter);
 
+void*       _dbus_hash_table_lookup_string (DBusHashTable *table,
+                                            const char    *key);
+void*       _dbus_hash_table_lookup_int    (DBusHashTable *table,
+                                            int            key);
+void        _dbus_hash_table_remove_string (DBusHashTable *table,
+                                            const char    *key);
+void        _dbus_hash_table_remove_int    (DBusHashTable *table,
+                                            int            key);
+dbus_bool_t _dbus_hash_table_insert_string (DBusHashTable *table,
+                                            char          *key,
+                                            void          *value);
+dbus_bool_t _dbus_hash_table_insert_int    (DBusHashTable *table,
+                                            int            key,
+                                            void          *value);
+int         _dbus_hash_table_get_n_entries (DBusHashTable *table);
 
 DBUS_END_DECLS
 
index 413eca5..8b341de 100644 (file)
 #  endif /* !__cplusplus */
 #endif
 
+/* Normally docs are in .c files, but there isn't a .c file for this. */
+/**
+ * @defgroup DBusMacros Utility macros
+ * @ingroup  DBus
+ * @brief #TRUE, #FALSE, #NULL, and so on
+ *
+ * Utility macros.
+ *
+ * @{
+ */
+
+/**
+ * @def DBUS_BEGIN_DECLS
+ *
+ * Macro used prior to declaring functions in the D-BUS header
+ * files. Expands to "extern "C"" when using a C++ compiler,
+ * and expands to nothing when using a C compiler.
+ */
+/**
+ * @def DBUS_END_DECLS
+ *
+ * Macro used after declaring functions in the D-BUS header
+ * files. Expands to "}" when using a C++ compiler,
+ * and expands to nothing when using a C compiler.
+ */
+/**
+ * @def TRUE
+ *
+ * Expands to "1"
+ */
+/**
+ * @def FALSE
+ *
+ * Expands to "0"
+ */
+/**
+ * @def NULL
+ *
+ * A null pointer, defined appropriately for C or C++.
+ */
+
+/** }@ */
+
 #endif /* DBUS_MACROS_H */
index 701896e..52a563e 100644 (file)
@@ -25,7 +25,7 @@
 #include <stdlib.h>
 
 /**
- * @defgroup Memory Memory Allocation
+ * @defgroup DBusMemory Memory Allocation
  * @ingroup  DBus
  * @brief dbus_malloc(), dbus_free(), etc.
  *
@@ -45,7 +45,7 @@
  *
  * @param type type name to allocate
  * @param count number of instances in the allocated array
- * @returns the new memory block or NULL on failure
+ * @returns the new memory block or #NULL on failure
  */
 
 /**
@@ -59,7 +59,7 @@
  *
  * @param type type name to allocate
  * @param count number of instances in the allocated array
- * @returns the new memory block or NULL on failure
+ * @returns the new memory block or #NULL on failure
  */
 
 /**
 
 /**
  * Allocates the given number of bytes, as with standard
- * malloc(). Guaranteed to return NULL if bytes is zero
- * on all platforms. Returns NULL if the allocation fails.
+ * malloc(). Guaranteed to return #NULL if bytes is zero
+ * on all platforms. Returns #NULL if the allocation fails.
  * The memory must be released with dbus_free().
  *
  * @param bytes number of bytes to allocate
- * @return allocated memory, or NULL if the allocation fails.
+ * @return allocated memory, or #NULL if the allocation fails.
  */
 void*
 dbus_malloc (size_t bytes)
@@ -91,11 +91,11 @@ dbus_malloc (size_t bytes)
 /**
  * Allocates the given number of bytes, as with standard malloc(), but
  * all bytes are initialized to zero as with calloc(). Guaranteed to
- * return NULL if bytes is zero on all platforms. Returns NULL if the
+ * return #NULL if bytes is zero on all platforms. Returns #NULL if the
  * allocation fails.  The memory must be released with dbus_free().
  *
  * @param bytes number of bytes to allocate
- * @return allocated memory, or NULL if the allocation fails.
+ * @return allocated memory, or #NULL if the allocation fails.
  */
 void*
 dbus_malloc0 (size_t bytes)
@@ -108,13 +108,13 @@ dbus_malloc0 (size_t bytes)
 
 /**
  * Resizes a block of memory previously allocated by dbus_malloc() or
- * dbus_malloc0(). Guaranteed to free the memory and return NULL if bytes
- * is zero on all platforms. Returns NULL if the resize fails.
+ * dbus_malloc0(). Guaranteed to free the memory and return #NULL if bytes
+ * is zero on all platforms. Returns #NULL if the resize fails.
  * If the resize fails, the memory is not freed.
  *
  * @param memory block to be resized
  * @param bytes new size of the memory block
- * @return allocated memory, or NULL if the resize fails.
+ * @return allocated memory, or #NULL if the resize fails.
  */
 void*
 dbus_realloc (void  *memory,
@@ -133,7 +133,7 @@ dbus_realloc (void  *memory,
 
 /**
  * Frees a block of memory previously allocated by dbus_malloc() or
- * dbus_malloc0().
+ * dbus_malloc0(). If passed #NULL, does nothing.
  * 
  * @param memory block to be freed
  */
index 4d8728a..b8caa83 100644 (file)
@@ -28,7 +28,7 @@
 #define DBUS_MEMORY_H
 
 #include <dbus/dbus-macros.h>
-#include <sys/types.h> /* for size_t - is there a better header for this? */
+#include <stddef.h>
 
 DBUS_BEGIN_DECLS
 
index 1064f42..0266ba4 100644 (file)
  * @{
  */
 
+/**
+ * @struct DBusMessage
+ * Object representing a message received from or to be sent to
+ * another application.
+ */
 struct DBusMessage
 {
-  int refcount;
+  int refcount; /**< Reference count */
 
 };
 
diff --git a/dbus/dbus-test.c b/dbus/dbus-test.c
new file mode 100644 (file)
index 0000000..2eb862e
--- /dev/null
@@ -0,0 +1,45 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-test.c  Program to run all tests
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "dbus-types.h"
+#include <stdio.h>
+
+/* To add a test, write a function like this one,
+ * declare it here, define it in the file to be tested,
+ * then call it from main() below. Test functions
+ * should return FALSE on failure.
+ */
+dbus_bool_t _dbus_hash_test (void);
+
+int
+main (int    argc,
+      char **argv)
+{
+  printf ("%s: running hash table tests\n", argv[0]);
+  if (!_dbus_hash_test ())
+    return 1;
+
+  
+  printf ("%s: completed successfully\n", argv[0]);
+  return 0;
+}
diff --git a/dbus/dbus-test.h b/dbus/dbus-test.h
new file mode 100644 (file)
index 0000000..b3b49a5
--- /dev/null
@@ -0,0 +1,31 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-test.h  Declarations of test functions.
+ *
+ * Copyright (C) 2002  Red Hat Inc.
+ *
+ * Licensed under the Academic Free License version 1.2
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef DBUS_TEST_H
+#define DBUS_TEST_H
+
+#include <dbus/dbus-types.h>
+
+dbus_bool_t _dbus_hash_test (void);
+
+#endif /* DBUS_TEST_H */
index 3f46f6e..d12b410 100644 (file)
@@ -29,4 +29,7 @@
 
 typedef unsigned int dbus_bool_t;
 
+typedef unsigned int dbus_uint32_t;
+typedef int          dbus_int32_t;
+
 #endif /* DBUS_TYPES_H */
index 74c6446..c03eb31 100644 (file)
 
 #include <dbus/dbus-macros.h>
 #include <dbus/dbus-message.h>
+#include <dbus/dbus-types.h>
 
 #undef DBUS_INSIDE_DBUS_H
 
 /**
- * @defgroup DBus D-BUS message system
+ * @defgroup DBus D-BUS message system public API
+ * @brief The exported public API of the D-BUS library.
  *
  * @{
  */
index e69de29..6e42b47 100644 (file)
@@ -0,0 +1,4 @@
+
+if DBUS_BUILD_TESTS
+
+endif