]> granicus.if.org Git - neomutt/commitdiff
refactor the hash types
authorRichard Russon <rich@flatcap.org>
Fri, 1 Sep 2017 15:04:04 +0000 (16:04 +0100)
committerRichard Russon <rich@flatcap.org>
Thu, 28 Dec 2017 20:09:14 +0000 (20:09 +0000)
The HashElem now contains the type of the data.
Also, the destructor is set once at the creation of the Hash table.

15 files changed:
alias.c
group.c
header.c
history.c
imap/imap.c
init.c
mbox.c
mh.c
mutt/hash.c
mutt/hash.h
mx.c
newsrc.c
nntp.c
pop.c
thread.c

diff --git a/alias.c b/alias.c
index 6ad6cf76c6f3410e4f8276252cab3503d5180a3d..d97feb56ab71bdce20894c1c6678a6ee12abf479 100644 (file)
--- a/alias.c
+++ b/alias.c
@@ -495,7 +495,7 @@ void mutt_alias_delete_reverse(struct Alias *t)
   for (ap = t->addr; ap; ap = ap->next)
   {
     if (!ap->group && ap->mailbox)
-      mutt_hash_delete(ReverseAliases, ap->mailbox, ap, NULL);
+      mutt_hash_delete(ReverseAliases, ap->mailbox, ap);
   }
 }
 
diff --git a/group.c b/group.c
index 159cc2a1e24ea9986d2cb17f363e2761bb6e8dea..361822b3def8a5573a02b2ea29608cada5e34a5c 100644 (file)
--- a/group.c
+++ b/group.c
@@ -53,7 +53,7 @@ static void group_remove(struct Group *g)
 {
   if (!g)
     return;
-  mutt_hash_delete(Groups, g->name, g, NULL);
+  mutt_hash_delete(Groups, g->name, g);
   mutt_addr_free(&g->as);
   mutt_regexlist_free(&g->rs);
   FREE(&g->name);
index 6fe6de40f63c13c726f527e40502dadf7fa4139b..e6e737e6ced5d029935d3912df7ed9e21f334d56 100644 (file)
--- a/header.c
+++ b/header.c
@@ -237,7 +237,7 @@ static void label_ref_dec(struct Context *ctx, char *label)
   count = (uintptr_t) elem->data;
   if (count <= 1)
   {
-    mutt_hash_delete(ctx->label_hash, label, NULL, NULL);
+    mutt_hash_delete(ctx->label_hash, label, NULL);
     return;
   }
 
index 262bd24ad8d8d0e5837151ccdc020d48e8070bcd..0673c79661fb4a0df4f526bc7b2a9e1d434884b2 100644 (file)
--- a/history.c
+++ b/history.c
@@ -162,7 +162,7 @@ static int dup_hash_dec(struct Hash *dup_hash, char *s)
   count = (uintptr_t) elem->data;
   if (count <= 1)
   {
-    mutt_hash_delete(dup_hash, s, NULL, NULL);
+    mutt_hash_delete(dup_hash, s, NULL);
     return 0;
   }
 
@@ -292,7 +292,7 @@ cleanup:
   }
   if (option(OPT_HISTORY_REMOVE_DUPS))
     for (hclass = 0; hclass < HC_LAST; hclass++)
-      mutt_hash_destroy(&dup_hashes[hclass], NULL);
+      mutt_hash_destroy(&dup_hashes[hclass]);
 }
 
 static void save_history(enum HistoryClass hclass, const char *s)
index eaba75ca78b0e1d44974f70b938e839ea6ea6b7f..fb2141439ee64723f85337b9e109a2e11c9891f5 100644 (file)
@@ -903,7 +903,7 @@ void imap_expunge_mailbox(struct ImapData *idata)
         FREE(&idata->cache[cacheno].path);
       }
 
-      mutt_hash_int_delete(idata->uid_hash, HEADER_DATA(h)->uid, h, NULL);
+      mutt_hash_int_delete(idata->uid_hash, HEADER_DATA(h)->uid, h);
 
       imap_free_header_data((struct ImapHeaderData **) &h->data);
     }
@@ -2329,7 +2329,7 @@ static int imap_close_mailbox(struct Context *ctx)
     mutt_list_free(&idata->flags);
     idata->ctx = NULL;
 
-    mutt_hash_destroy(&idata->uid_hash, NULL);
+    mutt_hash_destroy(&idata->uid_hash);
     FREE(&idata->msn_index);
     idata->msn_index_size = 0;
     idata->max_msn = 0;
diff --git a/init.c b/init.c
index 0fc54dc78823f03e80146f582e1e8e37732a82bc..243dad6a178ff57445e3463998580e5a3ff3d11f 100644 (file)
--- a/init.c
+++ b/init.c
@@ -3893,7 +3893,7 @@ void mutt_init(int skip_sys_rc, struct ListHead *commands)
   /* reverse alias keys need to be strdup'ed because of idna conversions */
   ReverseAliases = mutt_hash_create(
       1031, MUTT_HASH_STRCASECMP | MUTT_HASH_STRDUP_KEYS | MUTT_HASH_ALLOW_DUPS);
-  TagTransforms = mutt_hash_create(64, 1);
+  TagTransforms = mutt_hash_create(64, MUTT_HASH_STRCASECMP);
   TagFormats = mutt_hash_create(64, 0);
 
   mutt_menu_init();
diff --git a/mbox.c b/mbox.c
index 2a5a06ccc9a833f00bfc1d557860d3bf25fae0d6..89dcd29d1d71a564ffef333b84569e42093ea041 100644 (file)
--- a/mbox.c
+++ b/mbox.c
@@ -621,10 +621,10 @@ static int reopen_mailbox(struct Context *ctx, int *index_hint)
 
   /* simulate a close */
   if (ctx->id_hash)
-    mutt_hash_destroy(&ctx->id_hash, NULL);
+    mutt_hash_destroy(&ctx->id_hash);
   if (ctx->subj_hash)
-    mutt_hash_destroy(&ctx->subj_hash, NULL);
-  mutt_hash_destroy(&ctx->label_hash, NULL);
+    mutt_hash_destroy(&ctx->subj_hash);
+  mutt_hash_destroy(&ctx->label_hash);
   mutt_clear_threads(ctx);
   FREE(&ctx->v2r);
   if (ctx->readonly)
diff --git a/mh.c b/mh.c
index 89d85f6cf1466fa45205aeeb52b604e4c7e1590e..c1c0402242c7fe8212fc5a3ef9f8a1495247a88a 100644 (file)
--- a/mh.c
+++ b/mh.c
@@ -2171,7 +2171,7 @@ static int maildir_check_mailbox(struct Context *ctx, int *index_hint)
   }
 
   /* destroy the file name hash */
-  mutt_hash_destroy(&fnames, NULL);
+  mutt_hash_destroy(&fnames);
 
   /* If we didn't just get new mail, update the tables. */
   if (occult)
@@ -2295,7 +2295,7 @@ static int mh_check_mailbox(struct Context *ctx, int *index_hint)
 
   /* destroy the file name hash */
 
-  mutt_hash_destroy(&fnames, NULL);
+  mutt_hash_destroy(&fnames);
 
   /* If we didn't just get new mail, update the tables. */
   if (occult)
index bbf3501d1c82005605d5aa06405ad4683c2ec450..290f9eba97a52f370f29a35a802303a1d2b79781 100644 (file)
@@ -4,6 +4,7 @@
  *
  * @authors
  * Copyright (C) 1996-2009 Michael R. Elkins <me@mutt.org>
+ * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
  *
  * @copyright
  * This program is free software: you can redistribute it and/or modify it under
@@ -162,11 +163,13 @@ static struct Hash *new_hash(int nelem)
  * union_hash_insert - Insert into a hash table using a union as a key
  * @param table     Hash table to update
  * @param key       Key to hash on
+ * @param type      Data type
  * @param data      Data to associate with `key'
  * @retval -1 on error
  * @retval >=0 on success, index into the hash table
  */
-static int union_hash_insert(struct Hash *table, union HashKey key, void *data)
+static struct HashElem *union_hash_insert(struct Hash *table, union HashKey key,
+                                          int type, void *data)
 {
   struct HashElem *ptr = NULL;
   unsigned int h;
@@ -175,6 +178,7 @@ static int union_hash_insert(struct Hash *table, union HashKey key, void *data)
   h = table->gen_hash(key, table->nelem);
   ptr->key = key;
   ptr->data = data;
+  ptr->type = type;
 
   if (table->allow_dups)
   {
@@ -184,14 +188,15 @@ static int union_hash_insert(struct Hash *table, union HashKey key, void *data)
   else
   {
     struct HashElem *tmp = NULL, *last = NULL;
+    int r;
 
     for (tmp = table->table[h], last = NULL; tmp; last = tmp, tmp = tmp->next)
     {
-      int r = table->cmp_key(tmp->key, key);
+      r = table->cmp_key(tmp->key, key);
       if (r == 0)
       {
         FREE(&ptr);
-        return -1;
+        return NULL;
       }
       if (r > 0)
         break;
@@ -202,7 +207,7 @@ static int union_hash_insert(struct Hash *table, union HashKey key, void *data)
       table->table[h] = ptr;
     ptr->next = tmp;
   }
-  return h;
+  return ptr;
 }
 
 /**
@@ -249,13 +254,11 @@ static void *union_hash_find(const struct Hash *table, union HashKey key)
  * @param table   Hash table to use
  * @param key     Key (either string or integer)
  * @param data    Private data to match (or NULL for any match)
- * @param destroy Callback function to free the HashElem's data
  */
-static void union_hash_delete(struct Hash *table, union HashKey key,
-                              const void *data, void (*destroy)(void *))
+static void union_hash_delete(struct Hash *table, union HashKey key, const void *data)
 {
   int hash;
-  struct HashElem *ptr = NULL, **last = NULL;
+  struct HashElem *ptr, **last;
 
   if (!table)
     return;
@@ -266,11 +269,11 @@ static void union_hash_delete(struct Hash *table, union HashKey key,
 
   while (ptr)
   {
-    if (((data == ptr->data) || !data) && table->cmp_key(ptr->key, key) == 0)
+    if ((data == ptr->data || !data) && table->cmp_key(ptr->key, key) == 0)
     {
       *last = ptr->next;
-      if (destroy)
-        destroy(ptr->data);
+      if (table->destroy)
+        table->destroy(ptr->type, ptr->data, table->dest_data);
       if (table->strdup_keys)
         FREE(&ptr->key.strkey);
       FREE(&ptr);
@@ -327,6 +330,20 @@ struct Hash *mutt_hash_int_create(int nelem, int flags)
   return table;
 }
 
+void mutt_hash_set_destructor(struct Hash *hash, hash_destructor fn, intptr_t fn_data)
+{
+  hash->destroy = fn;
+  hash->dest_data = fn_data;
+}
+
+struct HashElem *mutt_hash_typed_insert(struct Hash *table, const char *strkey,
+                                        int type, void *data)
+{
+  union HashKey key;
+  key.strkey = table->strdup_keys ? mutt_str_strdup(strkey) : strkey;
+  return union_hash_insert(table, key, type, data);
+}
+
 /**
  * mutt_hash_insert - Add a new element to the Hash table (with string keys)
  * @param table  Hash table (with string keys)
@@ -335,11 +352,9 @@ struct Hash *mutt_hash_int_create(int nelem, int flags)
  * @retval -1 on error
  * @retval >=0 on success, index into the hash table
  */
-int mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
+struct HashElem *mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
 {
-  union HashKey key;
-  key.strkey = table->strdup_keys ? mutt_str_strdup(strkey) : strkey;
-  return union_hash_insert(table, key, data);
+  return mutt_hash_typed_insert(table, strkey, -1, data);
 }
 
 /**
@@ -350,11 +365,11 @@ int mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
  * @retval -1 on error
  * @retval >=0 on success, index into the hash table
  */
-int mutt_hash_int_insert(struct Hash *table, unsigned int intkey, void *data)
+struct HashElem *mutt_hash_int_insert(struct Hash *table, unsigned int intkey, void *data)
 {
   union HashKey key;
   key.intkey = intkey;
-  return union_hash_insert(table, key, data);
+  return union_hash_insert(table, key, -1, data);
 }
 
 /**
@@ -422,14 +437,12 @@ struct HashElem *mutt_hash_find_bucket(const struct Hash *table, const char *str
  * @param table   Hash table to use
  * @param strkey  String key to match
  * @param data    Private data to match (or NULL for any match)
- * @param destroy Callback function to free the HashElem's data
  */
-void mutt_hash_delete(struct Hash *table, const char *strkey, const void *data,
-                      void (*destroy)(void *))
+void mutt_hash_delete(struct Hash *table, const char *strkey, const void *data)
 {
   union HashKey key;
   key.strkey = strkey;
-  union_hash_delete(table, key, data, destroy);
+  union_hash_delete(table, key, data);
 }
 
 /**
@@ -437,22 +450,19 @@ void mutt_hash_delete(struct Hash *table, const char *strkey, const void *data,
  * @param table   Hash table to use
  * @param intkey  Integer key to match
  * @param data    Private data to match (or NULL for any match)
- * @param destroy Callback function to free the HashElem's data
  */
-void mutt_hash_int_delete(struct Hash *table, unsigned int intkey,
-                          const void *data, void (*destroy)(void *))
+void mutt_hash_int_delete(struct Hash *table, unsigned int intkey, const void *data)
 {
   union HashKey key;
   key.intkey = intkey;
-  union_hash_delete(table, key, data, destroy);
+  union_hash_delete(table, key, data);
 }
 
 /**
  * mutt_hash_destroy - Destroy a hash table
  * @param ptr     Pointer to the hash table to be freed
- * @param destroy Function to call to free the ->data member (optional)
  */
-void mutt_hash_destroy(struct Hash **ptr, void (*destroy)(void *))
+void mutt_hash_destroy(struct Hash **ptr)
 {
   struct Hash *pptr = NULL;
   struct HashElem *elem = NULL, *tmp = NULL;
@@ -467,8 +477,8 @@ void mutt_hash_destroy(struct Hash **ptr, void (*destroy)(void *))
     {
       tmp = elem;
       elem = elem->next;
-      if (destroy)
-        destroy(tmp->data);
+      if (pptr->destroy)
+        pptr->destroy(tmp->type, tmp->data, pptr->dest_data);
       if (pptr->strdup_keys)
         FREE(&tmp->key.strkey);
       FREE(&tmp);
index 40ec698caf656fd5e56843c3dbbf2eda5e6c63ce..df6d4c4faf2fa979ca85fe337c1be1124263eb4d 100644 (file)
@@ -4,6 +4,7 @@
  *
  * @authors
  * Copyright (C) 1996-2009 Michael R. Elkins <me@mutt.org>
+ * Copyright (C) 2017 Richard Russon <rich@flatcap.org>
  *
  * @copyright
  * This program is free software: you can redistribute it and/or modify it under
@@ -24,6 +25,7 @@
 #define _MUTT_HASH_H
 
 #include <stdbool.h>
+#include <stdint.h>
 
 /**
  * union HashKey - The data item stored in a HashElem
@@ -38,11 +40,14 @@ union HashKey {
  */
 struct HashElem
 {
+  int type;
   union HashKey key;
   void *data;
   struct HashElem *next;
 };
 
+typedef void (*hash_destructor)(int type, void *obj, intptr_t data);
+
 /**
  * struct Hash - A Hash Table
  */
@@ -54,6 +59,8 @@ struct Hash
   struct HashElem **table;
   unsigned int (*gen_hash)(union HashKey, unsigned int);
   int (*cmp_key)(union HashKey, union HashKey);
+  hash_destructor destroy;
+  intptr_t dest_data;
 };
 
 /* flags for mutt_hash_create() */
@@ -62,16 +69,18 @@ struct Hash
 #define MUTT_HASH_ALLOW_DUPS  (1 << 2) /**< allow duplicate keys to be inserted */
 
 struct Hash *    mutt_hash_create(int nelem, int flags);
-void             mutt_hash_delete(struct Hash *table, const char *strkey, const void *data, void (*destroy)(void *));
-void             mutt_hash_destroy(struct Hash **ptr, void (*destroy)(void *));
+void             mutt_hash_delete(struct Hash *table, const char *strkey, const void *data);
+void             mutt_hash_destroy(struct Hash **ptr);
 struct HashElem *mutt_hash_find_bucket(const struct Hash *table, const char *strkey);
 void *           mutt_hash_find(const struct Hash *table, const char *strkey);
 struct HashElem *mutt_hash_find_elem(const struct Hash *table, const char *strkey);
-int              mutt_hash_insert(struct Hash *table, const char *strkey, void *data);
+struct HashElem *mutt_hash_insert(struct Hash *table, const char *strkey, void *data);
+void             mutt_hash_set_destructor(struct Hash *hash, hash_destructor fn, intptr_t fn_data);
+struct HashElem *mutt_hash_typed_insert(struct Hash *table, const char *strkey, int type, void *data);
 struct Hash *    mutt_hash_int_create(int nelem, int flags);
-void             mutt_hash_int_delete(struct Hash *table, unsigned int intkey, const void *data, void (*destroy)(void *));
+void             mutt_hash_int_delete(struct Hash *table, unsigned int intkey, const void *data);
 void *           mutt_hash_int_find(const struct Hash *table, unsigned int intkey);
-int              mutt_hash_int_insert(struct Hash *table, unsigned int intkey, void *data);
+struct HashElem *mutt_hash_int_insert(struct Hash *table, unsigned int intkey, void *data);
 
 /**
  * struct HashWalkState - Cursor to iterate through a Hash Table
diff --git a/mx.c b/mx.c
index 6554161f8b6aefe9482522b2c3699793758716bc..5eda153a1deb026fac26a6850079d24a954e0223 100644 (file)
--- a/mx.c
+++ b/mx.c
@@ -535,10 +535,10 @@ void mx_fastclose_mailbox(struct Context *ctx)
     ctx->mx_ops->close(ctx);
 
   if (ctx->subj_hash)
-    mutt_hash_destroy(&ctx->subj_hash, NULL);
+    mutt_hash_destroy(&ctx->subj_hash);
   if (ctx->id_hash)
-    mutt_hash_destroy(&ctx->id_hash, NULL);
-  mutt_hash_destroy(&ctx->label_hash, NULL);
+    mutt_hash_destroy(&ctx->id_hash);
+  mutt_hash_destroy(&ctx->label_hash);
   mutt_clear_threads(ctx);
   for (int i = 0; i < ctx->msgcount; i++)
     mutt_free_header(&ctx->hdrs[i]);
@@ -980,9 +980,9 @@ void mx_update_tables(struct Context *ctx, bool committing)
                       ctx->hdrs[i]->content->hdr_offset);
       /* remove message from the hash tables */
       if (ctx->subj_hash && ctx->hdrs[i]->env->real_subj)
-        mutt_hash_delete(ctx->subj_hash, ctx->hdrs[i]->env->real_subj, ctx->hdrs[i], NULL);
+        mutt_hash_delete(ctx->subj_hash, ctx->hdrs[i]->env->real_subj, ctx->hdrs[i]);
       if (ctx->id_hash && ctx->hdrs[i]->env->message_id)
-        mutt_hash_delete(ctx->id_hash, ctx->hdrs[i]->env->message_id, ctx->hdrs[i], NULL);
+        mutt_hash_delete(ctx->id_hash, ctx->hdrs[i]->env->message_id, ctx->hdrs[i]);
       mutt_label_hash_remove(ctx, ctx->hdrs[i]);
       /* The path mx_check_mailbox() -> imap_check_mailbox() ->
        *          imap_expunge_mailbox() -> mx_update_tables()
index ae162cb315759cca23ed857076e95780956b9786..f66ad3c5fb891c8556fd05668ffd186126baef0f 100644 (file)
--- a/newsrc.c
+++ b/newsrc.c
@@ -115,6 +115,11 @@ void nntp_data_free(void *data)
   FREE(&data);
 }
 
+void nntp_hash_destructor(int type, void *obj, intptr_t data)
+{
+  nntp_data_free(obj);
+}
+
 /**
  * nntp_newsrc_close - Unlock and close .newsrc file
  */
@@ -1026,6 +1031,7 @@ struct NntpServer *nntp_select_server(char *server, bool leave_lock)
   nserv = mutt_mem_calloc(1, sizeof(struct NntpServer));
   nserv->conn = conn;
   nserv->groups_hash = mutt_hash_create(1009, 0);
+  mutt_hash_set_destructor(nserv->groups_hash, nntp_hash_destructor, 0);
   nserv->groups_max = 16;
   nserv->groups_list = mutt_mem_malloc(nserv->groups_max * sizeof(nntp_data));
 
@@ -1127,7 +1133,7 @@ struct NntpServer *nntp_select_server(char *server, bool leave_lock)
 
   if (rc < 0)
   {
-    mutt_hash_destroy(&nserv->groups_hash, nntp_data_free);
+    mutt_hash_destroy(&nserv->groups_hash);
     FREE(&nserv->groups_list);
     FREE(&nserv->newsrc_file);
     FREE(&nserv->authenticators);
diff --git a/nntp.c b/nntp.c
index b045870b479687c11f59fbf5cde6c7f4015ac2b1..76e510c40fe2f14654cef3774863806615f6f381 100644 (file)
--- a/nntp.c
+++ b/nntp.c
@@ -1681,9 +1681,9 @@ static int nntp_open_message(struct Context *ctx, struct Message *msg, int msgno
   /* replace envelope with new one
    * hash elements must be updated because pointers will be changed */
   if (ctx->id_hash && hdr->env->message_id)
-    mutt_hash_delete(ctx->id_hash, hdr->env->message_id, hdr, NULL);
+    mutt_hash_delete(ctx->id_hash, hdr->env->message_id, hdr);
   if (ctx->subj_hash && hdr->env->real_subj)
-    mutt_hash_delete(ctx->subj_hash, hdr->env->real_subj, hdr, NULL);
+    mutt_hash_delete(ctx->subj_hash, hdr->env->real_subj, hdr);
 
   mutt_env_free(&hdr->env);
   hdr->env = mutt_read_rfc822_header(msg->fp, hdr, 0, 0);
@@ -2015,9 +2015,9 @@ static int check_mailbox(struct Context *ctx)
   if (ret == MUTT_REOPENED)
   {
     if (ctx->subj_hash)
-      mutt_hash_destroy(&ctx->subj_hash, NULL);
+      mutt_hash_destroy(&ctx->subj_hash);
     if (ctx->id_hash)
-      mutt_hash_destroy(&ctx->id_hash, NULL);
+      mutt_hash_destroy(&ctx->id_hash);
     mutt_clear_threads(ctx);
 
     ctx->vcount = 0;
@@ -2245,7 +2245,7 @@ int nntp_active_fetch(struct NntpServer *nserv, bool new)
     if (data && data->deleted && !data->newsrc_ent)
     {
       nntp_delete_group_cache(data);
-      mutt_hash_delete(nserv->groups_hash, data->group, NULL, nntp_data_free);
+      mutt_hash_delete(nserv->groups_hash, data->group, NULL);
       nserv->groups_list[i] = NULL;
     }
   }
diff --git a/pop.c b/pop.c
index e72f597b1a76dd53e2b17909a43399a9f92831fc..10dbf6ff4e721e49b04920dff4999551ad57b1c3 100644 (file)
--- a/pop.c
+++ b/pop.c
@@ -666,7 +666,7 @@ static int pop_fetch_message(struct Context *ctx, struct Message *msg, int msgno
 
   /* we replace envelop, key in subj_hash has to be updated as well */
   if (ctx->subj_hash && h->env->real_subj)
-    mutt_hash_delete(ctx->subj_hash, h->env->real_subj, h, NULL);
+    mutt_hash_delete(ctx->subj_hash, h->env->real_subj, h);
   mutt_label_hash_remove(ctx, h);
   mutt_env_free(&h->env);
   h->env = mutt_read_rfc822_header(msg->fp, h, 0, 0);
index 673a084375855ba812154ada218cbda1e9075eec..3e33060a1ee83019d2414aaf95fb44a4029f9811 100644 (file)
--- a/thread.c
+++ b/thread.c
@@ -604,7 +604,7 @@ void mutt_clear_threads(struct Context *ctx)
   ctx->tree = NULL;
 
   if (ctx->thread_hash)
-    mutt_hash_destroy(&ctx->thread_hash, *free);
+    mutt_hash_destroy(&ctx->thread_hash);
 }
 
 static int compare_threads(const void *a, const void *b)
@@ -782,6 +782,11 @@ static void check_subjects(struct Context *ctx, int init)
   }
 }
 
+void thread_hash_destructor(int type, void *obj, intptr_t data)
+{
+  FREE(&obj);
+}
+
 void mutt_sort_threads(struct Context *ctx, int init)
 {
   struct Header *cur = NULL;
@@ -801,7 +806,10 @@ void mutt_sort_threads(struct Context *ctx, int init)
     init = 1;
 
   if (init)
+  {
     ctx->thread_hash = mutt_hash_create(ctx->msgcount * 2, MUTT_HASH_ALLOW_DUPS);
+    mutt_hash_set_destructor(ctx->thread_hash, thread_hash_destructor, 0);
+  }
 
   /* we want a quick way to see if things are actually attached to the top of the
    * thread tree or if they're just dangling, so we attach everything to a top