[PATCH 4/4] Add resize_hash() function.

"Kevin J. McCarthy" <[email protected]> Mon, 20 Jul 2026 13:00:40 +0800
Newsgroups gmane.mail.mutt.devel
Message-ID <[email protected]>
Some of mutt's hashes are based on the size of the mailbox, with room
to grow.  Generally they perform okay.

However, some of the hash tables are based on "Hmmmm... I think this
should be enough in general" guesstimates, such as the label hash, or
the auto_subscribe hash.  When those guesses are wrong, it can degrade
performance.  So this commit allows the hash table to resize itself.

Note, there are a couple functions that allow callers to work directly
with buckets or to walk the hash table.  None of the callers currently
do anything silly like try to insert/delete entries while walking the
internals.  So for now, I've let them be.

If later callers want to do crazy things, we could always introduce a
resize-lock, but since we're not a library I'd rather keep it simple
for now.
---
 hash.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/hash.c b/hash.c
index 8b50b244..fd2e06d4 100644
--- a/hash.c
+++ b/hash.c
@@ -117,16 +117,47 @@ HASH *int_hash_create(int bucket_count, int flags)
   return table;
 }
 
+static void resize_hash(HASH *table)
+{
+  struct hash_elem **old_buckets = table->table;
+  int old_bucket_count = table->bucket_count;
+  int bucket, hash;
+  struct hash_elem *elem, *next_elem;
+
+  while (table->elem_count > table->bucket_count * 1.2)
+    table->bucket_count *= 2;
+  table->table = safe_calloc(table->bucket_count, sizeof(struct hash_elem *));
+
+  for (bucket = 0; bucket < old_bucket_count; bucket++)
+  {
+    elem = old_buckets[bucket];
+    while (elem)
+    {
+      next_elem = elem->next;
+
+      hash = table->gen_hash(elem->key, table->bucket_count);
+      elem->next = table->table[hash];
+      table->table[hash] = elem;
+
+      elem = next_elem;
+    }
+  }
+  FREE(&old_buckets);
+}
+
 /* table        hash table to update
  * key          key to hash on
  * data         data to associate with `key'
  * allow_dup    if nonzero, duplicate keys are allowed in the table
  */
-static int union_hash_insert(HASH * table, union hash_key key, void *data)
+static int union_hash_insert(HASH *table, union hash_key key, void *data)
 {
   struct hash_elem *ptr;
   unsigned int h;
 
+  if (table->elem_count > table->bucket_count * 1.2)
+    resize_hash(table);
+
   ptr = (struct hash_elem *) safe_malloc(sizeof(struct hash_elem));
   h = table->gen_hash(key, table->bucket_count);
   ptr->key = key;
-- 
2.55.0