commit-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

hurd-l4/libhurd-ihash ChangeLog ihash.c ihash.h


From: Marcus Brinkmann
Subject: hurd-l4/libhurd-ihash ChangeLog ihash.c ihash.h
Date: Tue, 26 Aug 2003 18:47:18 -0400

CVSROOT:        /cvsroot/hurd
Module name:    hurd-l4
Branch:         
Changes by:     Marcus Brinkmann <address@hidden>       03/08/26 18:47:18

Modified files:
        libhurd-ihash  : ChangeLog ihash.c ihash.h 

Log message:
        2003-08-26  Marcus Brinkmann  <address@hidden>
        
        * ihash.h: Include <limits.h>.
        (HURD_IHASH_NO_LOCP): New macro.
        (HURD_IHASH_INITIALIZER): Take locp offset as argument.
        Initialize HT->nr_items, HT->max_load and HT->locp_offset.
        (HURD_IHASH_MAX_LOAD_DEFAULT): New macro.
        (struct _hurd_ihash_item): New structure.
        (_hurd_ihash_item_t): New type.
        (struct hurd_ihash): New fields NR_ITEMS, ITEMS, LOCP_OFFSET and
        MAX_LOAD.  Remove KEYS, TABLE and LOCPS.
        (hurd_ihash_set_max_load): New function.
        (HURD_IHASH_ITERATE): Rewrite to use ITEMS instead TABLE and KEYS.
        (hurd_ihash_init): Take locp_offs argument in prototype.
        (hurd_ihash_create): Likewise.
        (hurd_ihash_add): Don't take locp argument in prototype.
        (hurd_ihash_set_max_load): New prototype.
        * ihash.c (HASH, REHASH): Macros removed.
        (ihash_sizes): Change table to list prime numbers that are 3
        modulo 4.
        (index_empty): Use HT->items instead HT->table.
        (index_valid): Likewise.
        (find_index): Use quadratic probing.
        (locp_remove): New helper function.
        (hurd_ihash_init): Take locp_offs as argument.  Initialize
        HT->locp_offset, HT->nr_items and HT->max_load.
        (hurd_ihash_destroy): Free HT->items, but not anything else.
        (hurd_ihash_create): Take locp_offs as argument and pass it to
        hurd_ihash_init.
        (hurd_ihash_set_max_load): New function.
        (add_one): Don't take a locp argument anymore.  Use quadratic
        probing.  Call locp_remove instead duplicating the code.
        Increment HT->nr_items.
        (hurd_ihash_add): Don't take a locp argument anymore.  Check
        maximum load factor before adding element.  Use ITEMS and not KEYS
        and TABLE.  Don't allocate memory for locp.  Use calloc instead of
        malloc and initialization.
        (hurd_ihash_remove): Call locp_remove instead hurd_ihash_locp_remove.
        (hurd_ihash_locp_remove): Call locp_remove.

CVSWeb URLs:
http://savannah.gnu.org/cgi-bin/viewcvs/hurd/hurd-l4/libhurd-ihash/ChangeLog.diff?tr1=1.3&tr2=1.4&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/hurd/hurd-l4/libhurd-ihash/ihash.c.diff?tr1=1.4&tr2=1.5&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/hurd/hurd-l4/libhurd-ihash/ihash.h.diff?tr1=1.6&tr2=1.7&r1=text&r2=text

Patches:
Index: hurd-l4/libhurd-ihash/ChangeLog
diff -u hurd-l4/libhurd-ihash/ChangeLog:1.3 hurd-l4/libhurd-ihash/ChangeLog:1.4
--- hurd-l4/libhurd-ihash/ChangeLog:1.3 Sun Aug 17 01:47:32 2003
+++ hurd-l4/libhurd-ihash/ChangeLog     Tue Aug 26 18:47:18 2003
@@ -1,3 +1,43 @@
+2003-08-26  Marcus Brinkmann  <address@hidden>
+
+       * ihash.h: Include <limits.h>.
+       (HURD_IHASH_NO_LOCP): New macro.
+       (HURD_IHASH_INITIALIZER): Take locp offset as argument.
+       Initialize HT->nr_items, HT->max_load and HT->locp_offset.
+       (HURD_IHASH_MAX_LOAD_DEFAULT): New macro.
+       (struct _hurd_ihash_item): New structure.
+       (_hurd_ihash_item_t): New type.
+       (struct hurd_ihash): New fields NR_ITEMS, ITEMS, LOCP_OFFSET and
+       MAX_LOAD.  Remove KEYS, TABLE and LOCPS.
+       (hurd_ihash_set_max_load): New function.
+       (HURD_IHASH_ITERATE): Rewrite to use ITEMS instead TABLE and KEYS.
+       (hurd_ihash_init): Take locp_offs argument in prototype.
+       (hurd_ihash_create): Likewise.
+       (hurd_ihash_add): Don't take locp argument in prototype.
+       (hurd_ihash_set_max_load): New prototype.
+       * ihash.c (HASH, REHASH): Macros removed.
+       (ihash_sizes): Change table to list prime numbers that are 3
+       modulo 4.
+       (index_empty): Use HT->items instead HT->table.
+       (index_valid): Likewise.
+       (find_index): Use quadratic probing.
+       (locp_remove): New helper function.
+       (hurd_ihash_init): Take locp_offs as argument.  Initialize
+       HT->locp_offset, HT->nr_items and HT->max_load.
+       (hurd_ihash_destroy): Free HT->items, but not anything else.
+       (hurd_ihash_create): Take locp_offs as argument and pass it to
+       hurd_ihash_init.
+       (hurd_ihash_set_max_load): New function.
+       (add_one): Don't take a locp argument anymore.  Use quadratic
+       probing.  Call locp_remove instead duplicating the code.
+       Increment HT->nr_items.
+       (hurd_ihash_add): Don't take a locp argument anymore.  Check
+       maximum load factor before adding element.  Use ITEMS and not KEYS
+       and TABLE.  Don't allocate memory for locp.  Use calloc instead of
+       malloc and initialization.
+       (hurd_ihash_remove): Call locp_remove instead hurd_ihash_locp_remove.
+       (hurd_ihash_locp_remove): Call locp_remove.
+       
 2003-08-17  Marcus Brinkmann  <address@hidden>
 
        * ihash.h (HURD_IHASH_INITIALIZER): New macro.
Index: hurd-l4/libhurd-ihash/ihash.c
diff -u hurd-l4/libhurd-ihash/ihash.c:1.4 hurd-l4/libhurd-ihash/ihash.c:1.5
--- hurd-l4/libhurd-ihash/ihash.c:1.4   Sun Aug 17 01:14:48 2003
+++ hurd-l4/libhurd-ihash/ihash.c       Tue Aug 26 18:47:18 2003
@@ -30,48 +30,48 @@
 #include <hurd/ihash.h>
 
 
-/* The odd prime numbers greater than twice the last and less than
-   2^40 (nobody needs more than 640 GB of memory).  */
+/* The prime numbers of the form 4 * i + 3 for some i, all greater
+   than twice the previous one and smaller than 2^40 (for now).  */
 static const uint64_t ihash_sizes[] =
 {
   3,
   7,
-  17,
-  37,
-  79,
-  163,
-  331,
-  673,
-  1361,
-  2729,
-  5471,
-  10949,
-  21911,
-  43853,
-  87719,
-  175447,
-  350899,
-  701819,
-  1403641,
-  2807303,
-  5614657,
-  11229331,
-  22458671,
-  44917381,
-  89834777,
-  179669557,
-  359339171,
-  718678369,
-  1437356741,
-  UINT64_C (2874713497),
-  UINT64_C (5749427029),
-  UINT64_C (11498854069),
-  UINT64_C (22997708177),
-  UINT64_C (45995416409),
-  UINT64_C (91990832831),
-  UINT64_C (183981665689),
-  UINT64_C (367963331389),
-  UINT64_C (735926662813)
+  19,
+  43,
+  103,
+  211,
+  431,
+  863,
+  1747,
+  3499,
+  7019,
+  14051,
+  28111,
+  56239,
+  112507,
+  225023,
+  450067,
+  900139,
+  1800311,
+  3600659,
+  7201351,
+  14402743,
+  28805519,
+  57611039,
+  115222091,
+  230444239,
+  460888499,
+  921777067,
+  1843554151,
+  UINT64_C (3687108307),
+  UINT64_C (7374216631),
+  UINT64_C (14748433279),
+  UINT64_C (29496866579),
+  UINT64_C (58993733159),
+  UINT64_C (117987466379),
+  UINT64_C (235974932759),
+  UINT64_C (471949865531),
+  UINT64_C (943899731087)
 };
 
 
@@ -79,23 +79,13 @@
                                          / sizeof ihash_sizes[0]);
 
 
-/* Return an initial index in the hash table HT for the key KEY, to
-   search for an entry.  */
-#define HASH(ht, key)          ((key) % (ht)->size)
-
-
-/* Return the next possible index in the hash table HT for the key
-   KEY, given the previous one.  */
-#define REHASH(ht, key, idx)   (((idx) + (key)) % (ht)->size)
-
-
 /* Return 1 if the slot with the index IDX in the hash table HT is
    empty, and 0 otherwise.  */
 static inline int
 index_empty (hurd_ihash_t ht, unsigned int idx)
 {
-  return ht->table[idx] == _HURD_IHASH_EMPTY
-    || ht->table[idx] == _HURD_IHASH_DELETED;
+  return ht->items[idx].value == _HURD_IHASH_EMPTY
+    || ht->items[idx].value == _HURD_IHASH_DELETED;
 }
 
 
@@ -104,7 +94,7 @@
 static inline int
 index_valid (hurd_ihash_t ht, unsigned int idx, hurd_ihash_key_t key)
 {
-  return !index_empty (ht, idx) && ht->keys[idx] == key;
+  return !index_empty (ht, idx) && ht->items[idx].key == key;
 }
 
 
@@ -115,29 +105,70 @@
 find_index (hurd_ihash_t ht, hurd_ihash_key_t key)
 {
   unsigned int idx;
-  unsigned int first_idx;
-
-  first_idx = HASH (ht, key);
-  idx = first_idx;
+  unsigned int i;
+  unsigned int up_idx;
+  unsigned int down_idx;
+
+  idx = key % ht->size;
+
+  if (ht->items[idx].value == _HURD_IHASH_EMPTY || ht->items[idx].key == key)
+    return idx;
+
+  /* Instead of calculating idx + 1, idx + 4, idx + 9, ..., idx + i^2,
+     we add 1, 3, 5, 7, etc to the previous index.  We do this in both
+     directions separately.  */
+  i = 1;
+  up_idx = idx;
+  down_idx = idx;
 
-  while (ht->table[idx] != _HURD_IHASH_EMPTY && ht->keys[idx] != key)
+  do
     {
-      idx = REHASH (ht, key, idx);
-      if (idx == first_idx)
-       break;
+      up_idx = (up_idx + i) % ht->size;
+      if (ht->items[up_idx].value == _HURD_IHASH_EMPTY
+         || ht->items[up_idx].key == key)
+       return up_idx;
+
+      if (down_idx < i)
+       down_idx += ht->size;
+      down_idx = (down_idx - i) % ht->size;
+      if (ht->items[down_idx].value == _HURD_IHASH_EMPTY
+         || ht->items[down_idx].key == key)
+       return down_idx;
+
+      /* After (ht->size - 1) / 2 iterations, this will be 0.  */
+      i = (i + 2) % ht->size;
     }
+  while (i);
 
+  /* If we end up here, the item could not be found.  Return any
+     invalid index.  */
   return idx;
 }
 
+
+/* Remove the entry pointed to by the location pointer LOCP from the
+   hashtable HT.  LOCP is the location pointer of which the address
+   was provided to hurd_ihash_add().  */
+static inline void
+locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp)
+{
+  if (ht->cleanup)
+    (*ht->cleanup) (*locp, ht->cleanup_data);
+  *locp = _HURD_IHASH_DELETED;
+  ht->nr_items--;
+}
+
 
 /* Construction and destruction of hash tables.  */
 
 /* Initialize the hash table at address HT.  */
 void
-hurd_ihash_init (hurd_ihash_t ht)
+hurd_ihash_init (hurd_ihash_t ht, off_t locp_offs)
 {
+  ht->nr_items = 0;
   ht->size = 0;
+  ht->locp_offset = locp_offs;
+  ht->max_load = HURD_IHASH_MAX_LOAD_DEFAULT;
   ht->cleanup = 0;
 }
 
@@ -158,24 +189,20 @@
     }
 
   if (ht->size > 0)
-    {
-      free (ht->table);
-      free (ht->keys);
-      free (ht->locps);
-    }
+    free (ht->items);
 }
 
 
 /* Create a hash table, initialize it and return it in HT.  If a
    memory allocation error occurs, ENOMEM is returned, otherwise 0.  */
 error_t
-hurd_ihash_create (hurd_ihash_t *ht)
+hurd_ihash_create (hurd_ihash_t *ht, off_t locp_offs)
 {
   *ht = malloc (sizeof (struct hurd_ihash));
   if (*ht == NULL)
     return ENOMEM;
 
-  hurd_ihash_init (*ht);
+  hurd_ihash_init (*ht, locp_offs);
 
   return 0;
 }
@@ -203,42 +230,91 @@
 }
 
 
+/* Set the maximum load factor in percent to MAX_LOAD, which
+   should be between 1 and 100.  The default is
+   HURD_IHASH_MAX_LOAD_DEFAULT.  New elements are only added to the
+   hash table while the number of hashed elements is that much percent
+   of the total size of the hash table.  If more elements are added,
+   the hash table is first expanded and reorganized.  A MAX_LOAD of
+   100 will always fill the whole table before enlarging it, but note
+   that this will increase the cost of operations significantly when
+   the table is almost full.
+
+   If the value is set to a smaller value than the current load
+   factor, the next reorganization will happen when a new item is
+   added to the hash table.  */
+void
+hurd_ihash_set_max_load (hurd_ihash_t ht, unsigned int max_load)
+{
+  ht->max_load = max_load;
+}
+
+
 /* Helper function for hurd_ihash_add.  Return 1 if the item was
    added, and 0 if it could not be added because no empty slot was
-   found.  The arguments are identical to hurd_ihash_add.  */
+   found.  The arguments are identical to hurd_ihash_add.
+
+   We are using open address hashing.  As the hash function we use the
+   division method with quadratic probe.  This is guaranteed to try
+   all slots in the hash table if the prime number is 3 mod 4.  */
 static inline int
-add_one (hurd_ihash_t ht, hurd_ihash_key_t key,
-        hurd_ihash_value_t item, hurd_ihash_locp_t *locp)
+add_one (hurd_ihash_t ht, hurd_ihash_key_t key, hurd_ihash_value_t value)
 {
   unsigned int idx;
-  unsigned int first_idx;
   unsigned int first_free;
 
-  first_idx = HASH (ht, key);
-  idx = first_idx;
-  first_free = first_idx;
-
-  /* Search for for an empty or deleted space.  Even if a deleted
-     space is found, keep going until we know if the same key
-     already exists.  */
-  while (ht->table[idx] != _HURD_IHASH_EMPTY
-        && ht->keys[idx] != key)
-    {
-      if (ht->table[idx] == _HURD_IHASH_DELETED)
-       first_free = idx;
-      
-      idx = REHASH (ht, key, idx);
-      if (idx == first_idx)
-       break;
-    }
+  idx = key % ht->size;
+  first_free = idx;
 
-  if (ht->keys[idx] == key)
+  if (ht->items[idx].value != _HURD_IHASH_EMPTY && ht->items[idx].key != key)
     {
-      if (ht->cleanup)
-       ht->cleanup (ht->table[idx], ht->cleanup_data);
-      ht->table[idx] = _HURD_IHASH_DELETED;
+      /* Instead of calculating idx + 1, idx + 4, idx + 9, ..., idx +
+         i^2, we add 1, 3, 5, 7, ... 2 * i - 1 to the previous index.
+         We do this in both directions separately.  */
+      unsigned int i = 1;
+      unsigned int up_idx = idx;
+      unsigned int down_idx = idx;
+ 
+      do
+       {
+         up_idx = (up_idx + i) % ht->size;
+         if (ht->items[up_idx].value == _HURD_IHASH_EMPTY
+             || ht->items[up_idx].key == key)
+           {
+             idx = up_idx;
+             break;
+           }
+         if (first_free == idx
+             && ht->items[up_idx].value == _HURD_IHASH_DELETED)
+           first_free = up_idx;
+
+         if (down_idx < i)
+           down_idx += ht->size;
+         down_idx = (down_idx - i) % ht->size;
+         if (down_idx < 0)
+           down_idx += ht->size;
+         else
+           down_idx %= ht->size;
+         if (ht->items[down_idx].value == _HURD_IHASH_EMPTY
+             || ht->items[down_idx].key == key)
+           {
+             idx = down_idx;
+             break;
+           }
+         if (first_free == idx
+             && ht->items[down_idx].value == _HURD_IHASH_DELETED)
+           first_free = down_idx;
+
+         /* After (ht->size - 1) / 2 iterations, this will be 0.  */
+         i = (i + 2) % ht->size;
+       }
+      while (i);
     }
 
+  /* Remove the old entry for this key if necessary.  */
+  if (index_valid (ht, idx, key))
+    locp_remove (ht, &ht->items[idx].value);
+
   /* If we have not found an empty slot, maybe the last one we
      looked at was empty (or just got deleted).  */
   if (!index_empty (ht, first_free))
@@ -246,13 +322,14 @@
  
   if (index_empty (ht, first_free))
     {
-      ht->table[first_free] = item;
-      ht->keys[first_free] = key;
-      ht->locps[first_free] = locp;
-      
-      if (locp)
-       *locp = &ht->table[first_free];
-      
+      ht->nr_items++;
+      ht->items[first_free].value = value;
+      ht->items[first_free].key = key;
+
+      if (ht->locp_offset != HURD_IHASH_NO_LOCP)
+       *((hurd_ihash_locp_t) (((char *) value) + ht->locp_offset))
+         = &ht->items[first_free].value;
+
       return 1;
     }
 
@@ -260,75 +337,61 @@
 }
 
   
-/* Add ITEM to the hash table HT under the key KEY.  LOCP is the
-   address of a location pointer in ITEM; If non-NULL, it will be
-   filled with a pointer that may be used as an argument to
-   hurd_ihash_locp_remove().  (The variable pointed to by LOCP may be
-   written to subsequently between this call and when the element is
-   deleted).  If there already is an item under this key, call the
-   cleanup function (if any) for it before overriding the value.  If a
-   memory allocation error occurs, ENOMEM is returned, otherwise 0.  */
+/* Add ITEM to the hash table HT under the key KEY.  If there already
+   is an item under this key, call the cleanup function (if any) for
+   it before overriding the value.  If a memory allocation error
+   occurs, ENOMEM is returned, otherwise 0.  */
 error_t
-hurd_ihash_add (hurd_ihash_t ht, hurd_ihash_key_t key,
-               hurd_ihash_value_t item, hurd_ihash_locp_t *locp)
+hurd_ihash_add (hurd_ihash_t ht, hurd_ihash_key_t key, hurd_ihash_value_t item)
 {
   struct hurd_ihash old_ht = *ht;
   int was_added;
   int i;
 
-  if (ht->size && add_one (ht, key, item, locp))
-    return 0;
+  if (ht->size)
+    {
+      /* Only fill the hash table up to its maximum load factor.  */
+      if (ht->nr_items * 100 / ht->size <= ht->max_load)
+       if (add_one (ht, key, item))
+         return 0;
+    }
 
   /* The hash table is too small, and we have to increase it.  */
   for (i = 0; i < ihash_nsizes; i++)
     if (ihash_sizes[i] > old_ht.size)
       break;
   if (i == ihash_nsizes
-      || ihash_sizes[i] > SIZE_MAX / sizeof (hurd_ihash_value_t)
-      || ihash_sizes[i] > SIZE_MAX / sizeof (hurd_ihash_key_t)
-      || ihash_sizes[i] > SIZE_MAX / sizeof (hurd_ihash_locp_t *))
+      || ihash_sizes[i] > SIZE_MAX / sizeof (struct _hurd_ihash_item))
     return ENOMEM;             /* Surely will be true momentarily.  */
-    
+
+  ht->nr_items = 0;
   ht->size = ihash_sizes[i];
-  ht->table = malloc (ht->size * sizeof (hurd_ihash_value_t));
-  ht->keys = malloc (ht->size * sizeof (hurd_ihash_key_t));
-  ht->locps = malloc (ht->size * sizeof (hurd_ihash_locp_t *));
+  /* calloc() will initialize all values to _HURD_IHASH_EMPTY implicitely.  */
+  ht->items = calloc (ht->size, sizeof (struct _hurd_ihash_item));
 
-  if (ht->table == NULL || ht->locps == NULL || ht->keys == NULL)
+  if (ht->items == NULL)
     {
-      if (ht->table)
-       free (ht->table);
-      if (ht->keys)
-       free(ht->keys);
-      if (ht->locps)
-       free (ht->locps);
+      if (ht->items)
+       free(ht->items);
 
       *ht = old_ht;
       return ENOMEM;
     }
 
-  for (i = 0; i < ht->size; i++)
-    ht->table[i] = _HURD_IHASH_EMPTY;
-
   /* We have to rehash the old entries.  */
   for (i = 0; i < old_ht.size; i++)
     if (!index_empty (&old_ht, i))
       {
-       was_added = add_one (ht, old_ht.keys[i], old_ht.table[i],
-                            old_ht.locps[i]);
+       was_added = add_one (ht, old_ht.items[i].key, old_ht.items[i].value);
        assert (was_added);
       }
 
   /* Finally add the new element!  */
-  was_added = add_one (ht, key, item, locp);
+  was_added = add_one (ht, key, item);
   assert (was_added);
 
   if (old_ht.size > 0)
-    {
-      free (old_ht.table);
-      free (old_ht.keys);
-      free (old_ht.locps);
-    }
+    free (old_ht.items);
 
   return 0;
 }
@@ -344,7 +407,7 @@
   else
     {
       int idx = find_index (ht, key);
-      return index_valid (ht, idx, key) ? ht->table[idx] : NULL;
+      return index_valid (ht, idx, key) ? ht->items[idx].value : NULL;
     }
 }
 
@@ -358,7 +421,7 @@
 
   if (index_valid (ht, idx, key))
     {
-      hurd_ihash_locp_remove (ht, &ht->table[idx]);
+      locp_remove (ht, &ht->items[idx].value);
       return 1;
     }
   else
@@ -369,13 +432,9 @@
 /* Remove the entry pointed to by the location pointer LOCP from the
    hashtable HT.  LOCP is the location pointer of which the address
    was provided to hurd_ihash_add().  This call is faster than
-   hurd_ihash_remove().  HT can be NULL, in which case the call still
-   succeeds, but the cleanup function (if any) will not be invoked in
-   this case.  */
+   hurd_ihash_remove().  */
 void
 hurd_ihash_locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp)
 {
-  if (ht && ht->cleanup)
-    (*ht->cleanup) (*locp, ht->cleanup_data);
-  *locp = _HURD_IHASH_DELETED;
+  locp_remove (ht, locp);
 }
Index: hurd-l4/libhurd-ihash/ihash.h
diff -u hurd-l4/libhurd-ihash/ihash.h:1.6 hurd-l4/libhurd-ihash/ihash.h:1.7
--- hurd-l4/libhurd-ihash/ihash.h:1.6   Sun Aug 17 01:47:32 2003
+++ hurd-l4/libhurd-ihash/ihash.h       Tue Aug 26 18:47:18 2003
@@ -24,17 +24,19 @@
 
 #include <errno.h>
 #include <sys/types.h>
+#include <limits.h>
 
 
 /* The type of the values corresponding to the keys.  Must be a
-   pointer type.  */
+   pointer type.  The values (hurd_ihash_value_t) 0 and
+   (hurd_ihash_value_t) ~0 are reserved for the implementation.  */
 typedef void *hurd_ihash_value_t;
 
-/* When an entry in the table TABLE of a hash table is
-   _HURD_IHASH_EMPTY or _HURD_IHASH_DELETED, then the location is
-   available, and none of the other arrays are valid at that index.
-   The difference is that searches continue though HASH_DEL, but stop
-   at _HURD_IHASH_EMPTY.  */
+/* When an entry in the hash table is _HURD_IHASH_EMPTY or
+   _HURD_IHASH_DELETED, then the location is available, and none of
+   the other arrays are valid at that index.  The difference is that
+   searches continue though _HURD_IHASH_DELETED, but stop at
+   _HURD_IHASH_EMPTY.  */
 #define _HURD_IHASH_EMPTY      ((hurd_ihash_value_t) 0)
 #define _HURD_IHASH_DELETED    ((hurd_ihash_value_t) -1)
 
@@ -53,21 +55,35 @@
 typedef void (*hurd_ihash_cleanup_t) (hurd_ihash_value_t value, void *arg);
 
 
-struct hurd_ihash
+struct _hurd_ihash_item
 {
-  /* An array storing the elements in the hash table.  */
-  hurd_ihash_value_t *table;
+  /* The value of this hash item.  Must be the first element of
+     the struct for the HURD_IHASH_ITERATE macro.  */
+  hurd_ihash_value_t value;
+
+  /* The integer key of this hash item.  */
+  hurd_ihash_key_t key;
+};
+typedef struct _hurd_ihash_item *_hurd_ihash_item_t;
 
-  /* An array storing the integer key for each element.  */
-  hurd_ihash_key_t *keys;
 
-  /* An array storing pointers to the location pointers for each
-     element.  These are used as cookies for quick'n'easy removal.  */
-  hurd_ihash_locp_t **locps;
+struct hurd_ihash
+{
+  /* The number of hashed elements.  */
+  size_t nr_items;
 
-  /* The length of the three arrays TABLE, KEYS and LOCPS.  */
+  /* An array of (key, value) pairs.  */
+  _hurd_ihash_item_t items;
+
+  /* The length of the array ITEMS.  */
   size_t size;
 
+  /* The offset of the location pointer from the hash value.  */
+  off_t locp_offset;
+
+  /* The maximum load factor in percent.  */
+  int max_load;
+
   /* When freeing or overwriting an element, this function is called
      with the value as the first argument, and CLEANUP_DATA as the
      second argument.  This does not happen if CLEANUP is NULL.  */
@@ -79,11 +95,25 @@
 
 /* Construction and destruction of hash tables.  */
 
-#define HURD_IHASH_INITIALIZER \
-  { .size = 0, .cleanup = (hurd_ihash_cleanup_t) 0 }
+/* The default value for the maximum load factor in percent.  */
+#define HURD_IHASH_MAX_LOAD_DEFAULT 80
+
 
-/* Initialize the hash table at address HT.  */
-void hurd_ihash_init (hurd_ihash_t ht);
+#define HURD_IHASH_NO_LOCP     INT_MIN
+
+
+#define HURD_IHASH_INITIALIZER(locp_offs)                      \
+  { .nr_items = 0, .size = 0, .cleanup = (hurd_ihash_cleanup_t) 0,     \
+    .max_load = HURD_IHASH_MAX_LOAD_DEFAULT,                           \
+    .locp_offset = (locp_offs)}
+
+
+/* Initialize the hash table at address HT.  If LOCP_OFFSET is not
+   HURD_IHASH_NO_LOCP, then this is an offset (in bytes) from the
+   address of a hash value where a location pointer can be found.  The
+   location pointer must be of type hurd_ihash_locp_t and can be used
+   for fast removal with hurd_ihash_locp_remove().  */
+void hurd_ihash_init (hurd_ihash_t ht, off_t locp_offs);
 
 
 /* Destroy the hash table at address HT.  This first removes all
@@ -92,9 +122,14 @@
 void hurd_ihash_destroy (hurd_ihash_t ht);
 
 
-/* Create a hash table, initialize it and return it in HT.  If a
-   memory allocation error occurs, ENOMEM is returned, otherwise 0.  */
-error_t hurd_ihash_create (hurd_ihash_t *ht);
+/* Create a hash table, initialize it and return it in HT.  If
+   LOCP_OFFSET is not HURD_IHASH_NO_LOCP, then this is an offset (in
+   bytes) from the address of a hash value where a location pointer
+   can be found.  The location pointer must be of type
+   hurd_ihash_locp_t and can be used for fast removal with
+   hurd_ihash_locp_remove().  If a memory allocation error occurs,
+   ENOMEM is returned, otherwise 0.  */
+error_t hurd_ihash_create (hurd_ihash_t *ht, off_t locp_offs);
 
 
 /* Destroy the hash table HT and release the memory allocated for it
@@ -102,6 +137,8 @@
 void hurd_ihash_free (hurd_ihash_t ht);
 
 
+/* Configuration of the hash table.  */
+
 /* Set the cleanup function for the hash table HT to CLEANUP.  The
    second argument to CLEANUP will be CLEANUP_DATA on every
    invocation.  */
@@ -109,16 +146,28 @@
                             void *cleanup_data);
 
 
-/* Add ITEM to the hash table HT under the key KEY.  LOCP is the
-   address of a location pointer in ITEM; If non-NULL, it will be
-   filled with a pointer that may be used as an argument to
-   hurd_ihash_locp_remove().  (The variable pointed to by LOCP may be
-   written to subsequently between this call and when the element is
-   deleted).  If there already is an item under this key, call the
-   cleanup function (if any) for it before overriding the value.  If a
-   memory allocation error occurs, ENOMEM is returned, otherwise 0.  */
+/* Set the maximum load factor in percent to MAX_LOAD, which
+   should be between 50 and 100.  The default is
+   HURD_IHASH_MAX_LOAD_DEFAULT.  New elements are only added to the
+   hash table while the number of hashed elements is that much percent
+   of the total size of the hash table.  If more elements are added,
+   the hash table is first expanded and reorganized.  A MAX_LOAD of
+   100 will always fill the whole table before enlarging it, but note
+   that this will increase the cost of operations significantly when
+   the table is almost full.
+
+   If the value is set to a smaller value than the current load
+   factor, the next reorganization will happen when a new item is
+   added to the hash table.  */
+void hurd_ihash_set_max_load (hurd_ihash_t ht, unsigned int max_load);
+
+
+/* Add ITEM to the hash table HT under the key KEY.  If there already
+   is an item under this key, call the cleanup function (if any) for
+   it before overriding the value.  If a memory allocation error
+   occurs, ENOMEM is returned, otherwise 0.  */
 error_t hurd_ihash_add (hurd_ihash_t ht, hurd_ihash_key_t key,
-                       hurd_ihash_value_t item, hurd_ihash_locp_t *locp);
+                       hurd_ihash_value_t item);
 
 
 /* Find and return the item in the hash table HT with key KEY, or NULL
@@ -147,12 +196,35 @@
    The block will be run for every element in the hash table HT.  The
    value of the current element is available via the macro
    HURD_IHASH_ITERATOR_VALUE.  */
+
+/* The implementation of this macro is peculiar.  We want the macro to
+   execute a block following its invocation, so we can only prepend
+   code.  This excludes creating an outer block.  However, we must
+   define two variables: The hash value variable VALUE, and the loop
+   variable.
+
+   We can define variables inside the for-loop initializer (C99), but
+   we can only use one basic type to do that.  We can not use two
+   for-loops, because we want a break statement inside the iterator
+   block to terminate the operation.  So we must have both variables
+   of the same basic type, but we can make one of them a pointer type.
+
+   The pointer to the value can be used as the loop variable.  This is
+   also the first element of the hash item, so we can cast the pointer
+   freely between these two types.  The pointer is only dereferenced
+   after the loop condition is checked (but of course the value the
+   pointer pointed to must not have an influence on the condition
+   result, so the comma operator is used to make sure this
+   subexpression is always true).  */
 #define HURD_IHASH_ITERATE(ht, value)                                  \
-  for (hurd_ihash_value_t value = (ht)->size ? (ht)->table[0] : 0,     \
-         *_hurd_ihash_valuep = (ht)->size ? &(ht)->table[0] : 0;       \
-       (ht)->size && _hurd_ihash_valuep - &(ht)->table[0] < (ht)->size \
+  for (hurd_ihash_value_t value,                                       \
+         *_hurd_ihash_valuep = (ht)->size ? &(ht)->items[0].value : 0; \
+       (ht)->size                                                      \
+         && ((_hurd_ihash_item_t) _hurd_ihash_valuep) - &(ht)->items[0]        
\
+            < (ht)->size                                               \
          && (value = *_hurd_ihash_valuep, 1);                          \
-       _hurd_ihash_valuep++)                                           \
+       _hurd_ihash_valuep = (hurd_ihash_value_t *)                     \
+        (((_hurd_ihash_item_t) _hurd_ihash_valuep)++))                 \
     if (value != _HURD_IHASH_EMPTY && value != _HURD_IHASH_DELETED)
 
 
@@ -162,11 +234,9 @@
 
 
 /* Remove the entry pointed to by the location pointer LOCP from the
-   hashtable HT.  LOCP is the location pointer of which the address
+   hash table HT.  LOCP is the location pointer of which the address
    was provided to hurd_ihash_add().  This call is faster than
-   hurd_ihash_remove().  HT can be NULL, in which case the call still
-   succeeds, but the cleanup function (if any) will not be invoked in
-   this case.  */
+   hurd_ihash_remove().  */
 void hurd_ihash_locp_remove (hurd_ihash_t ht, hurd_ihash_locp_t locp);
 
 #endif /* _HURD_IHASH_H */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]