hash: check for resize before insertion
authorEric Blake <ebb9@byu.net>
Wed, 17 Jun 2009 19:01:41 +0000 (13:01 -0600)
committerJim Meyering <meyering@redhat.com>
Thu, 18 Jun 2009 07:12:06 +0000 (09:12 +0200)
* lib/hash.c (hash_insert): Check whether bucket usage exceeds
threshold before insertion, so that a pathological hash_rehash
that fills every bucket can still trigger another rehash.

Signed-off-by: Eric Blake <ebb9@byu.net>
ChangeLog
lib/hash.c

index 1d69f22..f001677 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2009-06-18  Eric Blake  <ebb9@byu.net>
+
+       hash: check for resize before insertion
+       * lib/hash.c (hash_insert): Check whether bucket usage exceeds
+       threshold before insertion, so that a pathological hash_rehash
+       that fills every bucket can still trigger another rehash.
+
 2009-06-18  Jim Meyering  <meyering@redhat.com>
 
        hash-tests: add a loop around the small tests
index 5068202..4de1069 100644 (file)
@@ -931,30 +931,6 @@ hash_insert (Hash_table *table, const void *entry)
   if ((data = hash_find_entry (table, entry, &bucket, false)) != NULL)
     return data;
 
-  /* ENTRY is not matched, it should be inserted.  */
-
-  if (bucket->data)
-    {
-      struct hash_entry *new_entry = allocate_entry (table);
-
-      if (new_entry == NULL)
-       return NULL;
-
-      /* Add ENTRY in the overflow of the bucket.  */
-
-      new_entry->data = (void *) entry;
-      new_entry->next = bucket->next;
-      bucket->next = new_entry;
-      table->n_entries++;
-      return (void *) entry;
-    }
-
-  /* Add ENTRY right in the bucket head.  */
-
-  bucket->data = (void *) entry;
-  table->n_entries++;
-  table->n_buckets_used++;
-
   /* If the growth threshold of the buckets in use has been reached, increase
      the table size and rehash.  There's no point in checking the number of
      entries:  if the hashing function is ill-conditioned, rehashing is not
@@ -981,10 +957,38 @@ hash_insert (Hash_table *table, const void *entry)
 
          /* If the rehash fails, arrange to return NULL.  */
          if (!hash_rehash (table, candidate))
-           entry = NULL;
+           return NULL;
+
+         /* Update the bucket we are interested in.  */
+         if (hash_find_entry (table, entry, &bucket, false) != NULL)
+           abort ();
        }
     }
 
+  /* ENTRY is not matched, it should be inserted.  */
+
+  if (bucket->data)
+    {
+      struct hash_entry *new_entry = allocate_entry (table);
+
+      if (new_entry == NULL)
+       return NULL;
+
+      /* Add ENTRY in the overflow of the bucket.  */
+
+      new_entry->data = (void *) entry;
+      new_entry->next = bucket->next;
+      bucket->next = new_entry;
+      table->n_entries++;
+      return (void *) entry;
+    }
+
+  /* Add ENTRY right in the bucket head.  */
+
+  bucket->data = (void *) entry;
+  table->n_entries++;
+  table->n_buckets_used++;
+
   return (void *) entry;
 }