summary refs log tree commit diff stats
path: root/src/common/tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/tree.c')
-rw-r--r--src/common/tree.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/src/common/tree.c b/src/common/tree.c
new file mode 100644
index 00000000..1627bd98
--- /dev/null
+++ b/src/common/tree.c
@@ -0,0 +1,215 @@
+/*
+This is used for quick userlist insertion and lookup. It's not really
+a tree, but it could be :)
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "tree.h"
+
+#define ARRAY_GROW 32
+
+struct _tree
+{
+	int elements;
+	int array_size;
+	void **array;
+	tree_cmp_func *cmp;
+	void *data;
+};
+
+tree *
+tree_new (tree_cmp_func *cmp, void *data)
+{
+	tree *t = calloc (1, sizeof (tree));
+	t->cmp = cmp;
+	t->data = data;
+	return t;
+}
+
+void
+tree_destroy (tree *t)
+{
+	if (t)
+	{
+		if (t->array)
+			free (t->array);
+		free (t);
+	}
+}
+
+static int
+tree_find_insertion_pos (tree *t, void *key, int *done)
+{
+	int c, u, l, idx;
+
+	if (t->elements < 1)
+	{
+		*done = 1;
+		t->array[0] = key;
+		t->elements++;
+		return 0;
+	}
+
+	if (t->elements < 2)
+	{
+		*done = 1;
+		c = t->cmp (key, t->array[0], t->data);
+		if (c == 0)
+			return -1;
+		t->elements++;
+		if (c > 0)
+		{
+			t->array[1] = key;
+			return 1;
+		}
+		t->array[1] = t->array[0];
+		t->array[0] = key;
+		return 0;
+	}
+
+	*done = 0;
+
+	c = t->cmp (key, t->array[0], t->data);
+	if (c < 0)
+		return 0;	/* prepend */
+
+	c = t->cmp (key, t->array[t->elements - 1], t->data);
+	if (c > 0)
+		return t->elements;	/* append */
+
+	l = 0;
+	u = t->elements - 1;
+	while (1)
+	{
+		idx = (l + u) / 2;
+		c = t->cmp (key, t->array[idx], t->data);
+
+		if (0 > c)
+			u = idx;
+		else if (0 < c && 0 > t->cmp (key, t->array[idx+1], t->data))
+			return idx + 1;
+		else if (c == 0)
+			return -1;
+		else
+			l = idx + 1;
+	}
+}
+
+static void
+tree_insert_at_pos (tree *t, void *key, int pos)
+{
+	int post_bytes;
+
+	/* append is easy */
+	if (pos != t->elements)
+	{
+		post_bytes = (t->elements - pos) * sizeof (void *);
+		memmove (&t->array[pos + 1], &t->array[pos], post_bytes);
+	}
+
+	t->array[pos] = key;
+	t->elements++;
+}
+
+static void *
+mybsearch (const void *key, void **array, size_t nmemb,
+			  int (*compar) (const void *, const void *, void *data), void *data, int *pos)
+{
+	int l, u, idx;
+	int comparison;
+
+	l = 0;
+	u = nmemb;
+	while (l < u)
+	{
+		idx = (l + u) / 2;
+		comparison = (*compar) (key, array[idx], data);
+		if (comparison < 0)
+			u = idx;
+		else if (comparison > 0)
+			l = idx + 1;
+		else
+		{
+			*pos = idx;
+			return array[idx];
+		}
+	}
+
+	return NULL;
+}
+
+void *
+tree_find (tree *t, void *key, tree_cmp_func *cmp, void *data, int *pos)
+{
+	if (!t || !t->array)
+		return NULL;
+
+	return mybsearch (key, &t->array[0], t->elements, cmp, data, pos);
+}
+
+static void
+tree_remove_at_pos (tree *t, int pos)
+{
+	int post_bytes;
+
+	t->elements--;
+	if (pos != t->elements)
+	{
+		post_bytes = (t->elements - pos) * sizeof (void *);
+		memmove (&t->array[pos], &t->array[pos + 1], post_bytes);
+	}
+}
+
+int
+tree_remove (tree *t, void *key, int *pos)
+{
+	void *data;
+
+	data = tree_find (t, key, t->cmp, t->data, pos);
+	if (!data)
+		return 0;
+
+	tree_remove_at_pos (t, *pos);
+	return 1;
+}
+
+void
+tree_foreach (tree *t, tree_traverse_func *func, void *data)
+{
+	int j;
+
+	if (!t || !t->array)
+		return;
+
+	for (j = 0; j < t->elements; j++)
+	{
+		if (!func (t->array[j], data))
+			break;
+	}
+}
+
+int
+tree_insert (tree *t, void *key)
+{
+	int pos, done;
+
+	if (!t)
+		return -1;
+
+	if (t->array_size < t->elements + 1)
+	{
+		int new_size = t->array_size + ARRAY_GROW;
+
+		t->array = realloc (t->array, sizeof (void *) * new_size);
+		t->array_size = new_size;
+	}
+
+	pos = tree_find_insertion_pos (t, key, &done);
+	if (!done && pos != -1)
+		tree_insert_at_pos (t, key, pos);
+
+	return pos;
+}