* rename mu-threader-utils => mu-container + cleanups, documentation

This commit is contained in:
Dirk-Jan C. Binnema 2011-07-01 20:51:39 +03:00
parent 126fa0e74a
commit 8955703828
4 changed files with 787 additions and 538 deletions

591
src/mu-container.c Normal file
View File

@ -0,0 +1,591 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <string.h> /* for memset */
#include <math.h> /* 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 ("<empty>\n");
return TRUE;
}
subject = (c->msg) ? mu_msg_get_subject (c->msg) : "<none>";
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;
}

196
src/mu-container.h Normal file
View File

@ -0,0 +1,196 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <glib.h>
#include <mu-msg.h>
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__*/

View File

@ -1,454 +0,0 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <string.h> /* 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 ("<empty>\n");
return TRUE;
}
subject = (c->msg) ? mu_msg_get_subject (c->msg) : "<none>";
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;
}

View File

@ -1,84 +0,0 @@
/*
** Copyright (C) 2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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 <glib.h>
#include <mu-msg.h>
/*
* 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__*/