[Xfce4-commits] <squeeze:master> Merge branch 'peter/btree' into merge

Peter de Ridder noreply at xfce.org
Thu Dec 22 23:00:08 CET 2011


Updating branch refs/heads/master
         to e012291673d37405022b2f8505d1f83463fe028c (commit)
       from ce6ae3e773c0636b3f996f96d4a659e2f040a576 (commit)

commit e012291673d37405022b2f8505d1f83463fe028c
Merge: ce6ae3e 1b7de7a
Author: Peter de Ridder <peter at xfce.org>
Date:   Tue Dec 20 14:13:30 2011 +0100

    Merge branch 'peter/btree' into merge

commit 1b7de7a745e0ac8823f31cbc4fac3af52da0cde7
Author: Peter de Ridder <peter at xfce.org>
Date:   Tue Oct 11 20:43:08 2011 +0200

    Balance btree only if it helps in depth

commit 2f2c6178e58c4e3eb4e9a0bbaf39239fdd266fd8
Author: Peter de Ridder <peter at xfce.org>
Date:   Sun Oct 9 20:21:03 2011 +0200

    Optionally balance btree after remove

commit 1f04b35581219671f11601e0b057f0b26a636769
Author: Peter de Ridder <peter at xfce.org>
Date:   Sun Oct 9 16:47:12 2011 +0200

    Start supporting delete from the btree

commit f3472d91253a3016d68e2941b128a0d96f52ae00
Author: Peter de Ridder <peter at xfce.org>
Date:   Tue Oct 4 21:42:13 2011 +0200

    Balance the btree
    
    btree delete isn't supported

commit d3cc31e6fb872e00eaad4291746c551435eea1c2
Author: Peter de Ridder <peter at xfce.org>
Date:   Sun Oct 2 12:09:39 2011 +0200

    Replaced archive iter buffer slist with a btree
    
    The btree isn't balanced and delete isn't supported

 libsqueeze/Makefile.am          |    2 +-
 libsqueeze/archive-iter.c       |  161 ++++-------
 libsqueeze/archive.c            |    6 +-
 libsqueeze/btree.c              |  646 +++++++++++++++++++++++++++++++++++++++
 libsqueeze/{slist.h => btree.h} |   50 ++--
 libsqueeze/slist.c              |  109 -------
 6 files changed, 727 insertions(+), 247 deletions(-)

diff --git a/libsqueeze/Makefile.am b/libsqueeze/Makefile.am
index 51cc350..b7beadb 100644
--- a/libsqueeze/Makefile.am
+++ b/libsqueeze/Makefile.am
@@ -5,6 +5,7 @@ libsqueeze_2_la_SOURCES =  \
 	archive-iter-pool.c archive-iter-pool.h \
 	archive-iter.c archive-iter.h \
 	archive-tempfs.c archive-tempfs.h \
+	btree.c btree.h \
 	command-queue.c command-queue.h \
 	command-option.c command-option.h \
 	internals.c internals.h \
@@ -13,7 +14,6 @@ libsqueeze_2_la_SOURCES =  \
 	parser-context.c parser-context.h \
 	parser.c parser.h \
 	scanf-parser.c scanf-parser.h \
-	slist.c slist.h \
 	support-reader.c support-reader.h \
 	support-template.c support-template.h \
 	support-factory.c support-factory.h
diff --git a/libsqueeze/archive-iter.c b/libsqueeze/archive-iter.c
index 6288535..0c3b5f4 100644
--- a/libsqueeze/archive-iter.c
+++ b/libsqueeze/archive-iter.c
@@ -13,7 +13,6 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
-
 #include <config.h>
 #include <string.h>
 #include <glib.h>
@@ -25,12 +24,12 @@
 #include "libsqueeze.h"
 #include "libsqueeze-view.h"
 #include "support-factory.h"
-#include "slist.h"
+#include "btree.h"
 
 #include "internals.h"
 
 #ifndef LSQ_ENTRY_CHILD_BUFFER_SIZE
-#define LSQ_ENTRY_CHILD_BUFFER_SIZE 500
+#define LSQ_ENTRY_CHILD_BUFFER_SIZE 8000
 #endif
 
 #ifdef LSQ_ENTRY_CHILD_BUFFER_DYNAMIC_SIZE
@@ -39,10 +38,6 @@ guint buffer_flush_size = LSQ_ENTRY_CHILD_BUFFER_SIZE;
 #define LSQ_ENTRY_CHILD_BUFFER_SIZE buffer_flush_size
 #endif
 
-#ifndef LSQ_ENTRY_BUFFER_INTERVALSIZE
-#define LSQ_ENTRY_BUFFER_INTERVALSIZE 20
-#endif
-
 #ifndef LSQ_MIME_DIRECTORY
 #define LSQ_MIME_DIRECTORY "inode/directory"
 #endif
@@ -69,8 +64,6 @@ inline static LSQArchiveEntry*
 lsq_archive_entry_nth_child(const LSQArchiveEntry *, guint);
 inline static void
 lsq_archive_entry_flush_buffer(LSQArchiveEntry *);
-inline static void
-lsq_archive_entry_build_buffer_index(LSQArchiveEntry *);
 static LSQArchiveEntry *
 lsq_archive_entry_get_child(const LSQArchiveEntry *, const gchar *);
 static LSQArchiveEntry *
@@ -102,8 +95,8 @@ struct _LSQArchiveEntry
 	gchar *content_type;
 	gpointer props;
 	LSQArchiveEntry **children;
-	LSQSList *buffer;
-	LSQSIndexList *buffer_index;
+	LSQBTree *buffer;
+        guint buffer_length;
 };
 
 
@@ -989,7 +982,10 @@ static void
 lsq_archive_entry_save_free(const LSQArchive *archive, LSQArchiveEntry *entry)
 {
 	guint i = 0; 
-	LSQSList *buffer_iter = entry->buffer;
+	LSQBTree *buffer_iter;
+
+	entry->buffer = lsq_btree_flatten(entry->buffer);
+	buffer_iter = entry->buffer;
 
 	/* free the buffer */
 	for ( ; NULL != buffer_iter ; buffer_iter = buffer_iter->next )
@@ -999,8 +995,9 @@ lsq_archive_entry_save_free(const LSQArchive *archive, LSQArchiveEntry *entry)
 		else
 			lsq_archive_entry_save_free(archive, buffer_iter->entry);
 	}
-	lsq_slist_free(entry->buffer);
+	lsq_btree_free(entry->buffer);
 	entry->buffer = NULL;
+        entry->buffer_length = 0;
 
 	/* free the sorted list */
 	if ( NULL != entry->children )
@@ -1026,15 +1023,19 @@ static void
 lsq_archive_entry_free(const LSQArchive *archive, LSQArchiveEntry *entry)
 {
 	guint i = 0; 
-	LSQSList *buffer_iter = entry->buffer;
+	LSQBTree *buffer_iter;
+
+	entry->buffer = lsq_btree_flatten(entry->buffer);
+	buffer_iter = entry->buffer;
 
 	/* free the buffer */
 	for ( ; NULL != buffer_iter; buffer_iter = buffer_iter->next )
 	{
 		lsq_archive_entry_free(archive, buffer_iter->entry);
 	}
-	lsq_slist_free(entry->buffer);
+	lsq_btree_free(entry->buffer);
 	entry->buffer = NULL;
+        entry->buffer_length = 0;
 
 	/* free the sorted list */
 	if ( NULL != entry->children )
@@ -1078,7 +1079,7 @@ inline static guint
 lsq_archive_entry_n_children(const LSQArchiveEntry *entry)
 {
 	/* the first element of the array (*entry->children) contains the size of the array */
-	return ((entry->children?GPOINTER_TO_UINT(*entry->children):0) + lsq_slist_length(entry->buffer));
+	return ((entry->children?GPOINTER_TO_UINT(*entry->children):0) + lsq_btree_length(entry->buffer));
 }
 
 inline static LSQArchiveEntry*
@@ -1099,7 +1100,7 @@ lsq_archive_entry_flush_buffer(LSQArchiveEntry *entry)
 	guint new_i = 1;
 	guint size;
 	guint n_children;
-	LSQSList *buffer_iter = NULL;
+	LSQBTree *buffer_iter = NULL;
 	LSQArchiveEntry **children_old;
 
 	if ( NULL == entry->buffer )
@@ -1107,12 +1108,17 @@ lsq_archive_entry_flush_buffer(LSQArchiveEntry *entry)
 		return;
 	}
 
+	//lsq_btree_print(entry->buffer);
+
+	/* Flatten the btree so we can iterate */
+	entry->buffer = lsq_btree_flatten(entry->buffer);
+
 	/* the first element of the array (*entry->children) contains the size of the array */
 	size = entry->children?GPOINTER_TO_UINT(*entry->children):0;
 	n_children = size;
 	children_old = (LSQArchiveEntry **)entry->children;
 
-	max_children = (n_children + lsq_slist_length(entry->buffer));
+	max_children = (n_children + lsq_btree_length(entry->buffer));
 	
 	/* do all elements of the buffer */
 	entry->children = g_new(LSQArchiveEntry *, max_children+1);
@@ -1164,44 +1170,17 @@ lsq_archive_entry_flush_buffer(LSQArchiveEntry *entry)
 	*entry->children = GUINT_TO_POINTER(n_children);
 	
 	/* free the buffer */
-	lsq_slist_free(entry->buffer);
+	lsq_btree_free(entry->buffer);
 	entry->buffer = NULL;
-	lsq_slist_index_free(entry->buffer_index);
-	entry->buffer_index = NULL;
+        entry->buffer_length = 0;
 
 	g_free(children_old);
 }
 
-inline static void
-lsq_archive_entry_build_buffer_index(LSQArchiveEntry *entry)
-{
-	guint i = 0;
-	LSQSList *buffer_iter = NULL;
-	LSQSIndexList **index_iter = &entry->buffer_index;
-	for ( buffer_iter = entry->buffer ; NULL != buffer_iter ; buffer_iter = buffer_iter->next )
-	{
-		++i;
-		if ( 0 == ( i % LSQ_ENTRY_BUFFER_INTERVALSIZE ) )
-		{
-			if ( NULL == *index_iter )
-			{
-				*index_iter = lsq_slist_index_new();
-			}
-
-			(*index_iter)->index = buffer_iter;
-
-			index_iter = &((*index_iter)->next);
-		}
-	}
-	lsq_slist_index_free(*index_iter);
-	*index_iter = NULL;
-}
-
 static LSQArchiveEntry *
 lsq_archive_entry_get_child(const LSQArchiveEntry *entry, const gchar *filename)
 {
-	LSQSList *entry_buffer, *buffer_iter = NULL;
-	LSQSIndexList *index_iter = NULL;
+	LSQBTree *buffer_iter = NULL;
 	/* the first element of the array (*entry->children) contains the size of the array */
 	guint size = entry->children?GPOINTER_TO_UINT(*entry->children):0;
 	guint pos = 0;
@@ -1240,37 +1219,25 @@ lsq_archive_entry_get_child(const LSQArchiveEntry *entry, const gchar *filename)
 		}
 	}
 
-	/* find a start index into the buffer */
-	entry_buffer = entry->buffer;
-	for ( index_iter = entry->buffer_index; NULL != index_iter; index_iter = index_iter->next )
-	{
-		cmp = strcmp(filename, index_iter->index->entry->filename);
-
-		if ( 0 == cmp )
-		{
-			g_free(_filename);
-			return index_iter->index->entry;
-		}
-		if ( 0 > cmp )
-		{
-			break;
-		}
-		entry_buffer = index_iter->index;
-	}
-
 	/* search the buffer */
-	for ( buffer_iter = entry_buffer; NULL != buffer_iter; buffer_iter = buffer_iter->next )
+	buffer_iter = entry->buffer;
+	while ( NULL != buffer_iter )
 	{
+		/* archive can be NULL */
 		cmp = strcmp(filename, buffer_iter->entry->filename);
 
-		if ( 0 == cmp )
+		if ( cmp < 0 )
 		{
-			g_free(_filename);
-			return buffer_iter->entry;
+		    buffer_iter = buffer_iter->left;
 		}
-		if ( 0 > cmp )
+		else if ( cmp > 0 )
 		{
-			break;
+		    buffer_iter = buffer_iter->right;
+		}
+		else
+		{
+		    g_free(_filename);
+		    return buffer_iter->entry;
 		}
 	}
 
@@ -1279,7 +1246,7 @@ lsq_archive_entry_get_child(const LSQArchiveEntry *entry, const gchar *filename)
 }
 
 static gint
-lsq_archive_entry_filename_compare(LSQArchiveEntry *a, LSQArchiveEntry *b)
+lsq_archive_entry_compare(LSQArchiveEntry *a, LSQArchiveEntry *b)
 {
 	return strcmp(a->filename, b->filename);
 }
@@ -1289,7 +1256,6 @@ lsq_archive_entry_add_child(LSQArchiveEntry *parent, const gchar *filename)
 {
 	LSQArchiveEntry *child = lsq_archive_entry_new(filename);
 	const gchar *contenttype = lsq_archive_entry_get_contenttype(parent);
-	guint length;
 
 	if ( ( NULL == contenttype ) || ( 0 != strcmp ( contenttype, LSQ_MIME_DIRECTORY ) ) )
 	{
@@ -1300,28 +1266,29 @@ lsq_archive_entry_add_child(LSQArchiveEntry *parent, const gchar *filename)
 		*/
 	}
 
-	parent->buffer = lsq_slist_insert_sorted_single(parent->buffer, child, (GCompareFunc)lsq_archive_entry_filename_compare);
+	//g_debug("i: %s", filename);
+	parent->buffer = lsq_btree_insert_sorted_single(parent->buffer, child, (GCompareFunc)lsq_archive_entry_compare);
 
-	/* TODO: cache the length so we doen't have to check every time? */
-	length = lsq_slist_length(parent->buffer);
+	/* Cache the length so we doen't have to check every time */
+	parent->buffer_length++;
 
-	if ( LSQ_ENTRY_CHILD_BUFFER_SIZE == length )
+	if ( LSQ_ENTRY_CHILD_BUFFER_SIZE == parent->buffer_length )
 	{
 		lsq_archive_entry_flush_buffer(parent);
 	}
-	else if ( 0 == ( length % LSQ_ENTRY_BUFFER_INTERVALSIZE ) )
-	{
-		lsq_archive_entry_build_buffer_index(parent);
-	}
 	
 	return child;
 }
 
+static gint
+lsq_archive_entry_filename_compare(const gchar *a_filename, LSQArchiveEntry *b)
+{
+	return strcmp(a_filename, b->filename);
+}
+
 static gboolean
 lsq_archive_entry_remove_child(LSQArchiveEntry *entry, const gchar *filename)
 {
-	LSQSList *buffer_iter = NULL, *prev_iter = NULL;
-
 	/* the first element of the array (*entry->children) contains the size of the array */
 	guint total_size, size = total_size = entry->children?GPOINTER_TO_UINT(*entry->children):0;
 	guint pos = 0;
@@ -1329,6 +1296,7 @@ lsq_archive_entry_remove_child(LSQArchiveEntry *entry, const gchar *filename)
 	gint cmp = 0;
 	const gchar *_pos = strchr(filename, '/');
 	gchar *_filename;
+	LSQArchiveEntry *found = NULL;
 
 	if ( 0 != _pos )
 	{
@@ -1370,31 +1338,10 @@ lsq_archive_entry_remove_child(LSQArchiveEntry *entry, const gchar *filename)
 	}
 
 	/* search the buffer */
-	for(buffer_iter = entry->buffer; buffer_iter; buffer_iter = buffer_iter->next)
-	{
-		cmp = strcmp(_filename, buffer_iter->entry->filename);
-
-		if ( 0 == cmp )
-		{
-			g_free(_filename);
-			if ( NULL != prev_iter )
-			{
-				prev_iter->next = buffer_iter->next;
-			}
-			else
-			{
-				entry->buffer = buffer_iter->next;
-			}
-			g_free(buffer_iter);
-			return TRUE;
-		}
-		if(cmp < 0)
-			break;
-		prev_iter = buffer_iter;
-	}
+	entry->buffer = lsq_btree_remove_sorted_single(entry->buffer, _filename, (GCompareFunc)lsq_archive_entry_filename_compare, &found);
 
 	g_free(_filename);
-	return FALSE;
+	return NULL != found;
 }
 
 inline static const gchar*
diff --git a/libsqueeze/archive.c b/libsqueeze/archive.c
index a2dcd7e..fdac9f4 100644
--- a/libsqueeze/archive.c
+++ b/libsqueeze/archive.c
@@ -31,16 +31,12 @@
 #include "support-template.h"
 #include "support-factory.h"
 
-#include "slist.h"
+#include "btree.h"
 #include "archive-tempfs.h"
 #include "command-queue.h"
 
 #include "internals.h"
 
-#ifndef LSQ_ENTRY_CHILD_BUFFER_SIZE
-#define LSQ_ENTRY_CHILD_BUFFER_SIZE 500
-#endif
-
 static void
 lsq_archive_class_init(LSQArchiveClass *archive_class);
 
diff --git a/libsqueeze/btree.c b/libsqueeze/btree.c
new file mode 100644
index 0000000..5d98f0e
--- /dev/null
+++ b/libsqueeze/btree.c
@@ -0,0 +1,646 @@
+/*
+ *  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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <libxfce4util/libxfce4util.h>
+
+#include "libsqueeze.h"
+#include "support-factory.h"
+#include "internals.h"
+#include "btree.h"
+
+#ifndef LSQ_BTREE_MAX_DEPTH
+#define LSQ_BTREE_MAX_DEPTH 20
+#endif
+
+LSQBTree *
+lsq_btree_insert_sorted_single (
+        LSQBTree *list,
+        LSQArchiveEntry *entry,
+        GCompareFunc cmp_func )
+{
+    gint cmp;
+    LSQBTree *iter;
+    LSQBTree **next = NULL;
+    LSQBTree *new_entry = NULL;
+    LSQBTree *stack[LSQ_BTREE_MAX_DEPTH];
+    guint stack_i = 0;
+    LSQArchiveEntry *swap_entry;
+    LSQBTree *swap_iter;
+    gint swap_balance;
+    gboolean short_side;
+
+    /* Check for a flat list */
+    if ( NULL != list && NULL != list->next )
+    {
+        g_critical("Cannot insert into a flattened tree");
+        return NULL;
+    }
+
+    /* Walk the btree */
+    for ( iter = list; NULL != iter; iter = *next )
+    {
+        /* archive can be NULL */
+        cmp = cmp_func( entry, iter->entry );
+
+        if ( 0 > cmp )
+        {
+            /* Go the the left */
+            next = &iter->left;
+        }
+        else if ( 0 < cmp )
+        {
+            /* Go to the right */
+            next = &iter->right;
+        }
+        else
+        {
+            /* Logic outside this routine dictates we should never find a match */
+            g_critical("THIS SHOULD NOT HAPPEN!!! (the universe has just collapsed)");
+            return NULL;
+        }
+
+        /* Keep a stack of the path we followed */
+        g_return_val_if_fail(stack_i < LSQ_BTREE_MAX_DEPTH, NULL);
+        stack[stack_i++] = iter;
+    }
+
+    /* Create a new tree element */
+    new_entry = g_new0(LSQBTree, 1);
+    new_entry->entry = entry;
+
+    /* Check it this is a new tree */
+    if ( NULL == next )
+    {
+        return new_entry;
+    }
+
+    /* Store ourself in the parent */
+    *next = new_entry;
+
+    /* Balance the tree */
+    while ( 0 < stack_i )
+    {
+        iter = stack[--stack_i];
+
+        /* Calculate new the balance for the parent */
+        if ( iter->left == new_entry )
+        {
+            short_side = iter->balance > 0;
+            --iter->balance;
+        }
+        else
+        {
+            short_side = iter->balance < 0;
+            ++iter->balance;
+        }
+
+        /* The balance in the higher parents doesn't change when the short side changed */
+        if ( FALSE != short_side )
+        {
+            break;
+        }
+
+        if ( 1 < iter->balance )
+        {
+            /* Rotate left */
+            /* The code could be easier if we would just overwrite our parent left or right value.
+             * But instead we move that data from our right to our self and use the right tree link to be placed in the tree as if it was ourself.
+             */
+            /* Letters are tree nodes, numbers are the data. This illustrates a rotate right.
+             *
+             *        A:4     |     A:2
+             *       /   \    |    /   \
+             *     B:2   C:5  |  D:1   B:4
+             *    /   \       |       /   \
+             *  D:1   E:3     |     E:3   C:5
+             */
+            swap_iter = iter->right;
+            swap_balance = swap_iter->balance;
+            /* Only balance if it helps */
+            if ( 0 < swap_balance )
+            {
+                /* Swap the data */
+                swap_entry = iter->entry;
+                iter->entry = swap_iter->entry;
+                swap_iter->entry = swap_entry;
+
+                /* Reformat the tree links */
+                iter->right = swap_iter->right;
+                swap_iter->right = swap_iter->left;
+                swap_iter->left = iter->left;
+                iter->left = swap_iter;
+
+                /* Fix the balance values
+                 *
+                 * if B > 0
+                 *   A = A - 1 - B
+                 * else
+                 *   A = A - 1
+                 *
+                 * diff = A - 1 - B
+                 * if diff < 0
+                 *   B = B - 1 + diff
+                 * else
+                 *   B = B - 1
+                 */
+                swap_iter->balance = iter->balance - 1;
+                if ( 0 < swap_balance )
+                {
+                    swap_iter->balance -= swap_balance;
+                }
+                iter->balance = iter->balance - 1 - swap_balance;
+                if ( 0 < iter->balance )
+                {
+                    iter->balance = 0;
+                }
+                iter->balance += swap_balance - 1;
+
+                /* We added a child so our depth was increased, but we also saved depth by rotation so our parents depth stays the same */
+                break;
+            }
+        }
+        else if ( -1 > iter->balance )
+        {
+            /* Rotate right */
+            /* The code could be easier if we would just overwrite our parent left or right value.
+             * But instead we move that data from our left to our self and use the left tree link to be placed in the tree as if it was ourself.
+             */
+            swap_iter = iter->left;
+            swap_balance = swap_iter->balance;
+            /* Only balance if it helps */
+            if ( 0 > swap_balance )
+            {
+                /* Swap the data */
+                swap_entry = iter->entry;
+                iter->entry = swap_iter->entry;
+                swap_iter->entry = swap_entry;
+
+                /* Reformat the tree links */
+                iter->left = swap_iter->left;
+                swap_iter->left = swap_iter->right;
+                swap_iter->right = iter->right;
+                iter->right = swap_iter;
+
+                /* Fix the balance values
+                 *
+                 * if B < 0
+                 *   A = A + 1 - B
+                 * else
+                 *   A = A + 1
+                 *
+                 * diff = A + 1 - B
+                 * if diff > 0
+                 *   B = B + 1 + diff
+                 * else
+                 *   B = B + 1
+                 */
+                swap_iter->balance = iter->balance + 1;
+                if ( 0 > swap_balance )
+                {
+                    swap_iter->balance -= swap_balance;
+                }
+                iter->balance = iter->balance + 1 - swap_balance;
+                if ( 0 > iter->balance )
+                {
+                    iter->balance = 0;
+                }
+                iter->balance += swap_balance + 1;
+
+                /* We added a child so our depth was increased, but we also saved depth by rotation so our parents depth stays the same */
+                break;
+            }
+        }
+
+        /* Store ourself in new_entry for the check in the next parent */
+        new_entry = iter;
+    }
+
+    return list;
+}
+
+LSQBTree *
+lsq_btree_remove_sorted_single (
+        LSQBTree *list,
+        gchar *filename,
+        GCompareFunc cmp_func,
+        LSQArchiveEntry **found )
+{
+    gint cmp;
+    LSQBTree *iter;
+    LSQBTree **next = &list;
+    LSQBTree *del_entry;
+    LSQBTree *stack[LSQ_BTREE_MAX_DEPTH];
+    guint stack_i = 0;
+    gboolean short_side;
+    LSQBTree *swap_iter;
+#ifdef BALANCE_ON_REMOVE
+    LSQArchiveEntry *swap_entry;
+    gint swap_balance;
+#endif
+
+    if ( NULL != found )
+    {
+        *found = NULL;
+    }
+
+    /* The tree is flattened */
+    if ( NULL == list || NULL != list->next )
+    {
+        for ( iter = list; NULL != iter; iter = iter->next)
+        {
+            cmp = cmp_func( filename, iter->entry );
+
+            if ( 0 == cmp )
+            {
+                if ( NULL != found )
+                {
+                    *found = iter->entry;
+                }
+
+                /* remove it */
+                *next = iter->next;
+                g_free ( iter );
+                return list;
+            }
+            else if ( 0 > cmp )
+            {
+                return list;
+            }
+
+            next = &iter->next;
+        }
+    }
+    else
+    {
+        /* Walk the btree */
+        for ( iter = list; NULL != iter; iter = *next )
+        {
+            /* archive can be NULL */
+            cmp = cmp_func( filename, iter->entry );
+
+            if ( 0 > cmp )
+            {
+                /* Go the the left */
+                next = &iter->left;
+            }
+            else if ( 0 < cmp )
+            {
+                /* Go to the right */
+                next = &iter->right;
+            }
+            else
+            {
+                if ( NULL != found )
+                {
+                    *found = iter->entry;
+                }
+                break;
+            }
+
+            /* Keep a stack of the path we followed */
+            g_return_val_if_fail(stack_i < LSQ_BTREE_MAX_DEPTH, NULL);
+            stack[stack_i++] = iter;
+        }
+
+        /* Not found */
+        if ( NULL == iter )
+        {
+            return list;
+        }
+
+        if ( NULL == iter->left )
+        {
+            *next = iter->right;
+            g_free ( iter );
+        }
+        else if ( NULL == iter->right )
+        {
+            *next = iter->left;
+            g_free ( iter );
+        }
+        else
+        {
+            /* Find either the most right or most left element in the tree and replace iter with it.
+             * We do this by replaceing the content of the iter, not the iter itself.
+             *
+             * We that the longest of the two paths, i the hope to be better balanced. */
+            
+            /* Keep a stack of the path we followed */
+            g_return_val_if_fail(stack_i < LSQ_BTREE_MAX_DEPTH, NULL);
+            stack[stack_i++] = iter;
+
+            /* Right it the longest */
+            if ( 0 < iter->balance )
+            {
+                /* Go right */
+                for ( swap_iter = iter->right; NULL != swap_iter->left; swap_iter = *next )
+                {
+                    next = &swap_iter->left;
+
+                    /* Keep a stack of the path we followed */
+                    g_return_val_if_fail(stack_i < LSQ_BTREE_MAX_DEPTH, NULL);
+                    stack[stack_i++] = iter;
+                }
+
+                /* remove the iter from the list */
+                *next = swap_iter->right;
+
+                /* copy the iter to our current location */
+                iter->entry = swap_iter->entry;
+
+                g_free ( swap_iter );
+            }
+            else
+            {
+                /* Go left */
+                for ( swap_iter = iter->right; NULL != iter->left; iter = iter->left )
+                {
+                    next = &swap_iter->right;
+
+                    /* Keep a stack of the path we followed */
+                    g_return_val_if_fail(stack_i < LSQ_BTREE_MAX_DEPTH, NULL);
+                    stack[stack_i++] = iter;
+                }
+
+                /* remove the iter from the list */
+                *next = swap_iter->left;
+
+                /* copy the iter to our current location */
+                iter->entry = swap_iter->entry;
+
+                g_free ( swap_iter );
+            }
+        }
+
+        /* Get the new pointer for balancing */
+        del_entry = *next;
+
+        /* Balance the tree */
+        while ( 0 < stack_i )
+        {
+            iter = stack[--stack_i];
+
+            /* Calculate new the balance for the parent */
+            if ( iter->left == del_entry )
+            {
+                short_side = 0 <= iter->balance;
+                ++iter->balance;
+            }
+            else
+            {
+                short_side = 0 >= iter->balance;
+                --iter->balance;
+            }
+
+            /* The balance in the higher parents doesn't change when the short side changed */
+            if ( FALSE != short_side )
+            {
+#ifdef BALANCE_ON_REMOVE
+                if ( 1 < iter->balance )
+                {
+                    /* Rotate left */
+                    /* The code could be easier if we would just overwrite our parent left or right value.
+                     * But instead we move that data from our right to our self and use the right tree link to be placed in the tree as if it was ourself.
+                     */
+                    /* Letters are tree nodes, numbers are the data. This illustrates a rotate right.
+                     *
+                     *        A:4     |     A:2
+                     *       /   \    |    /   \
+                     *     B:2   C:5  |  D:1   B:4
+                     *    /   \       |       /   \
+                     *  D:1   E:3     |     E:3   C:5
+                     */
+                    /* Swap the data */
+                    swap_iter = iter->right;
+                    swap_entry = iter->entry;
+                    iter->entry = swap_iter->entry;
+                    swap_iter->entry = swap_entry;
+
+                    /* Reformat the tree links */
+                    iter->right = swap_iter->right;
+                    swap_iter->right = swap_iter->left;
+                    swap_iter->left = iter->left;
+                    iter->left = swap_iter;
+
+                    /* Fix the balance values
+                     *
+                     * if B > 0
+                     *   A = A - 1 - B
+                     * else
+                     *   A = A - 1
+                     *
+                     * diff = A - 1 - B
+                     * if diff < 0
+                     *   B = B - 1 + diff
+                     * else
+                     *   B = B - 1
+                     */
+                    swap_balance = swap_iter->balance;
+                    swap_iter->balance = iter->balance - 1;
+                    if ( 0 < swap_balance )
+                    {
+                        swap_iter->balance -= swap_balance;
+                    }
+                    iter->balance = iter->balance - 1 - swap_balance;
+                    if ( 0 < iter->balance )
+                    {
+                        iter->balance = 0;
+                    }
+                    iter->balance += swap_balance - 1;
+
+                    /* Saved depth by rotation so our parents depth also changes */
+                    if ( 0 >= swap_balance )
+                    {
+                        break;
+                    }
+                }
+                else if ( -1 > iter->balance )
+                {
+                    /* Rotate right */
+                    /* The code could be easier if we would just overwrite our parent left or right value.
+                     * But instead we move that data from our left to our self and use the left tree link to be placed in the tree as if it was ourself.
+                     */
+                    /* Swap the data */
+                    swap_iter = iter->left;
+                    swap_entry = iter->entry;
+                    iter->entry = swap_iter->entry;
+                    swap_iter->entry = swap_entry;
+
+                    /* Reformat the tree links */
+                    iter->left = swap_iter->left;
+                    swap_iter->left = swap_iter->right;
+                    swap_iter->right = iter->right;
+                    iter->right = swap_iter;
+
+                    /* Fix the balance values
+                     *
+                     * if B < 0
+                     *   A = A + 1 - B
+                     * else
+                     *   A = A + 1
+                     *
+                     * diff = A + 1 - B
+                     * if diff > 0
+                     *   B = B + 1 + diff
+                     * else
+                     *   B = B + 1
+                     */
+                    swap_balance = swap_iter->balance;
+                    swap_iter->balance = iter->balance + 1;
+                    if ( 0 > swap_balance )
+                    {
+                        swap_iter->balance -= swap_balance;
+                    }
+                    iter->balance = iter->balance + 1 - swap_balance;
+                    if ( 0 > iter->balance )
+                    {
+                        iter->balance = 0;
+                    }
+                    iter->balance += swap_balance + 1;
+
+                    /* Saved depth by rotation so our parents depth also changes */
+                    if ( 0 <= swap_balance )
+                    {
+                        break;
+                    }
+                }
+                else
+                {
+                    break;
+                }
+#else
+                break;
+#endif
+            }
+
+            /* Store ourself in new_entry for the check in the next parent */
+            del_entry = iter;
+        }
+    }
+
+    return list;
+}
+
+guint
+lsq_btree_length ( LSQBTree *list )
+{
+    guint size = 0;
+    LSQBTree *iter;
+    LSQBTree *stack[LSQ_BTREE_MAX_DEPTH];
+    guint stack_i = 0;
+
+    /* The tree is flattened */
+    if ( NULL == list || NULL != list->next )
+    {
+        for ( iter = list; NULL != iter; iter = iter->next)
+        {
+            size++;
+        }
+    }
+    else
+    {
+        do
+        {
+            for ( iter = list; NULL != iter; iter = iter->left )
+            {
+                stack[stack_i++] = iter;
+		g_return_val_if_fail(stack_i < LSQ_BTREE_MAX_DEPTH, 0);
+
+                size++;
+            }
+
+            list = stack[--stack_i]->right;
+        }
+        while ( 0 != stack_i || NULL != list );
+    }
+
+    return size;
+}
+
+void
+lsq_btree_free ( LSQBTree *list )
+{
+    LSQBTree *iter, *next;
+    LSQBTree *stack[LSQ_BTREE_MAX_DEPTH];
+    guint stack_i = 0;
+
+    /* The tree is flattened */
+    if ( NULL == list || NULL != list->next )
+    {
+        for ( iter = list; NULL != iter; iter = next)
+        {
+            next = iter->next;
+            g_free( iter );
+        }
+    }
+    else
+    {
+        do
+        {
+            for ( iter = list; NULL != iter; iter = iter->left )
+            {
+                stack[stack_i++] = iter;
+		g_return_if_fail(stack_i < LSQ_BTREE_MAX_DEPTH);
+            }
+
+            list = stack[--stack_i]->right;
+
+            g_free( stack[stack_i] );
+        }
+        while ( 0 != stack_i || NULL != list );
+    }
+}
+
+LSQBTree *
+lsq_btree_flatten ( LSQBTree *list )
+{
+    LSQBTree *iter, *next = list;
+    LSQBTree **prev = &list;
+    LSQBTree *stack[LSQ_BTREE_MAX_DEPTH];
+    guint stack_i = 0;
+
+    /* The tree is flattened */
+    if ( NULL == list || NULL != list->next )
+    {
+        return list;
+    }
+
+    do
+    {
+        for ( iter = next; NULL != iter; iter = iter->left )
+        {
+            stack[stack_i++] = iter;
+	    g_return_val_if_fail(stack_i < LSQ_BTREE_MAX_DEPTH, NULL);
+        }
+
+        next = stack[--stack_i];
+
+        *prev = next;
+        prev = &next->next;
+
+        next = next->right;
+    }
+    while ( 0 != stack_i || NULL != next );
+
+    return list;
+}
+
diff --git a/libsqueeze/slist.h b/libsqueeze/btree.h
similarity index 53%
rename from libsqueeze/slist.h
rename to libsqueeze/btree.h
index 4772afc..574a87b 100644
--- a/libsqueeze/slist.h
+++ b/libsqueeze/btree.h
@@ -14,39 +14,39 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-#ifndef __LSQ_SLIST_H__
-#define __LSQ_SLIST_H__
+#ifndef __LSQ_BTREE_H__
+#define __LSQ_BTREE_H__
 
-typedef struct _LSQSList LSQSList;
+typedef struct _LSQBTree LSQBTree;
 
-struct _LSQSList {
-	LSQArchiveEntry *entry;
-	LSQSList *next;
+struct _LSQBTree {
+    LSQArchiveEntry *entry;
+    LSQBTree *next;
+    LSQBTree *left;
+    LSQBTree *right;
+    gint balance;
 };
 
-LSQSList *
-lsq_slist_insert_sorted_single (
-        LSQSList *list,
+LSQBTree *
+lsq_btree_insert_sorted_single (
+        LSQBTree *list,
         LSQArchiveEntry *entry,
-        GCompareFunc) G_GNUC_INTERNAL;
+        GCompareFunc ) G_GNUC_INTERNAL;
+
+LSQBTree *
+lsq_btree_remove_sorted_single (
+        LSQBTree *list,
+        gchar *filename,
+        GCompareFunc cmp_func,
+        LSQArchiveEntry **found ) G_GNUC_INTERNAL;
 
 guint
-lsq_slist_length(LSQSList *list) G_GNUC_INTERNAL;
+lsq_btree_length ( LSQBTree *list ) G_GNUC_INTERNAL;
 
 void
-lsq_slist_free(LSQSList *list) G_GNUC_INTERNAL;
-
-typedef struct _LSQSIndexList LSQSIndexList;
-
-struct _LSQSIndexList {
-    LSQSList *index;
-    LSQSIndexList *next;
-};
-
-LSQSIndexList *
-lsq_slist_index_new(void);
+lsq_btree_free ( LSQBTree *list ) G_GNUC_INTERNAL;
 
-void
-lsq_slist_index_free(LSQSIndexList *list);
+LSQBTree *
+lsq_btree_flatten ( LSQBTree *list ) G_GNUC_INTERNAL;
 
-#endif /* __LSQ_SLIST_H__ */
+#endif /* __LSQ_BTREE_H__ */
diff --git a/libsqueeze/slist.c b/libsqueeze/slist.c
deleted file mode 100644
index b15cd8a..0000000
--- a/libsqueeze/slist.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- *  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 2 of the License, 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-#include <config.h>
-#include <string.h>
-#include <glib.h>
-#include <glib-object.h>
-#include <gio/gio.h>
-
-#include <libxfce4util/libxfce4util.h>
-
-#include "libsqueeze.h"
-#include "support-factory.h"
-#include "internals.h"
-#include "slist.h"
-
-LSQSList *
-lsq_slist_insert_sorted_single ( 
-        LSQSList *list,
-        LSQArchiveEntry *entry,
-        GCompareFunc cmp_func)
-{
-    gint cmp = 1;
-    LSQSList *iter = list;
-    LSQSList *prev_entry = NULL;
-    LSQSList *new_entry = NULL;
-
-    for(; iter; iter = iter->next)
-    {
-        /* archive can be NULL */
-        cmp = cmp_func(entry, (LSQArchiveEntry*)iter->entry);
-
-        if ( 0 == cmp )
-        {
-            g_critical("THIS SHOULD NOT HAPPEN!!! (the universe has just collapsed)");
-            return NULL;
-        }
-        if ( 0 > cmp )
-        {
-            break;
-        }
-
-        prev_entry = iter;
-    }
-
-    new_entry = g_new0(LSQSList, 1);
-    new_entry->next = iter;
-    new_entry->entry = entry;
-
-    if ( NULL == prev_entry )
-    {
-        return new_entry;
-    }
-    
-    prev_entry->next = new_entry;
-    return list;
-}
-
-guint
-lsq_slist_length ( LSQSList *list )
-{
-    guint size = 0;
-    for(; list; list = list->next)
-    {
-        size++;
-    }
-    return size;
-}
-
-void
-lsq_slist_free ( LSQSList *list )
-{
-    LSQSList *next;
-    for(; list; list = next)
-    {
-        next = list->next;
-        g_free(list);
-    }
-}
-
-LSQSIndexList *
-lsq_slist_index_new(void)
-{
-    return g_new0(LSQSIndexList, 1);
-}
-
-void
-lsq_slist_index_free ( LSQSIndexList *list )
-{
-    LSQSIndexList *next;
-    for(; list; list = next)
-    {
-        next = list->next;
-        g_free(list);
-    }
-}
-


More information about the Xfce4-commits mailing list