[PATCH 2/5] Implement an internal generic string list and use it.
authorAustin Clements <amdragon@MIT.EDU>
Thu, 9 Dec 2010 20:59:53 +0000 (15:59 +1900)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:37:39 +0000 (09:37 -0800)
90/4133bc736ba08335952f56480cb4c4131519f9 [new file with mode: 0644]

diff --git a/90/4133bc736ba08335952f56480cb4c4131519f9 b/90/4133bc736ba08335952f56480cb4c4131519f9
new file mode 100644 (file)
index 0000000..e5b6f5a
--- /dev/null
@@ -0,0 +1,672 @@
+Return-Path: <amthrax@awakening.csail.mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 871B1431FB6\r
+       for <notmuch@notmuchmail.org>; Thu,  9 Dec 2010 13:00:30 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+       autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id qs2mtyM5j5os for <notmuch@notmuchmail.org>;\r
+       Thu,  9 Dec 2010 13:00:28 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-5.mit.edu (DMZ-MAILSEC-SCANNER-5.MIT.EDU\r
+       [18.7.68.34])\r
+       by olra.theworths.org (Postfix) with ESMTP id 917D3431FB5\r
+       for <notmuch@notmuchmail.org>; Thu,  9 Dec 2010 13:00:28 -0800 (PST)\r
+X-AuditID: 12074422-b7c3eae000000a70-ea-4d01436cb68a\r
+Received: from mailhub-auth-1.mit.edu ( [18.9.21.35])\r
+       by dmz-mailsec-scanner-5.mit.edu (Symantec Brightmail Gateway) with\r
+       SMTP id BC.D7.02672.C63410D4; Thu,  9 Dec 2010 16:00:28 -0500 (EST)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+       by mailhub-auth-1.mit.edu (8.13.8/8.9.2) with ESMTP id oB9L0Sij011352; \r
+       Thu, 9 Dec 2010 16:00:28 -0500\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+       (authenticated bits=0)\r
+       (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+       by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id oB9L0QXa010074\r
+       (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+       Thu, 9 Dec 2010 16:00:27 -0500 (EST)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.72)\r
+       (envelope-from <amthrax@awakening.csail.mit.edu>)\r
+       id 1PQnbG-0007Hp-SX; Thu, 09 Dec 2010 16:00:26 -0500\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH 2/5] Implement an internal generic string list and use it.\r
+Date: Thu,  9 Dec 2010 15:59:53 -0500\r
+Message-Id: <1291928396-27937-3-git-send-email-amdragon@mit.edu>\r
+X-Mailer: git-send-email 1.7.2.3\r
+In-Reply-To: <1291928396-27937-1-git-send-email-amdragon@mit.edu>\r
+References: <1291928396-27937-1-git-send-email-amdragon@mit.edu>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: 8bit\r
+X-Brightmail-Tracker: AAAAAhbjYfcW42MD\r
+Cc: Austin Clements <amdragon@mit.edu>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Thu, 09 Dec 2010 21:00:30 -0000\r
+\r
+This replaces the guts of the filename list and tag list, making those\r
+interfaces simple iterators over the generic string list.  The\r
+directory, message filename, and tags-related code now build generic\r
+string lists and then wraps them in specific iterators.  The real wins\r
+come in later patches, when we use these for even more generic\r
+functionality.\r
+\r
+As a nice side-effect, this also eliminates the annoying dependency on\r
+GList in the tag list.\r
+---\r
+ lib/Makefile.local    |    1 +\r
+ lib/database.cc       |   12 +++---\r
+ lib/directory.cc      |    7 ++--\r
+ lib/filenames.c       |   52 +++------------------------\r
+ lib/message.cc        |   15 ++++----\r
+ lib/messages.c        |   11 +++---\r
+ lib/notmuch-private.h |   68 +++++++++++++++++++++--------------\r
+ lib/strings.c         |   94 +++++++++++++++++++++++++++++++++++++++++++++++++\r
+ lib/tags.c            |   63 ++++++--------------------------\r
+ lib/thread.cc         |   10 +++---\r
+ 10 files changed, 179 insertions(+), 154 deletions(-)\r
+ create mode 100644 lib/strings.c\r
+\r
+diff --git a/lib/Makefile.local b/lib/Makefile.local\r
+index 5233ea6..37d1c0d 100644\r
+--- a/lib/Makefile.local\r
++++ b/lib/Makefile.local\r
+@@ -50,6 +50,7 @@ extra_cflags += -I$(dir) -fPIC\r
+ libnotmuch_c_srcs =           \\r
+       $(notmuch_compat_srcs)  \\r
+       $(dir)/filenames.c      \\r
++      $(dir)/strings.c        \\r
+       $(dir)/libsha1.c        \\r
+       $(dir)/message-file.c   \\r
+       $(dir)/messages.c       \\r
+diff --git a/lib/database.cc b/lib/database.cc\r
+index 7a00917..45613bd 100644\r
+--- a/lib/database.cc\r
++++ b/lib/database.cc\r
+@@ -1752,15 +1752,15 @@ _notmuch_convert_tags (void *ctx, Xapian::TermIterator &i,\r
+                      Xapian::TermIterator &end)\r
+ {\r
+     const char *prefix = _find_prefix ("tag");\r
+-    notmuch_tags_t *tags;\r
++    notmuch_string_list_t *list;\r
+     std::string tag;\r
\r
+     /* Currently this iteration is written with the assumption that\r
+      * "tag" has a single-character prefix. */\r
+     assert (strlen (prefix) == 1);\r
\r
+-    tags = _notmuch_tags_create (ctx);\r
+-    if (unlikely (tags == NULL))\r
++    list = _notmuch_string_list_create (ctx);\r
++    if (unlikely (list == NULL))\r
+       return NULL;\r
\r
+     i.skip_to (prefix);\r
+@@ -1771,14 +1771,14 @@ _notmuch_convert_tags (void *ctx, Xapian::TermIterator &i,\r
+       if (tag.empty () || tag[0] != *prefix)\r
+           break;\r
\r
+-      _notmuch_tags_add_tag (tags, tag.c_str () + 1);\r
++      _notmuch_string_list_append (list, tag.c_str () + 1);\r
\r
+       i++;\r
+     }\r
\r
+-    _notmuch_tags_prepare_iterator (tags);\r
++    _notmuch_string_list_sort (list);\r
\r
+-    return tags;\r
++    return _notmuch_tags_create (ctx, list, TRUE);\r
+ }\r
\r
+ notmuch_tags_t *\r
+diff --git a/lib/directory.cc b/lib/directory.cc\r
+index 946be4f..aeee9ca 100644\r
+--- a/lib/directory.cc\r
++++ b/lib/directory.cc\r
+@@ -33,11 +33,11 @@ _create_filenames_for_terms_with_prefix (void *ctx,\r
+                                        notmuch_database_t *notmuch,\r
+                                        const char *prefix)\r
+ {\r
+-    notmuch_filename_list_t *filename_list;\r
++    notmuch_string_list_t *filename_list;\r
+     Xapian::TermIterator i, end;\r
+     int prefix_len = strlen (prefix);\r
\r
+-    filename_list = _notmuch_filename_list_create (ctx);\r
++    filename_list = _notmuch_string_list_create (ctx);\r
+     if (unlikely (filename_list == NULL))\r
+       return NULL;\r
\r
+@@ -47,8 +47,7 @@ _create_filenames_for_terms_with_prefix (void *ctx,\r
+     {\r
+       std::string term = *i;\r
\r
+-      _notmuch_filename_list_add_filename (filename_list, term.c_str () +\r
+-                                           prefix_len);\r
++      _notmuch_string_list_append (filename_list, term.c_str () + prefix_len);\r
+     }\r
\r
+     return _notmuch_filenames_create (ctx, filename_list);\r
+diff --git a/lib/filenames.c b/lib/filenames.c\r
+index f078c95..f1ea243 100644\r
+--- a/lib/filenames.c\r
++++ b/lib/filenames.c\r
+@@ -21,56 +21,14 @@\r
+ #include "notmuch-private.h"\r
\r
+ struct _notmuch_filenames {\r
+-    notmuch_filename_node_t *iterator;\r
++    notmuch_string_node_t *iterator;\r
+ };\r
\r
+-/* Create a new notmuch_filename_list_t object, with 'ctx' as its\r
+- * talloc owner.\r
+- *\r
+- * This function can return NULL in case of out-of-memory.\r
+- */\r
+-notmuch_filename_list_t *\r
+-_notmuch_filename_list_create (const void *ctx)\r
+-{\r
+-    notmuch_filename_list_t *list;\r
+-\r
+-    list = talloc (ctx, notmuch_filename_list_t);\r
+-    if (unlikely (list == NULL))\r
+-      return NULL;\r
+-\r
+-    list->head = NULL;\r
+-    list->tail = &list->head;\r
+-\r
+-    return list;\r
+-}\r
+-\r
+-void\r
+-_notmuch_filename_list_add_filename (notmuch_filename_list_t *list,\r
+-                                   const char *filename)\r
+-{\r
+-    /* Create and initialize new node. */\r
+-    notmuch_filename_node_t *node = talloc (list,\r
+-                                          notmuch_filename_node_t);\r
+-\r
+-    node->filename = talloc_strdup (node, filename);\r
+-    node->next = NULL;\r
+-\r
+-    /* Append the node to the list. */\r
+-    *(list->tail) = node;\r
+-    list->tail = &node->next;\r
+-}\r
+-\r
+-void\r
+-_notmuch_filename_list_destroy (notmuch_filename_list_t *list)\r
+-{\r
+-    talloc_free (list);\r
+-}\r
+-\r
+-/* The notmuch_filenames_t is an iterator object for a\r
+- * notmuch_filename_list_t */\r
++/* The notmuch_filenames_t iterates over a notmuch_string_list_t of\r
++ * file names */\r
+ notmuch_filenames_t *\r
+ _notmuch_filenames_create (const void *ctx,\r
+-                         notmuch_filename_list_t *list)\r
++                         notmuch_string_list_t *list)\r
+ {\r
+     notmuch_filenames_t *filenames;\r
\r
+@@ -99,7 +57,7 @@ notmuch_filenames_get (notmuch_filenames_t *filenames)\r
+     if (filenames->iterator == NULL)\r
+       return NULL;\r
\r
+-    return filenames->iterator->filename;\r
++    return filenames->iterator->string;\r
+ }\r
\r
+ void\r
+diff --git a/lib/message.cc b/lib/message.cc\r
+index d6ab636..031eda5 100644\r
+--- a/lib/message.cc\r
++++ b/lib/message.cc\r
+@@ -32,7 +32,7 @@ struct _notmuch_message {\r
+     char *message_id;\r
+     char *thread_id;\r
+     char *in_reply_to;\r
+-    notmuch_filename_list_t *filename_list;\r
++    notmuch_string_list_t *filename_list;\r
+     char *author;\r
+     notmuch_message_file_t *message_file;\r
+     notmuch_message_list_t *replies;\r
+@@ -434,7 +434,7 @@ _notmuch_message_add_filename (notmuch_message_t *message,\r
+     char *direntry;\r
\r
+     if (message->filename_list) {\r
+-      _notmuch_filename_list_destroy (message->filename_list);\r
++      talloc_free (message->filename_list);\r
+       message->filename_list = NULL;\r
+     }\r
\r
+@@ -511,7 +511,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)\r
+     if (message->filename_list)\r
+       return;\r
\r
+-    message->filename_list = _notmuch_filename_list_create (message);\r
++    message->filename_list = _notmuch_string_list_create (message);\r
\r
+     i = message->doc.termlist_begin ();\r
+     i.skip_to (prefix);\r
+@@ -532,7 +532,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)\r
+       if (data == NULL)\r
+           INTERNAL_ERROR ("message with no filename");\r
\r
+-      _notmuch_filename_list_add_filename (message->filename_list, data);\r
++      _notmuch_string_list_append (message->filename_list, data);\r
\r
+       return;\r
+     }\r
+@@ -573,8 +573,7 @@ _notmuch_message_ensure_filename_list (notmuch_message_t *message)\r
+           filename = talloc_asprintf (message, "%s/%s",\r
+                                       db_path, basename);\r
\r
+-      _notmuch_filename_list_add_filename (message->filename_list,\r
+-                                           filename);\r
++      _notmuch_string_list_append (message->filename_list, filename);\r
\r
+       talloc_free (local);\r
+     }\r
+@@ -589,12 +588,12 @@ notmuch_message_get_filename (notmuch_message_t *message)\r
+       return NULL;\r
\r
+     if (message->filename_list->head == NULL ||\r
+-      message->filename_list->head->filename == NULL)\r
++      message->filename_list->head->string == NULL)\r
+     {\r
+       INTERNAL_ERROR ("message with no filename");\r
+     }\r
\r
+-    return message->filename_list->head->filename;\r
++    return message->filename_list->head->string;\r
+ }\r
\r
+ notmuch_filenames_t *\r
+diff --git a/lib/messages.c b/lib/messages.c\r
+index 120a48a..404e8b3 100644\r
+--- a/lib/messages.c\r
++++ b/lib/messages.c\r
+@@ -146,13 +146,14 @@ notmuch_messages_destroy (notmuch_messages_t *messages)\r
+ notmuch_tags_t *\r
+ notmuch_messages_collect_tags (notmuch_messages_t *messages)\r
+ {\r
+-    notmuch_tags_t *tags, *msg_tags;\r
++    notmuch_string_list_t *tags;\r
++    notmuch_tags_t *msg_tags;\r
+     notmuch_message_t *msg;\r
+     GHashTable *htable;\r
+     GList *keys, *l;\r
+     const char *tag;\r
\r
+-    tags = _notmuch_tags_create (messages);\r
++    tags = _notmuch_string_list_create (messages);\r
+     if (tags == NULL) return NULL;\r
\r
+     htable = g_hash_table_new_full (g_str_hash, g_str_equal, free, NULL);\r
+@@ -170,12 +171,12 @@ notmuch_messages_collect_tags (notmuch_messages_t *messages)\r
\r
+     keys = g_hash_table_get_keys (htable);\r
+     for (l = keys; l; l = l->next) {\r
+-      _notmuch_tags_add_tag (tags, (char *)l->data);\r
++      _notmuch_string_list_append (tags, (char *)l->data);\r
+     }\r
\r
+     g_list_free (keys);\r
+     g_hash_table_destroy (htable);\r
\r
+-    _notmuch_tags_prepare_iterator (tags);\r
+-    return tags;\r
++    _notmuch_string_list_sort (tags);\r
++    return _notmuch_tags_create (messages, tags, TRUE);\r
+ }\r
+diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h\r
+index 303aeb3..b6f1095 100644\r
+--- a/lib/notmuch-private.h\r
++++ b/lib/notmuch-private.h\r
+@@ -453,48 +453,60 @@ notmuch_sha1_of_string (const char *str);\r
+ char *\r
+ notmuch_sha1_of_file (const char *filename);\r
\r
+-/* tags.c */\r
++/* strings.c */\r
\r
+-notmuch_tags_t *\r
+-_notmuch_tags_create (void *ctx);\r
++typedef struct _notmuch_string_node {\r
++    char *string;\r
++    struct _notmuch_string_node *next;\r
++} notmuch_string_node_t;\r
++\r
++typedef struct _notmuch_string_list {\r
++    int length;\r
++    notmuch_string_node_t *head;\r
++    notmuch_string_node_t **tail;\r
++} notmuch_string_list_t;\r
\r
++notmuch_string_list_t *\r
++_notmuch_string_list_create (const void *ctx);\r
++\r
++/* Add 'string' to 'list'.\r
++ *\r
++ * The list will create its own talloced copy of 'string'.\r
++ */\r
+ void\r
+-_notmuch_tags_add_tag (notmuch_tags_t *tags, const char *tag);\r
++_notmuch_string_list_append (notmuch_string_list_t *list,\r
++                           const char *string);\r
\r
+ void\r
+-_notmuch_tags_prepare_iterator (notmuch_tags_t *tags);\r
++_notmuch_string_list_sort (notmuch_string_list_t *list);\r
\r
+-/* filenames.c */\r
++/* tags.c */\r
++\r
++notmuch_tags_t *\r
++_notmuch_tags_create (const void *ctx, notmuch_string_list_t *list,\r
++                    notmuch_bool_t steal);\r
\r
+-typedef struct _notmuch_filename_node {\r
+-    char *filename;\r
+-    struct _notmuch_filename_node *next;\r
+-} notmuch_filename_node_t;\r
++/* filenames.c */\r
\r
+-typedef struct _notmuch_filename_list {\r
+-    notmuch_filename_node_t *head;\r
+-    notmuch_filename_node_t **tail;\r
+-} notmuch_filename_list_t;\r
++/* The notmuch_filenames_t iterates over a notmuch_string_list_t of\r
++ * file names */\r
++notmuch_filenames_t *\r
++_notmuch_filenames_create (const void *ctx,\r
++                         notmuch_string_list_t *list);\r
\r
+-notmuch_filename_list_t *\r
+-_notmuch_filename_list_create (const void *ctx);\r
++/* tags.c */\r
\r
+-/* Add 'filename' to 'list'.\r
+- *\r
+- * The list will create its own talloced copy of 'filename'.\r
+- */\r
+-void\r
+-_notmuch_filename_list_add_filename (notmuch_filename_list_t *list,\r
+-                                   const char *filename);\r
++notmuch_tags_t *\r
++_notmuch_tags_create (const void *ctx, notmuch_string_list_t *list,\r
++                    notmuch_bool_t steal);\r
\r
+-void\r
+-_notmuch_filename_list_destroy (notmuch_filename_list_t *list);\r
++/* filenames.c */\r
\r
+-/* The notmuch_filenames_t is an iterator object for a\r
+- * notmuch_filename_list_t */\r
++/* The notmuch_filenames_t iterates over a notmuch_string_list_t of\r
++ * file names */\r
+ notmuch_filenames_t *\r
+ _notmuch_filenames_create (const void *ctx,\r
+-                         notmuch_filename_list_t *list);\r
++                         notmuch_string_list_t *list);\r
\r
+ #pragma GCC visibility pop\r
\r
+diff --git a/lib/strings.c b/lib/strings.c\r
+new file mode 100644\r
+index 0000000..d92a0ba\r
+--- /dev/null\r
++++ b/lib/strings.c\r
+@@ -0,0 +1,94 @@\r
++/* strings.c - Iterator for a list of strings\r
++ *\r
++ * Copyright © 2010 Intel Corporation\r
++ *\r
++ * This program is free software: you can redistribute it and/or modify\r
++ * it under the terms of the GNU General Public License as published by\r
++ * the Free Software Foundation, either version 3 of the License, or\r
++ * (at your option) any later version.\r
++ *\r
++ * This program is distributed in the hope that it will be useful,\r
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
++ * GNU General Public License for more details.\r
++ *\r
++ * You should have received a copy of the GNU General Public License\r
++ * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
++ *\r
++ * Author: Carl Worth <cworth@cworth.org>\r
++ */\r
++\r
++#include "notmuch-private.h"\r
++\r
++/* Create a new notmuch_string_list_t object, with 'ctx' as its\r
++ * talloc owner.\r
++ *\r
++ * This function can return NULL in case of out-of-memory.\r
++ */\r
++notmuch_string_list_t *\r
++_notmuch_string_list_create (const void *ctx)\r
++{\r
++    notmuch_string_list_t *list;\r
++\r
++    list = talloc (ctx, notmuch_string_list_t);\r
++    if (unlikely (list == NULL))\r
++      return NULL;\r
++\r
++    list->length = 0;\r
++    list->head = NULL;\r
++    list->tail = &list->head;\r
++\r
++    return list;\r
++}\r
++\r
++void\r
++_notmuch_string_list_append (notmuch_string_list_t *list,\r
++                           const char *string)\r
++{\r
++    /* Create and initialize new node. */\r
++    notmuch_string_node_t *node = talloc (list, notmuch_string_node_t);\r
++\r
++    node->string = talloc_strdup (node, string);\r
++    node->next = NULL;\r
++\r
++    /* Append the node to the list. */\r
++    *(list->tail) = node;\r
++    list->tail = &node->next;\r
++    list->length++;\r
++}\r
++\r
++static int\r
++cmpnode (const void *pa, const void *pb)\r
++{\r
++    notmuch_string_node_t *a = *(notmuch_string_node_t * const *)pa;\r
++    notmuch_string_node_t *b = *(notmuch_string_node_t * const *)pb;\r
++\r
++    return strcmp (a->string, b->string);\r
++}\r
++\r
++void\r
++_notmuch_string_list_sort (notmuch_string_list_t *list)\r
++{\r
++    notmuch_string_node_t **nodes, *node;\r
++    int i;\r
++\r
++    if (list->length == 0)\r
++      return;\r
++\r
++    nodes = talloc_array (list, notmuch_string_node_t *, list->length);\r
++    if (unlikely (nodes == NULL))\r
++      INTERNAL_ERROR ("Could not allocate memory for list sort");\r
++\r
++    for (i = 0, node = list->head; node; i++, node = node->next)\r
++      nodes[i] = node;\r
++\r
++    qsort (nodes, list->length, sizeof (*nodes), cmpnode);\r
++\r
++    for (i = 0; i < list->length - 1; ++i)\r
++      nodes[i]->next = nodes[i+1];\r
++    nodes[i]->next = NULL;\r
++    list->head = nodes[0];\r
++    list->tail = &nodes[i]->next;\r
++\r
++    talloc_free (nodes);\r
++}\r
+diff --git a/lib/tags.c b/lib/tags.c\r
+index 8fe4a3f..8392ef6 100644\r
+--- a/lib/tags.c\r
++++ b/lib/tags.c\r
+@@ -20,30 +20,20 @@\r
\r
+ #include "notmuch-private.h"\r
\r
+-#include <glib.h> /* GList */\r
+-\r
+ struct _notmuch_tags {\r
+-    int sorted;\r
+-    GList *tags;\r
+-    GList *iterator;\r
++    notmuch_string_node_t *iterator;\r
+ };\r
\r
+-/* XXX: Should write some talloc-friendly list to avoid the need for\r
+- * this. */\r
+-static int\r
+-_notmuch_tags_destructor (notmuch_tags_t *tags)\r
+-{\r
+-    g_list_free (tags->tags);\r
+-\r
+-    return 0;\r
+-}\r
+-\r
+ /* Create a new notmuch_tags_t object, with 'ctx' as its talloc owner.\r
++ * If steal is true, then the iterator will steal the reference to the\r
++ * list (useful for one-shot iterators); otherwise it will add a\r
++ * reference.\r
+  *\r
+  * This function can return NULL in case of out-of-memory.\r
+  */\r
+ notmuch_tags_t *\r
+-_notmuch_tags_create (void *ctx)\r
++_notmuch_tags_create (const void *ctx, notmuch_string_list_t *list,\r
++                    notmuch_bool_t steal)\r
+ {\r
+     notmuch_tags_t *tags;\r
\r
+@@ -51,44 +41,15 @@ _notmuch_tags_create (void *ctx)\r
+     if (unlikely (tags == NULL))\r
+       return NULL;\r
\r
+-    talloc_set_destructor (tags, _notmuch_tags_destructor);\r
+-\r
+-    tags->sorted = 1;\r
+-    tags->tags = NULL;\r
+-    tags->iterator = NULL;\r
++    tags->iterator = list->head;\r
++    if (steal)\r
++      talloc_steal (tags, list);\r
++    else\r
++      (void) talloc_reference (tags, list);\r
\r
+     return tags;\r
+ }\r
\r
+-/* Add a new tag to 'tags'. The tags object will create its own copy\r
+- * of the string.\r
+- *\r
+- * Note: The tags object will not do anything to prevent duplicate\r
+- * tags being stored, so the caller really shouldn't pass\r
+- * duplicates. */\r
+-void\r
+-_notmuch_tags_add_tag (notmuch_tags_t *tags, const char *tag)\r
+-{\r
+-    tags->tags = g_list_prepend (tags->tags, talloc_strdup (tags, tag));\r
+-    tags->sorted = 0;\r
+-}\r
+-\r
+-/* Prepare 'tag' for iteration.\r
+- *\r
+- * The internal creator of 'tags' should call this function before\r
+- * returning 'tags' to the user to call the public functions such as\r
+- * notmuch_tags_valid, notmuch_tags_get, and\r
+- * notmuch_tags_move_to_next. */\r
+-void\r
+-_notmuch_tags_prepare_iterator (notmuch_tags_t *tags)\r
+-{\r
+-    if (! tags->sorted)\r
+-      tags->tags = g_list_sort (tags->tags, (GCompareFunc) strcmp);\r
+-    tags->sorted = 1;\r
+-\r
+-    tags->iterator = tags->tags;\r
+-}\r
+-\r
+ notmuch_bool_t\r
+ notmuch_tags_valid (notmuch_tags_t *tags)\r
+ {\r
+@@ -101,7 +62,7 @@ notmuch_tags_get (notmuch_tags_t *tags)\r
+     if (tags->iterator == NULL)\r
+       return NULL;\r
\r
+-    return (char *) tags->iterator->data;\r
++    return (char *) tags->iterator->string;\r
+ }\r
\r
+ void\r
+diff --git a/lib/thread.cc b/lib/thread.cc\r
+index b29f2c9..5b024fd 100644\r
+--- a/lib/thread.cc\r
++++ b/lib/thread.cc\r
+@@ -535,23 +535,23 @@ notmuch_thread_get_newest_date (notmuch_thread_t *thread)\r
+ notmuch_tags_t *\r
+ notmuch_thread_get_tags (notmuch_thread_t *thread)\r
+ {\r
+-    notmuch_tags_t *tags;\r
++    notmuch_string_list_t *tags;\r
+     GList *keys, *l;\r
\r
+-    tags = _notmuch_tags_create (thread);\r
++    tags = _notmuch_string_list_create (thread);\r
+     if (unlikely (tags == NULL))\r
+       return NULL;\r
\r
+     keys = g_hash_table_get_keys (thread->tags);\r
\r
+     for (l = keys; l; l = l->next)\r
+-      _notmuch_tags_add_tag (tags, (char *) l->data);\r
++      _notmuch_string_list_append (tags, (char *) l->data);\r
\r
+     g_list_free (keys);\r
\r
+-    _notmuch_tags_prepare_iterator (tags);\r
++    _notmuch_string_list_sort (tags);\r
\r
+-    return tags;\r
++    return _notmuch_tags_create (thread, tags, true);\r
+ }\r
\r
+ void\r
+-- \r
+1.7.2.3\r
+\r