From 895570382876f77d211a58d77c2eae08cf9b018d Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Fri, 1 Jul 2011 20:51:39 +0300 Subject: [PATCH] * rename mu-threader-utils => mu-container + cleanups, documentation --- src/mu-container.c | 591 ++++++++++++++++++++++++++++++++++++++++ src/mu-container.h | 196 +++++++++++++ src/mu-threader-utils.c | 454 ------------------------------ src/mu-threader-utils.h | 84 ------ 4 files changed, 787 insertions(+), 538 deletions(-) create mode 100644 src/mu-container.c create mode 100644 src/mu-container.h delete mode 100644 src/mu-threader-utils.c delete mode 100644 src/mu-threader-utils.h diff --git a/src/mu-container.c b/src/mu-container.c new file mode 100644 index 00000000..8efbe366 --- /dev/null +++ b/src/mu-container.c @@ -0,0 +1,591 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#include /* for memset */ +#include /* for log, ceil */ + +#include "mu-container.h" +#include "mu-msg.h" +#include "mu-msg-iter.h" + + +/* + * path data structure, to determine the thread paths mentioned above; + * the path is filled as we're traversing the tree of MuContainers + * (messages) + */ +struct _Path { + int *_data; + guint _len; +}; +typedef struct _Path Path; + +static Path* path_new (guint initial); +static void path_destroy (Path *p); +static void path_inc (Path *p, guint index); +static gchar* path_to_string (Path *p, const char* frmt); + +MuContainer* +mu_container_new (MuMsg *msg, guint docid, const char *msgid) +{ + MuContainer *c; + + g_return_val_if_fail (!msg || docid != 0, NULL); + + c = g_slice_new0 (MuContainer); + if (msg) + c->msg = mu_msg_ref (msg); + + c->docid = docid; + c->msgid = msgid; + + return c; +} + +void +mu_container_destroy (MuContainer *c) +{ + if (!c) + return; + + if (c->msg) + mu_msg_unref (c->msg); + + g_slice_free (MuContainer, c); +} + + +static void +set_parent (MuContainer *c, MuContainer *parent) +{ + while (c) { + c->parent = parent; + c = c->next; + } +} + +static MuContainer* +find_last (MuContainer *c) +{ + while (c && c->next) + c = c->next; + + return c; +} + + +#if 0 +static gboolean +check_dup (MuContainer *c, GHashTable *hash) +{ + if (g_hash_table_lookup (hash, c)) { + g_warning ("ALREADY!!"); + mu_container_dump (c, TRUE); + g_assert (0); + } else + g_hash_table_insert (hash, c, GUINT_TO_POINTER(TRUE)); + + return TRUE; +} + + +G_GNUC_UNUSED static void +assert_no_duplicates (MuContainer *c) +{ + GHashTable *hash; + + hash = g_hash_table_new (g_direct_hash, g_direct_equal); + + mu_container_foreach (c, + (MuContainerForeachFunc)check_dup, + hash); + + g_hash_table_destroy (hash); +} +#endif + + +MuContainer* +mu_container_append_siblings (MuContainer *c, MuContainer *sibling) +{ + g_assert (c); + + g_return_val_if_fail (c, NULL); + g_return_val_if_fail (sibling, NULL); + g_return_val_if_fail (c != sibling, NULL); + + /* assert_no_duplicates (c); */ + + set_parent (sibling, c->parent); + (find_last(c))->next = sibling; + + /* assert_no_duplicates (c); */ + + return c; +} + +MuContainer* +mu_container_remove_sibling (MuContainer *c, MuContainer *sibling) +{ + MuContainer *cur, *prev; + + g_return_val_if_fail (c, NULL); + g_return_val_if_fail (sibling, NULL); + + for (prev = NULL, cur = c; cur; cur = cur->next) { + + if (cur == sibling) { + if (!prev) + c = cur->next; + else + prev->next = cur->next; + break; + } + prev = cur; + } + + return c; +} + +MuContainer* +mu_container_append_children (MuContainer *c, MuContainer *child) +{ + g_return_val_if_fail (c, NULL); + g_return_val_if_fail (child, NULL); + g_return_val_if_fail (c != child, NULL); + + /* assert_no_duplicates (c); */ + + set_parent (child, c); + if (!c->child) + c->child = child; + else + c->child = mu_container_append_siblings (c->child, child); + + /* assert_no_duplicates (c->child); */ + + return c; +} + + +MuContainer* +mu_container_remove_child (MuContainer *c, MuContainer *child) +{ + g_return_val_if_fail (c, NULL); + g_return_val_if_fail (child, NULL); + + g_assert (!child->child); + g_return_val_if_fail (!child->child, NULL); + g_return_val_if_fail (c != child, NULL); + + c->child = mu_container_remove_sibling (c->child, child); + + return c; +} + +typedef void (*MuContainerPathForeachFunc) (MuContainer*, gpointer, Path*); + +static void +mu_container_path_foreach_real (MuContainer *c, guint level, Path *path, + MuContainerPathForeachFunc func, gpointer user_data) +{ + if (!c) + return; + + path_inc (path, level); + func (c, user_data, path); + + /* children */ + mu_container_path_foreach_real (c->child, level + 1, path, func, user_data); + + /* siblings */ + mu_container_path_foreach_real (c->next, level, path, func, user_data); +} + +static void +mu_container_path_foreach (MuContainer *c, MuContainerPathForeachFunc func, + gpointer user_data) +{ + Path *path; + + path = path_new (100); + + mu_container_path_foreach_real (c, 0, path, func, user_data); + + path_destroy (path); +} + + +gboolean +mu_container_foreach (MuContainer *c, MuContainerForeachFunc func, + gpointer user_data) +{ + g_return_val_if_fail (func, FALSE); + + if (!c) + return TRUE; + + if (!mu_container_foreach (c->child, func, user_data)) + return FALSE; /* recurse into children */ + + /* recurse into siblings */ + if (!mu_container_foreach (c->next, func, user_data)) + return FALSE; + + return func (c, user_data); +} + + +MuContainer* +mu_container_splice_children (MuContainer *parent, MuContainer *child) +{ + MuContainer *newchild; + + g_return_val_if_fail (parent, NULL); + g_return_val_if_fail (child, NULL); + g_return_val_if_fail (parent != child, NULL); + + newchild = child->child; + child->child=NULL; + + mu_container_remove_child (parent, child); + + return mu_container_append_children (parent, newchild); +} + + +static GSList* +mu_container_to_list (MuContainer *c) +{ + GSList *lst; + + for (lst = NULL; c; c = c->next) + lst = g_slist_prepend (lst, c); + + return lst; +} + + +static MuContainer* +mu_container_from_list (GSList *lst) +{ + MuContainer *c, *cur; + + if (!lst) + return NULL; + + for (c = cur = (MuContainer*)lst->data; cur; lst = g_slist_next(lst)) { + cur->next = lst ? (MuContainer*)lst->data : NULL; + cur=cur->next; + } + + return c; +} + +struct _SortFuncData { + GCompareDataFunc func; + gboolean invert; + gpointer user_data; +}; +typedef struct _SortFuncData SortFuncData; + + +static int +sort_func_wrapper (MuContainer *a, MuContainer *b, SortFuncData *data) +{ + MuContainer *a1, *b1; + + /* use the first non-empty 'left child' message if this one + * is */ + for (a1 = a; a1->msg == NULL && a1->child != NULL; a1 = a1->child); + for (b1 = b; b1->msg == NULL && b1->child != NULL; b1 = b1->child); + + if (data->invert) + return data->func (b1, a1, data->user_data); + else + return data->func (a1, b1, data->user_data); +} + +static MuContainer* +mu_container_sort_real (MuContainer *c, SortFuncData *sfdata) +{ + GSList *lst; + MuContainer *cur; + + if (!c) + return NULL; + + for (cur = c; cur; cur = cur->next) + if (cur->child) + cur->child = mu_container_sort_real (cur->child, sfdata); + + /* sort siblings */ + lst = mu_container_to_list (c); + lst = g_slist_sort_with_data(lst, + (GCompareDataFunc)sort_func_wrapper, + sfdata); + c = mu_container_from_list (lst); + g_slist_free (lst); + + return c; +} + + +MuContainer* +mu_container_sort (MuContainer *c, GCompareDataFunc func, gpointer user_data, + gboolean invert) +{ + + SortFuncData sfdata = { func, invert, user_data }; + + g_return_val_if_fail (c, NULL); + g_return_val_if_fail (func, NULL); + + return mu_container_sort_real (c, &sfdata); +} + + +static gboolean +unequal (MuContainer *a, MuContainer *b) +{ + return a == b ? FALSE : TRUE; +} + + +gboolean +mu_container_reachable (MuContainer *haystack, MuContainer *needle) +{ + g_return_val_if_fail (haystack, FALSE); + g_return_val_if_fail (needle, FALSE); + + if (!mu_container_foreach + (haystack, (MuContainerForeachFunc)unequal, needle)) + return TRUE; + + return FALSE; +} + + +static gboolean +dump_container (MuContainer *c) +{ + const gchar* subject; + + if (!c) { + g_print ("\n"); + return TRUE; + } + + subject = (c->msg) ? mu_msg_get_subject (c->msg) : ""; + + g_print ("[%s][%s m:%p p:%p docid:%u %s]\n",c->msgid, subject, (void*)c, + (void*)c->parent, c->docid, + c->msg ? mu_msg_get_path (c->msg) : ""); + + return TRUE; +} + + +void +mu_container_dump (MuContainer *c, gboolean recursive) +{ + g_return_if_fail (c); + + if (!recursive) + dump_container (c); + else + mu_container_foreach (c, (MuContainerForeachFunc)dump_container, + NULL); +} + + + +static Path* +path_new (guint initial) +{ + Path *p; + + p = g_slice_new0 (Path); + + p->_data = g_new0 (int, initial); + p->_len = initial; + + return p; +} + +static void +path_destroy (Path *p) +{ + if (!p) + return; + + g_free (p->_data); + g_slice_free (Path, p); +} + +static void +path_inc (Path *p, guint index) +{ + if (index + 1 >= p->_len) { + p->_data = g_renew (int, p->_data, 2 * p->_len); + memset (&p->_data[p->_len], 0, p->_len); + p->_len *= 2; + } + + ++p->_data[index]; + p->_data[index + 1] = 0; +} + + +static gchar* +path_to_string (Path *p, const char* frmt) +{ + char *str; + guint u; + + if (!p->_data) + return NULL; + + for (u = 0, str = NULL; p->_data[u] != 0; ++u) { + + char segm[16]; + snprintf (segm, sizeof(segm), frmt, p->_data[u] - 1); + + if (!str) + str = g_strdup (segm); + else { + gchar *tmp; + tmp = g_strdup_printf ("%s:%s", str, segm); + g_free (str); + str = tmp; + } + } + + return str; +} + + +static MuMsgIterThreadInfo* +thread_info_new (gchar *threadpath, gboolean root, gboolean child, + gboolean empty_parent, gboolean is_dup) +{ + MuMsgIterThreadInfo *ti; + + ti = g_slice_new (MuMsgIterThreadInfo); + ti->threadpath = threadpath; + + ti->prop = 0; + ti->prop |= root ? MU_MSG_ITER_THREAD_PROP_ROOT : 0; + ti->prop |= child ? MU_MSG_ITER_THREAD_PROP_FIRST_CHILD : 0; + ti->prop |= empty_parent ? MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT : 0; + ti->prop |= is_dup ? MU_MSG_ITER_THREAD_PROP_DUP : 0; + + return ti; +} + +static void +thread_info_destroy (MuMsgIterThreadInfo *ti) +{ + if (ti) { + g_free (ti->threadpath); + g_slice_free (MuMsgIterThreadInfo, ti); + } +} + + +struct _ThreadInfo { + GHashTable *hash; + const char* format; +}; +typedef struct _ThreadInfo ThreadInfo; + + +static void +add_to_thread_info_hash (GHashTable *thread_info_hash, MuContainer *c, + char *threadpath) +{ + gboolean is_root, first_child, empty_parent, is_dup; + + /* 'root' means we're a child of the dummy root-container */ + is_root = (c->parent == NULL); + + first_child = is_root ? FALSE : (c->parent->child == c); + empty_parent = is_root ? FALSE : (!c->parent->msg); + is_dup = c->flags & MU_CONTAINER_FLAG_DUP; + + g_hash_table_insert (thread_info_hash, + GUINT_TO_POINTER(c->docid), + thread_info_new (threadpath, + is_root, + first_child, + empty_parent, + is_dup)); +} + +/* device a format string that is the minimum size to fit up to + * matchnum matches -- returns static memory */ +static const char* +thread_segment_format_string (size_t matchnum) +{ + unsigned digitnum; + static char frmt[16]; + + /* get the number of digits needed in a hex-representation of + * matchnum */ + digitnum = (unsigned) (ceil (log(matchnum)/log(16))); + snprintf (frmt, sizeof(frmt),"%%0%ux", digitnum); + + return frmt; +} + +static gboolean +add_thread_info (MuContainer *c, ThreadInfo *ti, Path *path) +{ + gchar *pathstr; + + pathstr = path_to_string (path, ti->format); + add_to_thread_info_hash (ti->hash, c, pathstr); + + return TRUE; +} + + +GHashTable* +mu_container_thread_info_hash_new (MuContainer *root_set, size_t matchnum) +{ + ThreadInfo ti; + + g_return_val_if_fail (root_set, NULL); + g_return_val_if_fail (matchnum > 0, NULL); + + /* create hash docid => thread-info */ + ti.hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, + (GDestroyNotify)thread_info_destroy); + + ti.format = thread_segment_format_string (matchnum); + + mu_container_path_foreach (root_set, + (MuContainerPathForeachFunc)add_thread_info, + &ti); + + return ti.hash; +} + + diff --git a/src/mu-container.h b/src/mu-container.h new file mode 100644 index 00000000..f5018621 --- /dev/null +++ b/src/mu-container.h @@ -0,0 +1,196 @@ +/* +** Copyright (C) 2011 Dirk-Jan C. Binnema +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + +#ifndef __MU_CONTAINER_H__ +#define __MU_CONTAINER_H__ + +#include +#include + +enum _MuContainerFlag { + MU_CONTAINER_FLAG_NONE = 0, + MU_CONTAINER_FLAG_DELETE = 1 << 0, + MU_CONTAINER_FLAG_SPLICE = 1 << 1, + MU_CONTAINER_FLAG_DUP = 1 << 2 +}; + +typedef guint8 MuContainerFlag; + +/* + * MuContainer data structure, as seen in JWZs document: + * http://www.jwz.org/doc/threading.html + */ +struct _MuContainer { + struct _MuContainer *parent, *child, *next; + MuContainerFlag flags; + MuMsg *msg; + guint docid; + const char* msgid; +}; +typedef struct _MuContainer MuContainer; + + +/** + * create a new Container object + * + * @param msg a MuMsg, or NULL; when it's NULL, docid should be 0 + * @param docid a Xapian docid, or 0 + * @param msgid a message id, or NULL + * + * @return a new Container instance, or NULL in case of error; free + * with mu_container_destroy + */ +MuContainer* mu_container_new (MuMsg *msg, guint docid, const char* msgid); + + +/** + * free a Container object + * + * @param c a Container object, or NULL + */ +void mu_container_destroy (MuContainer *c); + + + +/** + * append new child(ren) to this container; the child(ren) container's + * parent pointer will point to this one + * + * @param c a Container instance + * @param child a child + * + * @return the Container instance with a child added + */ +MuContainer* mu_container_append_children (MuContainer *c, MuContainer *child); + +/** + * append a new sibling to this (list of) containers; all the siblings + * will get the same parent that @c has + * + * @param c a container instance + * @param sibling a sibling + * + * @return the container (list) with the sibling(s) appended + */ +MuContainer* mu_container_append_siblings (MuContainer *c, MuContainer *sibling); + +/** + * remove a _single_ child container from a container + * + * @param c a container instance + * @param child the child container to remove + * + * @return the container with the child removed; if the container did + * have this child, nothing changes + */ +MuContainer* mu_container_remove_child (MuContainer *c, MuContainer *child); + +/** + * remove a _single_ sibling container from a container + * + * @param c a container instance + * @param sibling the sibling container to remove + * + * @return the container with the sibling removed; if the container did + * have this sibling, nothing changes + */ +MuContainer* mu_container_remove_sibling (MuContainer *c, MuContainer *sibling); + + +/** + * promote child's children to be parent's children and remove child + * + * @param parent a container instance + * @param child a child of this container + * + * @return the new container with it's children's children promoted + */ +MuContainer* mu_container_splice_children (MuContainer *parent, + MuContainer *child); + +typedef gboolean (*MuContainerForeachFunc) (MuContainer*, gpointer); + +/** + * execute some function on all siblings an children of some container + * (recursively) until all children have been visited or the callback + * function returns FALSE + * + * @param c a container + * @param func a function to call for each container + * @param user_data a pointer to pass to the callback function + * + * @return + */ +gboolean mu_container_foreach (MuContainer *c, + MuContainerForeachFunc func, + gpointer user_data); + +/** + * check wither container needle is a child or sibling (recursively) + * of container haystack + * + * @param haystack a container + * @param needle a container + * + * @return TRUE if needle is reachable from haystack, FALSE otherwise + */ +gboolean mu_container_reachable (MuContainer *haystack, MuContainer *needle); + + +/** + * dump the container to stdout (for debugging) + * + * @param c a container + * @param recursive whether to include siblings, children + */ +void mu_container_dump (MuContainer *c, gboolean recursive); + + +typedef int (*MuContainerCmpFunc) (MuContainer *c1, MuContainer *c2, + gpointer user_data); + +/** + * sort the tree of MuContainers, recursively; ie. each of the list of + * siblings (children) will be sorted according to @func; if the + * container is empty, the first non-empty 'leftmost' child is used. + * + * @param c a container + * @param func a sorting function + * @param user_data a user pointer to pass to the sorting function + * @param invert if TRUE, invert the sorting order + * + * @return a sorted container + */ +MuContainer *mu_container_sort (MuContainer *c, GCompareDataFunc func, + gpointer user_data, gboolean invert); + +/** + * create a hashtable with maps document-ids to information about them, + * ie. Xapian docid => MuMsgIterThreadInfo + * + * @param root_set the containers @param matchnum the number of + * matches in the list (this is needed to determine the shortest + * possible collation keys ('threadpaths') for the messages + * + * @return a hash; free with g_hash_table_destroy + */ +GHashTable* mu_container_thread_info_hash_new (MuContainer *root_set, + size_t matchnum); + +#endif /*__MU_CONTAINER_H__*/ diff --git a/src/mu-threader-utils.c b/src/mu-threader-utils.c deleted file mode 100644 index d4dccc71..00000000 --- a/src/mu-threader-utils.c +++ /dev/null @@ -1,454 +0,0 @@ -/* -** Copyright (C) 2011 Dirk-Jan C. Binnema -** -** This program is free software; you can redistribute it and/or modify it -** under the terms of the GNU General Public License as published by the -** Free Software Foundation; either version 3, or (at your option) any -** later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software Foundation, -** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -*/ - -#include /* for memset */ - -#include "mu-threader-utils.h" -#include "mu-msg.h" - -struct _Path { - int *_data; - guint _len; -}; - -Container* -container_new (MuMsg *msg, guint docid, const char *msgid) -{ - Container *c; - - g_return_val_if_fail (!msg || docid != 0, NULL); - - c = g_slice_new0 (Container); - if (msg) - c->msg = mu_msg_ref (msg); - - c->docid = docid; - c->msgid = msgid; - - return c; -} - -void -container_destroy (Container *c) -{ - if (!c) - return; - - if (c->msg) - mu_msg_unref (c->msg); - - g_slice_free (Container, c); -} - - -static void -set_parent (Container *c, Container *parent) -{ - while (c) { - c->parent = parent; - c = c->next; - } -} - -static Container* -find_last (Container *c) -{ - while (c && c->next) - c = c->next; - - return c; -} - - -#if 0 -static gboolean -check_dup (Container *c, GHashTable *hash) -{ - if (g_hash_table_lookup (hash, c)) { - g_warning ("ALREADY!!"); - container_dump (c, TRUE); - g_assert (0); - } else - g_hash_table_insert (hash, c, GUINT_TO_POINTER(TRUE)); - - return TRUE; -} - - -G_GNUC_UNUSED static void -assert_no_duplicates (Container *c) -{ - GHashTable *hash; - - hash = g_hash_table_new (g_direct_hash, g_direct_equal); - - container_foreach (c, - (ContainerForeachFunc)check_dup, - hash); - - g_hash_table_destroy (hash); -} -#endif - - -Container* -container_append_siblings (Container *c, Container *sibling) -{ - g_assert (c); - - g_return_val_if_fail (c, NULL); - g_return_val_if_fail (sibling, NULL); - g_return_val_if_fail (c != sibling, NULL); - - /* assert_no_duplicates (c); */ - - set_parent (sibling, c->parent); - (find_last(c))->next = sibling; - - /* assert_no_duplicates (c); */ - - return c; -} - -Container* -container_remove_sibling (Container *c, Container *sibling) -{ - Container *cur, *prev; - - g_return_val_if_fail (c, NULL); - g_return_val_if_fail (sibling, NULL); - - for (prev = NULL, cur = c; cur; cur = cur->next) { - - if (cur == sibling) { - if (!prev) - c = cur->next; - else - prev->next = cur->next; - break; - } - prev = cur; - } - - return c; -} - -Container* -container_append_children (Container *c, Container *child) -{ - g_return_val_if_fail (c, NULL); - g_return_val_if_fail (child, NULL); - g_return_val_if_fail (c != child, NULL); - - /* assert_no_duplicates (c); */ - - set_parent (child, c); - if (!c->child) - c->child = child; - else - c->child = container_append_siblings (c->child, child); - - /* assert_no_duplicates (c->child); */ - - return c; -} - - -Container* -container_remove_child (Container *c, Container *child) -{ - g_return_val_if_fail (c, NULL); - g_return_val_if_fail (child, NULL); - - g_assert (!child->child); - g_return_val_if_fail (!child->child, NULL); - g_return_val_if_fail (c != child, NULL); - - c->child = container_remove_sibling (c->child, child); - - return c; -} - -void -container_path_foreach_real (Container *c, guint level, Path *path, - ContainerPathForeachFunc func, gpointer user_data) -{ - if (!c) - return; - - path_inc (path, level); - func (c, user_data, path); - - /* children */ - container_path_foreach_real (c->child, level + 1, path, func, user_data); - - /* siblings */ - container_path_foreach_real (c->next, level, path, func, user_data); -} - - -void -container_path_foreach (Container *c, ContainerPathForeachFunc func, - gpointer user_data) -{ - Path *path; - - path = path_new (100); - - container_path_foreach_real (c, 0, path, func, user_data); - - path_destroy (path); -} - - -gboolean -container_foreach (Container *c, ContainerForeachFunc func, gpointer user_data) -{ - if (!c) - return TRUE; - - if (!container_foreach (c->child, func, user_data)) - return FALSE; /* recurse into children */ - - /* recurse into siblings */ - if (!container_foreach (c->next, func, user_data)) - return FALSE; - - return func (c, user_data); -} - - -Container* -container_splice_children (Container *parent, Container *child) -{ - Container *newchild; - - g_return_val_if_fail (parent, NULL); - g_return_val_if_fail (child, NULL); - g_return_val_if_fail (parent != child, NULL); - - newchild = child->child; - child->child=NULL; - - container_remove_child (parent, child); - - return container_append_children (parent, newchild); -} - - -GSList* -container_to_list (Container *c) -{ - GSList *lst; - - for (lst = NULL; c; c = c->next) - lst = g_slist_prepend (lst, c); - - return lst; -} - - -static Container* -container_from_list (GSList *lst) -{ - Container *c, *cur; - - if (!lst) - return NULL; - - for (c = cur = (Container*)lst->data; cur; lst = g_slist_next(lst)) { - cur->next = lst ? (Container*)lst->data : NULL; - cur=cur->next; - } - - return c; -} - -struct _SortFuncData { - GCompareDataFunc func; - gboolean invert; - gpointer user_data; -}; -typedef struct _SortFuncData SortFuncData; - - -static int -sort_func_wrapper (Container *a, Container *b, SortFuncData *data) -{ - Container *a1, *b1; - - /* use the first non-empty 'left child' message if this one - * is */ - for (a1 = a; a1->msg == NULL && a1->child != NULL; a1 = a1->child); - for (b1 = b; b1->msg == NULL && b1->child != NULL; b1 = b1->child); - - if (data->invert) - return data->func (b1, a1, data->user_data); - else - return data->func (a1, b1, data->user_data); -} - -static Container* -container_sort_real (Container *c, SortFuncData *sfdata) -{ - GSList *lst; - Container *cur; - - if (!c) - return NULL; - - for (cur = c; cur; cur = cur->next) - if (cur->child) - cur->child = container_sort_real (cur->child, sfdata); - - /* sort siblings */ - lst = container_to_list (c); - lst = g_slist_sort_with_data(lst, - (GCompareDataFunc)sort_func_wrapper, - sfdata); - c = container_from_list (lst); - g_slist_free (lst); - - return c; -} - - -Container * -container_sort (Container *c, GCompareDataFunc func, gpointer user_data, - gboolean invert) -{ - SortFuncData sfdata = { func, invert, user_data }; - - return container_sort_real (c, &sfdata); -} - - -static gboolean -unequal (Container *a, Container *b) -{ - return a == b ? FALSE : TRUE; -} - - -gboolean -container_reachable (Container *haystack, Container *needle) -{ - if (!container_foreach - (haystack, (ContainerForeachFunc)unequal, needle)) - return TRUE; - - return FALSE; -} - - -static gboolean -dump_container (Container *c) -{ - const gchar* subject; - - if (!c) { - g_print ("\n"); - return TRUE; - } - - subject = (c->msg) ? mu_msg_get_subject (c->msg) : ""; - - g_print ("[%s][%s m:%p p:%p docid:%u %s]\n",c->msgid, subject, (void*)c, - (void*)c->parent, c->docid, - c->msg ? mu_msg_get_path (c->msg) : ""); - - return TRUE; -} - - -void -container_dump (Container *c, gboolean recursive) -{ - if (!recursive) - dump_container (c); - else - container_foreach (c, (ContainerForeachFunc)dump_container, - NULL); -} - - - -Path* -path_new (guint initial) -{ - Path *p; - - p = g_slice_new0 (Path); - - p->_data = g_new0 (int, initial); - p->_len = initial; - - return p; -} - -void -path_destroy (Path *p) -{ - if (!p) - return; - - g_free (p->_data); - g_slice_free (Path, p); -} - -void -path_inc (Path *p, guint index) -{ - if (index + 1 >= p->_len) { - p->_data = g_renew (int, p->_data, 2 * p->_len); - memset (&p->_data[p->_len], 0, p->_len); - p->_len *= 2; - } - - ++p->_data[index]; - p->_data[index + 1] = 0; -} - - -gchar* -path_to_string (Path *p, const char* frmt) -{ - char *str; - guint u; - - if (!p->_data) - return NULL; - - for (u = 0, str = NULL; p->_data[u] != 0; ++u) { - - char segm[16]; - snprintf (segm, sizeof(segm), frmt, p->_data[u] - 1); - - if (!str) - str = g_strdup (segm); - else { - gchar *tmp; - tmp = g_strdup_printf ("%s:%s", str, segm); - g_free (str); - str = tmp; - } - } - - return str; -} diff --git a/src/mu-threader-utils.h b/src/mu-threader-utils.h deleted file mode 100644 index 5e29e5b7..00000000 --- a/src/mu-threader-utils.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -** Copyright (C) 2011 Dirk-Jan C. Binnema -** -** This program is free software; you can redistribute it and/or modify it -** under the terms of the GNU General Public License as published by the -** Free Software Foundation; either version 3, or (at your option) any -** later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software Foundation, -** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -*/ - -#ifndef __MU_THREADER_UTILS_H__ -#define __MU_THREADER_UTILS_H__ - -#include -#include - -/* - * path data structure, to determine the thread paths mentioned - * above - * */ -struct _Path; -typedef struct _Path Path; - -Path* path_new (guint initial); -void path_destroy (Path *p); -void path_inc (Path *p, guint index); -gchar* path_to_string (Path *p, const char* frmt); - -/* Container data structure, as seen in the JWZ-doc* - * -*/ -enum _ContainerFlag { - CONTAINER_FLAG_NONE = 0, - CONTAINER_FLAG_DELETE = 1 << 0, - CONTAINER_FLAG_SPLICE = 1 << 1, - CONTAINER_FLAG_DUP = 1 << 2 -}; -typedef guint8 ContainerFlag; - - -struct _Container { - struct _Container *parent, *child, *next; - ContainerFlag flags; - MuMsg *msg; - guint docid; - const char* msgid; -}; -typedef struct _Container Container; - -Container* container_new (MuMsg *msg, guint docid, const char* msgid); -void container_destroy (Container *c); -Container* container_append_children (Container *c, Container *child); -Container* container_append_siblings (Container *c, Container *sibling); -Container* container_remove_child (Container *c, Container *child); -Container* container_remove_sibling (Container *c, Container *sibling); -Container* container_splice_children (Container *parent, Container *child); - -typedef gboolean (*ContainerForeachFunc) (Container*, gpointer); -gboolean container_foreach (Container *c, - ContainerForeachFunc func, - gpointer user_data); - -typedef void (*ContainerPathForeachFunc) (Container*, gpointer, Path*); -void container_path_foreach (Container *c, - ContainerPathForeachFunc func, - gpointer user_data); - -gboolean container_reachable (Container *haystack, Container *needle); -void container_dump (Container *c, gboolean recursive); - -typedef int (*ContainerCmpFunc) (Container *c1, Container *c2, gpointer user_data); -Container * container_sort (Container *c, GCompareDataFunc func, - gpointer user_data, gboolean invert); - -#endif /*__MU_THREADER_UTILS_H__*/