util/set: Do a full search when adding new items
authorJason Ekstrand <jason.ekstrand@intel.com>
Thu, 5 Feb 2015 02:46:23 +0000 (18:46 -0800)
committerJason Ekstrand <jason.ekstrand@intel.com>
Sun, 8 Feb 2015 01:01:05 +0000 (17:01 -0800)
Previously, the set_insert function would bail early if it found a deleted
slot that it could re-use.  However, this is a problem if the key being
inserted is already in the set but further down the list.  If this happens,
the element ends up getting inserted in the set twice.  This commit makes
it so that we walk over all of the possible entries for the given key and
then, if we don't find the key, place it in the available free entry we
found.

Reviewed-by: Eric Anholt <eric@anholt.net>
src/util/set.c

index c3252a09401efdb3a2361a616585e02cc098b0e7..f01f8699ac2d7ab3594f675e9e91ca1b374c3247 100644 (file)
@@ -251,6 +251,7 @@ static struct set_entry *
 set_add(struct set *ht, uint32_t hash, const void *key)
 {
    uint32_t hash_address;
+   struct set_entry *available_entry = NULL;
 
    if (ht->entries >= ht->max_entries) {
       set_rehash(ht, ht->size_index + 1);
@@ -264,12 +265,11 @@ set_add(struct set *ht, uint32_t hash, const void *key)
       uint32_t double_hash;
 
       if (!entry_is_present(entry)) {
-         if (entry_is_deleted(entry))
-            ht->deleted_entries--;
-         entry->hash = hash;
-         entry->key = key;
-         ht->entries++;
-         return entry;
+         /* Stash the first available entry we find */
+         if (available_entry == NULL)
+            available_entry = entry;
+         if (entry_is_free(entry))
+            break;
       }
 
       /* Implement replacement when another insert happens
@@ -293,6 +293,15 @@ set_add(struct set *ht, uint32_t hash, const void *key)
       hash_address = (hash_address + double_hash) % ht->size;
    } while (hash_address != hash % ht->size);
 
+   if (available_entry) {
+      if (entry_is_deleted(available_entry))
+         ht->deleted_entries--;
+      available_entry->hash = hash;
+      available_entry->key = key;
+      ht->entries++;
+      return available_entry;
+   }
+
    /* We could hit here if a required resize failed. An unchecked-malloc
     * application could ignore this result.
     */