ListNames and ListQueuedOwners updated to work with new kdbus
[platform/upstream/dbus.git] / dbus / dbus-hash.c
index d7791d2..c80835a 100644 (file)
@@ -1,5 +1,5 @@
-/* -*- mode: C; c-file-style: "gnu" -*- */
-/* dbus-hash.c Generic hash table utility (internal to D-BUS implementation)
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* dbus-hash.c Generic hash table utility (internal to D-Bus implementation)
  * 
  * Copyright (C) 2002  Red Hat, Inc.
  * Copyright (c) 1991-1993 The Regents of the University of California.
@@ -7,10 +7,10 @@
  * 
  * Hash table implementation based on generic/tclHash.c from the Tcl
  * source code. The original Tcl license applies to portions of the
- * code from tclHash.c; the Tcl license follows this standad D-BUS
+ * code from tclHash.c; the Tcl license follows this standad D-Bus 
  * license information.
  *
- * Licensed under the Academic Free License version 1.2
+ * Licensed under the Academic Free License version 2.1
  * 
  * 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
@@ -24,7 +24,7 @@
  * 
  * 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
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *
  */
 /* 
@@ -74,6 +74,7 @@
  * accordance with the terms specified in this license.
  */
 
+#include <config.h>
 #include "dbus-hash.h"
 #include "dbus-internals.h"
 #include "dbus-mempool.h"
  * 
  */
 #define RANDOM_INDEX(table, i) \
-    (((((long) (i))*1103515245) >> (table)->down_shift) & (table)->mask)
+    (((((intptr_t) (i))*1103515245) >> (table)->down_shift) & (table)->mask)
 
 /**
  * Initial number of buckets in hash table (hash table statically
@@ -152,10 +153,11 @@ struct DBusHashEntry
 /**
  * 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);
+typedef DBusHashEntry* (* DBusFindEntryFunction) (DBusHashTable        *table,
+                                                  void                 *key,
+                                                  dbus_bool_t           create_if_not_found,
+                                                  DBusHashEntry      ***bucket,
+                                                  DBusPreallocatedHash *preallocated);
 
 /**
  * @brief Internals of DBusHashTable.
@@ -220,24 +222,29 @@ typedef struct
   int n_entries_on_init;     /**< used to detect table resize since initialization */
 } DBusRealHashIter;
 
-static DBusHashEntry* find_direct_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);
-static void           free_entry_data      (DBusHashTable   *table,
-                                            DBusHashEntry   *entry);
+_DBUS_STATIC_ASSERT (sizeof (DBusRealHashIter) == sizeof (DBusHashIter));
+
+static DBusHashEntry* find_direct_function      (DBusHashTable          *table,
+                                                 void                   *key,
+                                                 dbus_bool_t             create_if_not_found,
+                                                 DBusHashEntry        ***bucket,
+                                                 DBusPreallocatedHash   *preallocated);
+static DBusHashEntry* find_string_function      (DBusHashTable          *table,
+                                                 void                   *key,
+                                                 dbus_bool_t             create_if_not_found,
+                                                 DBusHashEntry        ***bucket,
+                                                 DBusPreallocatedHash   *preallocated);
+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);
+static void           free_entry_data           (DBusHashTable          *table,
+                                                 DBusHashEntry          *entry);
+
 
 /** @} */
 
@@ -312,7 +319,7 @@ _dbus_hash_table_new (DBusHashType     type,
   switch (table->key_type)
     {
     case DBUS_HASH_INT:
-    case DBUS_HASH_POINTER:
+    case DBUS_HASH_UINTPTR:
       table->find_function = find_direct_function;
       break;
     case DBUS_HASH_STRING:
@@ -334,11 +341,14 @@ _dbus_hash_table_new (DBusHashType     type,
  * Increments the reference count for a hash table.
  *
  * @param table the hash table to add a reference to.
+ * @returns the hash table.
  */
-void
+DBusHashTable *
 _dbus_hash_table_ref (DBusHashTable *table)
 {
   table->refcount += 1;
+  
+  return table;
 }
 
 /**
@@ -399,6 +409,22 @@ _dbus_hash_table_unref (DBusHashTable *table)
     }
 }
 
+/**
+ * Removed all entries from a hash table.
+ *
+ * @param table the hash table to remove all entries from.
+ */
+void
+_dbus_hash_table_remove_all (DBusHashTable *table)
+{
+  DBusHashIter iter;
+  _dbus_hash_iter_init (table, &iter);
+  while (_dbus_hash_iter_next (&iter))
+    {
+      _dbus_hash_iter_remove_entry(&iter);
+    }
+}
+
 static DBusHashEntry*
 alloc_entry (DBusHashTable *table)
 {
@@ -644,6 +670,25 @@ _dbus_hash_iter_get_int_key (DBusHashIter *iter)
 
 /**
  * Gets the key for the current entry.
+ * Only works for hash tables of type #DBUS_HASH_UINTPTR.
+ *
+ * @param iter the hash table iterator.
+ */
+uintptr_t
+_dbus_hash_iter_get_uintptr_key (DBusHashIter *iter)
+{
+  DBusRealHashIter *real;
+
+  real = (DBusRealHashIter*) iter;
+
+  _dbus_assert (real->table != NULL);
+  _dbus_assert (real->entry != NULL);
+
+  return (uintptr_t) real->entry->key;
+}
+
+/**
+ * Gets the key for the current entry.
  * Only works for hash tables of type #DBUS_HASH_STRING
  * @param iter the hash table iterator.
  */
@@ -705,7 +750,7 @@ _dbus_hash_iter_lookup (DBusHashTable *table,
   
   real = (DBusRealHashIter*) iter;
 
-  entry = (* table->find_function) (table, key, create_if_not_found, &bucket);
+  entry = (* table->find_function) (table, key, create_if_not_found, &bucket, NULL);
 
   if (entry == NULL)
     return FALSE;
@@ -722,22 +767,14 @@ _dbus_hash_iter_lookup (DBusHashTable *table,
   return TRUE;
 }
 
-static DBusHashEntry*
-add_entry (DBusHashTable   *table, 
-           unsigned int     idx,
-           void            *key,
-           DBusHashEntry ***bucket)
+static void
+add_allocated_entry (DBusHashTable   *table,
+                     DBusHashEntry   *entry,
+                     unsigned int     idx,
+                     void            *key,
+                     DBusHashEntry ***bucket)
 {
-  DBusHashEntry  *entry;
-  DBusHashEntry **b;
-  
-  entry = alloc_entry (table);
-  if (entry == NULL)
-    {
-      if (bucket)
-        *bucket = NULL;
-      return NULL;
-    }
+  DBusHashEntry **b;  
   
   entry->key = key;
   
@@ -756,68 +793,83 @@ add_entry (DBusHashTable   *table,
   if (table->n_entries >= table->hi_rebuild_size ||
       table->n_entries < table->lo_rebuild_size)
     rebuild_table (table);
+}
+
+static DBusHashEntry*
+add_entry (DBusHashTable        *table, 
+           unsigned int          idx,
+           void                 *key,
+           DBusHashEntry      ***bucket,
+           DBusPreallocatedHash *preallocated)
+{
+  DBusHashEntry  *entry;
+
+  if (preallocated == NULL)
+    {
+      entry = alloc_entry (table);
+      if (entry == NULL)
+        {
+          if (bucket)
+            *bucket = NULL;
+          return NULL;
+        }
+    }
+  else
+    {
+      entry = (DBusHashEntry*) preallocated;
+    }
+
+  add_allocated_entry (table, entry, idx, key, bucket);
 
   return entry;
 }
-           
+
+/* This is g_str_hash from GLib which was
+ * extensively discussed/tested/profiled
+ */
 static unsigned int
 string_hash (const char *str)
 {
-  register unsigned int result;
-  register int c;
+  const char *p = str;
+  unsigned int h = *p;
 
-  /*
-   * I tried a zillion different hash functions and asked many other
-   * people for advice.  Many people had their own favorite functions,
-   * all different, but no-one had much idea why they were good ones.
-   * I chose the one below (multiply by 9 and add new character)
-   * because of the following reasons:
-   *
-   * 1. Multiplying by 10 is perfect for keys that are decimal strings,
-   *    and multiplying by 9 is just about as good.
-   * 2. Times-9 is (shift-left-3) plus (old).  This means that each
-   *    character's bits hang around in the low-order bits of the
-   *    hash value for ever, plus they spread fairly rapidly up to
-   *    the high-order bits to fill out the hash value.  This seems
-   *    works well both for decimal and non-decimal strings.
-   */
+  if (h)
+    for (p += 1; *p != '\0'; p++)
+      h = (h << 5) - h + *p;
 
-  result = 0;
-  while (TRUE)
-    {
-      c = *str;
-      str++;
-      if (c == 0)
-        break;
-      
-      result += (result << 3) + c;
-    }
-  
-  return result;
+  return h;
 }
 
+/** Key comparison function */
+typedef int (* KeyCompareFunc) (const void *key_a, const void *key_b);
+
 static DBusHashEntry*
-find_string_function (DBusHashTable   *table,
-                      void            *key,
-                      dbus_bool_t      create_if_not_found,
-                      DBusHashEntry ***bucket)
+find_generic_function (DBusHashTable        *table,
+                       void                 *key,
+                       unsigned int          idx,
+                       KeyCompareFunc        compare_func,
+                       dbus_bool_t           create_if_not_found,
+                       DBusHashEntry      ***bucket,
+                       DBusPreallocatedHash *preallocated)
 {
   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 ((compare_func == NULL && key == entry->key) ||
+          (compare_func != NULL && (* compare_func) (key, entry->key) == 0))
         {
           if (bucket)
             *bucket = &(table->buckets[idx]);
+
+          if (preallocated)
+            _dbus_hash_table_free_preallocated_entry (table, preallocated);
+          
           return entry;
         }
       
@@ -825,44 +877,44 @@ find_string_function (DBusHashTable   *table,
     }
 
   if (create_if_not_found)
-    entry = add_entry (table, idx, key, bucket);
-
+    entry = add_entry (table, idx, key, bucket, preallocated);
+  else if (preallocated)
+    _dbus_hash_table_free_preallocated_entry (table, preallocated);
+  
   return entry;
 }
 
 static DBusHashEntry*
-find_direct_function (DBusHashTable   *table,
-                      void            *key,
-                      dbus_bool_t      create_if_not_found,
-                      DBusHashEntry ***bucket)
+find_string_function (DBusHashTable        *table,
+                      void                 *key,
+                      dbus_bool_t           create_if_not_found,
+                      DBusHashEntry      ***bucket,
+                      DBusPreallocatedHash *preallocated)
 {
-  DBusHashEntry *entry;
   unsigned int idx;
+  
+  idx = string_hash (key) & table->mask;
 
-  if (bucket)
-    *bucket = NULL;
+  return find_generic_function (table, key, idx,
+                                (KeyCompareFunc) strcmp, create_if_not_found, bucket,
+                                preallocated);
+}
+
+static DBusHashEntry*
+find_direct_function (DBusHashTable        *table,
+                      void                 *key,
+                      dbus_bool_t           create_if_not_found,
+                      DBusHashEntry      ***bucket,
+                      DBusPreallocatedHash *preallocated)
+{
+  unsigned int idx;
   
   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;
-    }
-
-  /* Entry not found.  Add a new one to the bucket. */
-  if (create_if_not_found)
-    entry = add_entry (table, idx, key, bucket);
 
-  return entry;
+  return find_generic_function (table, key, idx,
+                                NULL, create_if_not_found, bucket,
+                                preallocated);
 }
 
 static void
@@ -963,7 +1015,7 @@ rebuild_table (DBusHashTable *table)
               idx = string_hash (entry->key) & table->mask;
               break;
             case DBUS_HASH_INT:
-            case DBUS_HASH_POINTER:
+            case DBUS_HASH_UINTPTR:
               idx = RANDOM_INDEX (table, entry->key);
               break;
             default:
@@ -1001,7 +1053,7 @@ _dbus_hash_table_lookup_string (DBusHashTable *table,
 
   _dbus_assert (table->key_type == DBUS_HASH_STRING);
   
-  entry = (* table->find_function) (table, (char*) key, FALSE, NULL);
+  entry = (* table->find_function) (table, (char*) key, FALSE, NULL, NULL);
 
   if (entry)
     return entry->value;
@@ -1026,7 +1078,7 @@ _dbus_hash_table_lookup_int (DBusHashTable *table,
 
   _dbus_assert (table->key_type == DBUS_HASH_INT);
   
-  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, NULL);
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, NULL, NULL);
 
   if (entry)
     return entry->value;
@@ -1036,7 +1088,7 @@ _dbus_hash_table_lookup_int (DBusHashTable *table,
 
 /**
  * Looks up the value for a given integer in a hash table
- * of type #DBUS_HASH_POINTER. Returns %NULL if the value
+ * of type #DBUS_HASH_UINTPTR. 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.
@@ -1044,14 +1096,14 @@ _dbus_hash_table_lookup_int (DBusHashTable *table,
  * @returns the value of the hash entry.
  */
 void*
-_dbus_hash_table_lookup_pointer (DBusHashTable *table,
-                                 void          *key)
+_dbus_hash_table_lookup_uintptr (DBusHashTable *table,
+                                 uintptr_t      key)
 {
   DBusHashEntry *entry;
 
-  _dbus_assert (table->key_type == DBUS_HASH_POINTER);
+  _dbus_assert (table->key_type == DBUS_HASH_UINTPTR);
   
-  entry = (* table->find_function) (table, key, FALSE, NULL);
+  entry = (* table->find_function) (table, (void*) key, FALSE, NULL, NULL);
 
   if (entry)
     return entry->value;
@@ -1076,7 +1128,7 @@ _dbus_hash_table_remove_string (DBusHashTable *table,
   
   _dbus_assert (table->key_type == DBUS_HASH_STRING);
   
-  entry = (* table->find_function) (table, (char*) key, FALSE, &bucket);
+  entry = (* table->find_function) (table, (char*) key, FALSE, &bucket, NULL);
 
   if (entry)
     {
@@ -1104,7 +1156,7 @@ _dbus_hash_table_remove_int (DBusHashTable *table,
   
   _dbus_assert (table->key_type == DBUS_HASH_INT);
   
-  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, &bucket);
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), FALSE, &bucket, NULL);
   
   if (entry)
     {
@@ -1124,15 +1176,15 @@ _dbus_hash_table_remove_int (DBusHashTable *table,
  * @returns #TRUE if the entry existed
  */
 dbus_bool_t
-_dbus_hash_table_remove_pointer (DBusHashTable *table,
-                                 void          *key)
+_dbus_hash_table_remove_uintptr (DBusHashTable *table,
+                                 uintptr_t      key)
 {
   DBusHashEntry *entry;
   DBusHashEntry **bucket;
   
-  _dbus_assert (table->key_type == DBUS_HASH_POINTER);
+  _dbus_assert (table->key_type == DBUS_HASH_UINTPTR);
   
-  entry = (* table->find_function) (table, key, FALSE, &bucket);
+  entry = (* table->find_function) (table, (void*) key, FALSE, &bucket, NULL);
   
   if (entry)
     {
@@ -1143,7 +1195,6 @@ _dbus_hash_table_remove_pointer (DBusHashTable *table,
     return FALSE;
 }
 
-
 /**
  * Creates a hash entry with the given key and value.
  * The key and value are not copied; they are stored
@@ -1164,24 +1215,17 @@ _dbus_hash_table_insert_string (DBusHashTable *table,
                                 char          *key,
                                 void          *value)
 {
-  DBusHashEntry *entry;
+  DBusPreallocatedHash *preallocated;
 
   _dbus_assert (table->key_type == DBUS_HASH_STRING);
-  
-  entry = (* table->find_function) (table, key, TRUE, NULL);
-
-  if (entry == NULL)
-    return FALSE; /* no memory */
-
-  if (table->free_key_function && entry->key != key)
-    (* table->free_key_function) (entry->key);
 
-  if (table->free_value_function && entry->value != value)
-    (* table->free_value_function) (entry->value);
-      
-  entry->key = key;
-  entry->value = value;
+  preallocated = _dbus_hash_table_preallocate_entry (table);
+  if (preallocated == NULL)
+    return FALSE;
 
+  _dbus_hash_table_insert_string_preallocated (table, preallocated,
+                                               key, value);
+  
   return TRUE;
 }
 
@@ -1209,7 +1253,7 @@ _dbus_hash_table_insert_int (DBusHashTable *table,
 
   _dbus_assert (table->key_type == DBUS_HASH_INT);
   
-  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), TRUE, NULL);
+  entry = (* table->find_function) (table, _DBUS_INT_TO_POINTER (key), TRUE, NULL, NULL);
 
   if (entry == NULL)
     return FALSE; /* no memory */
@@ -1242,32 +1286,108 @@ _dbus_hash_table_insert_int (DBusHashTable *table,
  * @param value the hash entry value.
  */
 dbus_bool_t
-_dbus_hash_table_insert_pointer (DBusHashTable *table,
-                                 void          *key,
+_dbus_hash_table_insert_uintptr (DBusHashTable *table,
+                                 uintptr_t      key,
                                  void          *value)
 {
   DBusHashEntry *entry;
 
-  _dbus_assert (table->key_type == DBUS_HASH_POINTER);
+  _dbus_assert (table->key_type == DBUS_HASH_UINTPTR);
   
-  entry = (* table->find_function) (table, key, TRUE, NULL);
+  entry = (* table->find_function) (table, (void*) key, TRUE, NULL, NULL);
 
   if (entry == NULL)
     return FALSE; /* no memory */
 
-  if (table->free_key_function && entry->key != key)
+  if (table->free_key_function && entry->key != (void*) key)
     (* table->free_key_function) (entry->key);
   
   if (table->free_value_function && entry->value != value)
     (* table->free_value_function) (entry->value);
   
-  entry->key = key;
+  entry->key = (void*) key;
   entry->value = value;
 
   return TRUE;
 }
 
 /**
+ * Preallocate an opaque data blob that allows us to insert into the
+ * hash table at a later time without allocating any memory.
+ *
+ * @param table the hash table
+ * @returns the preallocated data, or #NULL if no memory
+ */
+DBusPreallocatedHash*
+_dbus_hash_table_preallocate_entry (DBusHashTable *table)
+{
+  DBusHashEntry *entry;
+  
+  entry = alloc_entry (table);
+
+  return (DBusPreallocatedHash*) entry;
+}
+
+/**
+ * Frees an opaque DBusPreallocatedHash that was *not* used
+ * in order to insert into the hash table.
+ *
+ * @param table the hash table
+ * @param preallocated the preallocated data
+ */
+void
+_dbus_hash_table_free_preallocated_entry (DBusHashTable        *table,
+                                          DBusPreallocatedHash *preallocated)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (preallocated != NULL);
+  
+  entry = (DBusHashEntry*) preallocated;
+  
+  /* Don't use free_entry(), since this entry has no key/data */
+  _dbus_mem_pool_dealloc (table->entry_pool, entry);
+}
+
+/**
+ * Inserts a string-keyed entry into the hash table, using a
+ * preallocated data block from
+ * _dbus_hash_table_preallocate_entry(). This function cannot fail due
+ * to lack of memory. The DBusPreallocatedHash object is consumed and
+ * should not be reused or freed. Otherwise this function works
+ * just like _dbus_hash_table_insert_string().
+ *
+ * @param table the hash table
+ * @param preallocated the preallocated data
+ * @param key the hash key
+ * @param value the value 
+ */
+void
+_dbus_hash_table_insert_string_preallocated (DBusHashTable        *table,
+                                             DBusPreallocatedHash *preallocated,
+                                             char                 *key,
+                                             void                 *value)
+{
+  DBusHashEntry *entry;
+
+  _dbus_assert (table->key_type == DBUS_HASH_STRING);
+  _dbus_assert (preallocated != NULL);
+  
+  entry = (* table->find_function) (table, key, TRUE, NULL, preallocated);
+
+  _dbus_assert (entry != NULL);
+  
+  if (table->free_key_function && entry->key != key)
+    (* table->free_key_function) (entry->key);
+
+  if (table->free_value_function && entry->value != value)
+    (* table->free_value_function) (entry->value);
+      
+  entry->key = key;
+  entry->value = value;
+}
+
+/**
  * Gets the number of hash entries in a hash table.
  *
  * @param table the hash table.
@@ -1281,7 +1401,7 @@ _dbus_hash_table_get_n_entries (DBusHashTable *table)
 
 /** @} */
 
-#ifdef DBUS_BUILD_TESTS
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
 #include "dbus-test.h"
 #include <stdio.h>
 
@@ -1316,15 +1436,32 @@ _dbus_hash_test (void)
   int i;
   DBusHashTable *table1;
   DBusHashTable *table2;
+  DBusHashTable *table3;
   DBusHashIter iter;
 #define N_HASH_KEYS 5000
-  char keys[N_HASH_KEYS][128];
+  char **keys;
+  dbus_bool_t ret = FALSE;
+
+  keys = dbus_new (char *, N_HASH_KEYS);
+  if (keys == NULL)
+    _dbus_assert_not_reached ("no memory");
+
+  for (i = 0; i < N_HASH_KEYS; i++)
+    {
+      keys[i] = dbus_malloc (128);
+
+      if (keys[i] == NULL)
+       _dbus_assert_not_reached ("no memory");
+    }
 
   printf ("Computing test hash keys...\n");
   i = 0;
   while (i < N_HASH_KEYS)
     {
-      sprintf (keys[i], "Hash key %d", i); 
+      int len;
+
+      len = sprintf (keys[i], "Hash key %d", i);
+      _dbus_assert (*(keys[i] + len) == '\0');
       ++i;
     }
   printf ("... done.\n");
@@ -1332,12 +1469,17 @@ _dbus_hash_test (void)
   table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
                                  dbus_free, dbus_free);
   if (table1 == NULL)
-    return FALSE;
+    goto out;
 
   table2 = _dbus_hash_table_new (DBUS_HASH_INT,
                                  NULL, dbus_free);
   if (table2 == NULL)
-    return FALSE;
+    goto out;
+
+  table3 = _dbus_hash_table_new (DBUS_HASH_UINTPTR,
+                                 NULL, dbus_free);
+  if (table3 == NULL)
+    goto out;
 
   /* Insert and remove a bunch of stuff, counting the table in between
    * to be sure it's not broken and that iteration works
@@ -1350,25 +1492,34 @@ _dbus_hash_test (void)
 
       key = _dbus_strdup (keys[i]);
       if (key == NULL)
-        return FALSE;
+        goto out;
       value = _dbus_strdup ("Value!");
       if (value == NULL)
-        return FALSE;
+        goto out;
       
       if (!_dbus_hash_table_insert_string (table1,
                                            key, value))
-        return FALSE;
+        goto out;
 
       value = _dbus_strdup (keys[i]);
       if (value == NULL)
-        return FALSE;
+        goto out;
       
       if (!_dbus_hash_table_insert_int (table2,
                                         i, value))
-        return FALSE;
+        goto out;
+
+      value = _dbus_strdup (keys[i]);
+      if (value == NULL)
+        goto out;
       
+      if (!_dbus_hash_table_insert_uintptr (table3,
+                                          i, value))
+        goto out;
+
       _dbus_assert (count_entries (table1) == i + 1);
       _dbus_assert (count_entries (table2) == i + 1);
+      _dbus_assert (count_entries (table3) == i + 1);
 
       value = _dbus_hash_table_lookup_string (table1, keys[i]);
       _dbus_assert (value != NULL);
@@ -1377,7 +1528,11 @@ _dbus_hash_test (void)
       value = _dbus_hash_table_lookup_int (table2, i);
       _dbus_assert (value != NULL);
       _dbus_assert (strcmp (value, keys[i]) == 0);
-      
+
+      value = _dbus_hash_table_lookup_uintptr (table3, i);
+      _dbus_assert (value != NULL);
+      _dbus_assert (strcmp (value, keys[i]) == 0);
+
       ++i;
     }
 
@@ -1389,19 +1544,25 @@ _dbus_hash_test (void)
 
       _dbus_hash_table_remove_int (table2, i);
 
+      _dbus_hash_table_remove_uintptr (table3, i);
+
       _dbus_assert (count_entries (table1) == i);
       _dbus_assert (count_entries (table2) == i);
+      _dbus_assert (count_entries (table3) == i);
 
       --i;
     }
 
   _dbus_hash_table_ref (table1);
   _dbus_hash_table_ref (table2);
+  _dbus_hash_table_ref (table3);
   _dbus_hash_table_unref (table1);
   _dbus_hash_table_unref (table2);
+  _dbus_hash_table_unref (table3);
   _dbus_hash_table_unref (table1);
   _dbus_hash_table_unref (table2);
-
+  _dbus_hash_table_unref (table3);
+  table3 = NULL;
 
   /* Insert a bunch of stuff then check
    * that iteration works correctly (finds the right
@@ -1410,12 +1571,12 @@ _dbus_hash_test (void)
   table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
                                  dbus_free, dbus_free);
   if (table1 == NULL)
-    return FALSE;
+    goto out;
   
   table2 = _dbus_hash_table_new (DBUS_HASH_INT,
                                  NULL, dbus_free);
   if (table2 == NULL)
-    return FALSE;
+    goto out;
   
   i = 0;
   while (i < 5000)
@@ -1425,22 +1586,22 @@ _dbus_hash_test (void)
       
       key = _dbus_strdup (keys[i]);
       if (key == NULL)
-        return FALSE;
+        goto out;
       value = _dbus_strdup ("Value!");
       if (value == NULL)
-        return FALSE;
+        goto out;
       
       if (!_dbus_hash_table_insert_string (table1,
                                            key, value))
-        return FALSE;
+        goto out;
 
       value = _dbus_strdup (keys[i]);
       if (value == NULL)
-        return FALSE;
+        goto out;
       
       if (!_dbus_hash_table_insert_int (table2,
                                         i, value))
-        return FALSE;
+        goto out;
       
       _dbus_assert (count_entries (table1) == i + 1);
       _dbus_assert (count_entries (table2) == i + 1);
@@ -1461,7 +1622,7 @@ _dbus_hash_test (void)
 
       value = _dbus_strdup ("Different value!");
       if (value == NULL)
-        return FALSE;
+        goto out;
       
       _dbus_hash_iter_set_value (&iter, value);
 
@@ -1489,7 +1650,7 @@ _dbus_hash_test (void)
 
       value = _dbus_strdup ("Different value!");
       if (value == NULL)
-        return FALSE;
+        goto out;
       
       _dbus_hash_iter_set_value (&iter, value);
 
@@ -1516,15 +1677,15 @@ _dbus_hash_test (void)
             
       key = _dbus_strdup (keys[i]);
       if (key == NULL)
-        return FALSE;
+        goto out;
 
       value = _dbus_strdup ("Value!");
       if (value == NULL)
-        return FALSE;
+        goto out;
       
       if (!_dbus_hash_table_insert_string (table1,
                                            key, value))
-        return FALSE;
+        goto out;
       
       ++i;
     }
@@ -1537,20 +1698,20 @@ _dbus_hash_test (void)
       
       key = _dbus_strdup (keys[i]);
       if (key == NULL)
-        return FALSE;
+        goto out;
       value = _dbus_strdup ("Value!");
       if (value == NULL)
-        return FALSE;
+        goto out;
 
       if (!_dbus_hash_table_remove_string (table1, keys[i]))
-        return FALSE;
+        goto out;
       
       if (!_dbus_hash_table_insert_string (table1,
                                            key, value))
-        return FALSE;
+        goto out;
 
       if (!_dbus_hash_table_remove_string (table1, keys[i]))
-        return FALSE;
+        goto out;
       
       _dbus_assert (_dbus_hash_table_get_n_entries (table1) == i);
       
@@ -1568,12 +1729,12 @@ _dbus_hash_test (void)
   table1 = _dbus_hash_table_new (DBUS_HASH_STRING,
                                  dbus_free, dbus_free);
   if (table1 == NULL)
-    return FALSE;
+    goto out;
   
   table2 = _dbus_hash_table_new (DBUS_HASH_INT,
                                  NULL, dbus_free);
   if (table2 == NULL)
-    return FALSE;
+    goto out;
   
   i = 0;
   while (i < 3000)
@@ -1583,24 +1744,24 @@ _dbus_hash_test (void)
 
       key = _dbus_strdup (keys[i]);
       if (key == NULL)
-        return FALSE;
+        goto out;
       value = _dbus_strdup ("Value!");
       if (value == NULL)
-        return FALSE;
+        goto out;
       
       if (!_dbus_hash_iter_lookup (table1,
                                    key, TRUE, &iter))
-        return FALSE;
+        goto out;
       _dbus_assert (_dbus_hash_iter_get_value (&iter) == NULL);
       _dbus_hash_iter_set_value (&iter, value);
 
       value = _dbus_strdup (keys[i]);
       if (value == NULL)
-        return FALSE;
+        goto out;
 
       if (!_dbus_hash_iter_lookup (table2,
                                    _DBUS_INT_TO_POINTER (i), TRUE, &iter))
-        return FALSE;
+        goto out;
       _dbus_assert (_dbus_hash_iter_get_value (&iter) == NULL);
       _dbus_hash_iter_set_value (&iter, value); 
       
@@ -1608,7 +1769,7 @@ _dbus_hash_test (void)
       _dbus_assert (count_entries (table2) == i + 1);
 
       if (!_dbus_hash_iter_lookup (table1, keys[i], FALSE, &iter))
-        return FALSE;
+        goto out;
       
       value = _dbus_hash_iter_get_value (&iter);
       _dbus_assert (value != NULL);
@@ -1621,7 +1782,7 @@ _dbus_hash_test (void)
         ;
       
       if (!_dbus_hash_iter_lookup (table2, _DBUS_INT_TO_POINTER (i), FALSE, &iter))
-        return FALSE;
+        goto out;
 
       value = _dbus_hash_iter_get_value (&iter);
       _dbus_assert (value != NULL);
@@ -1656,8 +1817,15 @@ _dbus_hash_test (void)
   _dbus_hash_table_unref (table1);
   _dbus_hash_table_unref (table2);
 
+  ret = TRUE;
+
+ out:
+  for (i = 0; i < N_HASH_KEYS; i++)
+    dbus_free (keys[i]);
+
+  dbus_free (keys);
   
-  return TRUE;
+  return ret;
 }
 
-#endif /* DBUS_BUILD_TESTS */
+#endif /* DBUS_ENABLE_EMBEDDED_TESTS */