Module Name:    src
Committed By:   yamt
Date:           Tue May 19 12:39:56 UTC 2009

Added Files:
        src/common/lib/libc/gen: rpst.c
        src/sys/sys: rpst.h

Log Message:
radix priority search tree.


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/common/lib/libc/gen/rpst.c
cvs rdiff -u -r0 -r1.1 src/sys/sys/rpst.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Added files:

Index: src/common/lib/libc/gen/rpst.c
diff -u /dev/null src/common/lib/libc/gen/rpst.c:1.1
--- /dev/null	Tue May 19 12:39:56 2009
+++ src/common/lib/libc/gen/rpst.c	Tue May 19 12:39:56 2009
@@ -0,0 +1,508 @@
+/*	$NetBSD: rpst.c,v 1.1 2009/05/19 12:39:56 yamt Exp $	*/
+
+/*-
+ * Copyright (c)2009 YAMAMOTO Takashi,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * radix priority search tree
+ *
+ * described in:
+ *	SIAM J. COMPUT.
+ *	Vol. 14, No. 2, May 1985
+ *	PRIORITY SEARCH TREES
+ *	EDWARD M. McCREIGHT
+ *
+ * ideas from linux:
+ *	- grow tree height on-demand.
+ *	- allow duplicated X values.  in that case, we act as a heap.
+ */
+
+#include <sys/cdefs.h>
+
+#if defined(_KERNEL)
+__KERNEL_RCSID(0, "$NetBSD: rpst.c,v 1.1 2009/05/19 12:39:56 yamt Exp $");
+#include <sys/param.h>
+#else /* defined(_KERNEL) */
+__RCSID("$NetBSD: rpst.c,v 1.1 2009/05/19 12:39:56 yamt Exp $");
+#include <assert.h>
+#include <stdbool.h>
+#include <string.h>
+#define	KASSERT	assert
+#endif /* defined(_KERNEL) */
+
+#include <sys/rpst.h>
+
+/*
+ * rpst_init_tree: initialize a tree.
+ */
+
+void
+rpst_init_tree(struct rpst_tree *t)
+{
+
+	t->t_root = NULL;
+	t->t_height = 0;
+}
+
+/*
+ * rpst_height2max: calculate the maximum index which can be handled by
+ * a tree with the given height.
+ *
+ * 0  ... 0x0000000000000001
+ * 1  ... 0x0000000000000003
+ * 2  ... 0x0000000000000007
+ * 3  ... 0x000000000000000f
+ *
+ * 31 ... 0x00000000ffffffff
+ *
+ * 63 ... 0xffffffffffffffff
+ */
+
+static uint64_t
+rpst_height2max(unsigned int height)
+{
+
+	KASSERT(height < 64);
+	if (height == 63) {
+		return UINT64_MAX;
+	}
+	return (UINT64_C(1) << (height + 1)) - 1;
+}
+
+/*
+ * rpst_startmask: calculate the mask for the start of a search.
+ * (ie. the mask for the top-most bit)
+ */
+
+static uint64_t
+rpst_startmask(const struct rpst_tree *t)
+{
+
+	return UINT64_C(1) << t->t_height;
+}
+
+/*
+ * rpst_enlarge_tree: enlarge tree so that 'index' can be stored
+ */
+
+static void
+rpst_enlarge_tree(struct rpst_tree *t, uint64_t idx)
+{
+
+	while (idx > rpst_height2max(t->t_height)) {
+		struct rpst_node *n = t->t_root;
+
+		if (n != NULL) {
+			rpst_remove_node(t, n);
+			memset(&n->n_children, 0, sizeof(n->n_children));
+			n->n_children[0] = t->t_root;
+			t->t_root = n;
+		}
+		t->t_height++;
+	}
+}
+
+/*
+ * rpst_insert_node1: a helper for rpst_insert_node.
+ */
+
+static struct rpst_node *
+rpst_insert_node1(struct rpst_node **where, struct rpst_node *n, uint64_t mask)
+{
+	struct rpst_node *cur;
+	unsigned int idx;
+
+	KASSERT((n->n_x & ((-mask) << 1)) == 0);
+next:
+	cur = *where;
+	if (cur == NULL) {
+		memset(&n->n_children, 0, sizeof(n->n_children));
+		*where = n;
+		return NULL;
+	}
+	if (n->n_y == cur->n_y && n->n_x != cur->n_x) {
+		return cur;
+	}
+	if (n->n_y < cur->n_y) {
+		/* swap cur and n */
+		memcpy(n->n_children, cur->n_children, sizeof(n->n_children));
+		*where = n;
+		n = cur;
+		cur = *where;
+	}
+	KASSERT(*where == cur);
+	idx = (n->n_x & mask) != 0;
+	where = &cur->n_children[idx];
+	mask >>= 1;
+	goto next;
+}
+
+/*
+ * rpst_insert_node: insert a node into the tree.
+ *
+ * => return NULL on success.
+ * => if a duplicated node (a node with the same X,Y pair as ours) is found,
+ *    return the node.  in that case, the tree is intact.
+ */
+
+struct rpst_node *
+rpst_insert_node(struct rpst_tree *t, struct rpst_node *n)
+{
+
+	rpst_enlarge_tree(t, n->n_x);
+	return rpst_insert_node1(&t->t_root, n, rpst_startmask(t));
+}
+
+/*
+ * rpst_find_pptr: find a pointer to the given node.
+ *
+ * also, return the parent node via parentp.  (NULL for the root node.)
+ *
+ * XXX is it better to simply make each nodes have a pointer to parent?
+ */
+
+static struct rpst_node **
+rpst_find_pptr(struct rpst_node **where, struct rpst_node *n, uint64_t mask,
+    struct rpst_node **parentp)
+{
+	struct rpst_node *pn = NULL;
+	struct rpst_node *cur;
+	unsigned int idx;
+
+next:
+	cur = *where;
+	KASSERT(cur != NULL);
+	if (cur == n) {
+		*parentp = pn;
+		return where;
+	}
+	idx = (n->n_x & mask) != 0;
+	pn = cur;
+	where = &cur->n_children[idx];
+	mask >>= 1;
+	goto next;
+}
+
+/*
+ * rpst_remove_node_at: remove a node at *where.
+ */
+
+static void
+rpst_remove_node_at(struct rpst_node **where)
+{
+	struct rpst_node *tmp[2];
+	struct rpst_node *cur;
+	struct rpst_node *selected;
+	unsigned int selected_idx;
+	unsigned int i;
+
+	cur = *where;
+	KASSERT(cur != NULL);
+next:
+	selected = NULL;
+	for (i = 0; i < 2; i++) {
+		struct rpst_node *c;
+
+		c = cur->n_children[i];
+		if (selected == NULL || (c != NULL && c->n_y < selected->n_y)) {
+			selected = c;
+			selected_idx = i;
+		}
+	}
+	*where = selected;
+	if (selected == NULL) {
+		return;
+	}
+	/*
+	 * swap selected->n_children and cur->n_children.
+	 */
+	memcpy(tmp, selected->n_children, sizeof(tmp));
+	memcpy(selected->n_children, cur->n_children, sizeof(tmp));
+	memcpy(cur->n_children, tmp, sizeof(tmp));
+	where = &selected->n_children[selected_idx];
+	goto next;
+}
+
+/*
+ * rpst_remove_node: remove a node from the tree.
+ */
+
+void
+rpst_remove_node(struct rpst_tree *t, struct rpst_node *n)
+{
+	struct rpst_node *parent;
+	struct rpst_node **where;
+
+	where = rpst_find_pptr(&t->t_root, n, rpst_startmask(t), &parent);
+	KASSERT(*where == n);
+	rpst_remove_node_at(where);
+}
+
+static bool __unused
+rpst_iterator_match_p(const struct rpst_node *n, const struct rpst_iterator *it)
+{
+
+	if (n->n_y > it->it_max_y) {
+		return false;
+	}
+	if (n->n_x < it->it_min_x) {
+		return false;
+	}
+	if (n->n_x > it->it_max_x) {
+		return false;
+	}
+	return true;
+}
+
+static uint64_t
+rpst_level2mask(const struct rpst_tree *t, unsigned int level)
+{
+
+	if (t->t_height < level) {
+		return 0;
+	}
+	return UINT64_C(1) << (t->t_height - level);
+}
+
+struct rpst_node *
+rpst_iterate_first(struct rpst_tree *t, uint64_t max_y, uint64_t min_x,
+    uint64_t max_x, struct rpst_iterator *it)
+{
+	struct rpst_node *n;
+
+	KASSERT(min_x <= max_x);
+	n = t->t_root;
+	if (n == NULL) {
+		return NULL;
+	}
+	it->it_tree = t;
+	it->it_cur = n;
+	it->it_idx = 0;
+	it->it_level = 0;
+	it->it_max_y = max_y;
+	it->it_min_x = min_x;
+	it->it_max_x = max_x;
+	return rpst_iterate_next(it);
+}
+
+struct rpst_node *
+rpst_iterate_next(struct rpst_iterator *it)
+{
+	struct rpst_tree *t;
+	struct rpst_node *n;
+	struct rpst_node *next;
+	const uint64_t max_y = it->it_max_y;
+	const uint64_t min_x = it->it_min_x;
+	const uint64_t max_x = it->it_max_x;
+	unsigned int idx;
+	unsigned int maxidx;
+	unsigned int level;
+	uint64_t mask;
+
+	t = it->it_tree;
+	n = it->it_cur;
+	idx = it->it_idx;
+	level = it->it_level;
+	mask = rpst_level2mask(t, level);
+	maxidx = (max_x & mask) != 0;
+	KASSERT(n == t->t_root || rpst_iterator_match_p(n, it));
+next:
+	KASSERT(mask == rpst_level2mask(t, level));
+	KASSERT(maxidx == ((max_x & mask) != 0));
+	KASSERT(idx <= maxidx + 2);
+	KASSERT(n != NULL);
+#if 0
+	printf("%s: cur=%p, idx=%u maxidx=%u level=%u mask=%" PRIx64 "\n",
+	    __func__, (void *)n, idx, maxidx, level, mask);
+#endif
+	if (idx == maxidx + 1) { /* visit the current node */
+		idx++;
+		if (min_x <= n->n_x && n->n_x <= max_x) {
+			it->it_tree = t;
+			it->it_cur = n;
+			it->it_idx = idx;
+			it->it_level = level;
+			KASSERT(rpst_iterator_match_p(n, it));
+			return n; /* report */
+		}
+		goto next;
+	} else if (idx == maxidx + 2) { /* back to the parent */
+		struct rpst_node **where;
+
+		where = rpst_find_pptr(&t->t_root, n, rpst_startmask(t), &next);
+		if (next == NULL) {
+			KASSERT(level == 0);
+			KASSERT(t->t_root == n);
+			KASSERT(&t->t_root == where);
+			return NULL; /* done */
+		}
+		KASSERT(level > 0);
+		level--;
+		mask = rpst_level2mask(t, level);
+		maxidx = (max_x & mask) != 0;
+		n = next;
+		idx = where - n->n_children + 1;
+		KASSERT(idx < 2 + 1);
+		goto next;
+	}
+	/* go to a child */
+	KASSERT(idx < 2);
+	next = n->n_children[idx];
+	if (next == NULL || next->n_y > max_y) {
+		idx++;
+		goto next;
+	}
+	level++;
+	mask >>= 1;
+	maxidx = (max_x & mask) != 0;
+	n = next;
+	idx = (min_x & mask) != 0;
+	goto next;
+}
+
+#if defined(UNITTEST)
+#include <sys/time.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void
+rpst_dump_node(const struct rpst_node *n, unsigned int depth)
+{
+	unsigned int i;
+
+	for (i = 0; i < depth; i++) {
+		printf("  ");
+	}
+	printf("[%u]", depth);
+	if (n == NULL) {
+		printf("NULL\n");
+		return;
+	}
+	printf("%p x=%" PRIx64 "(%" PRIu64 ") y=%" PRIx64 "(%" PRIu64 ")\n",
+	    (const void *)n, n->n_x, n->n_x, n->n_y, n->n_y);
+	for (i = 0; i < 2; i++) {
+		rpst_dump_node(n->n_children[i], depth + 1);
+	}
+}
+
+static void
+rpst_dump_tree(const struct rpst_tree *t)
+{
+
+	printf("pst %p bits=%u\n", (const void *)t, t->t_height);
+	rpst_dump_node(t->t_root, 0);
+}
+
+struct testnode {
+	struct rpst_node n;
+	struct testnode *next;
+};
+
+int
+main(int argc, char *argv[])
+{
+	struct rpst_tree t;
+	struct testnode *h = NULL;
+	struct testnode *n;
+	struct rpst_node *rn;
+	unsigned int i = 500000;
+	struct rpst_iterator it;
+	struct timeval start;
+	struct timeval end;
+	unsigned int done;
+
+	rpst_init_tree(&t);
+	rpst_dump_tree(&t);
+	assert(NULL == rpst_iterate_first(&t, UINT64_MAX, 0, UINT64_MAX, &it));
+
+	done = 0;
+	gettimeofday(&start, NULL);
+	while (i > 0) {
+		n = malloc(sizeof(*n));
+		if (i > 499000) {
+			n->n.n_x = 10;
+			n->n.n_y = random();
+		} else if (i > 400000) {
+			n->n.n_x = i;
+			n->n.n_y = random();
+		} else {
+			n->n.n_x = random();
+			n->n.n_y = random();
+		}
+#if 0
+		printf("[%u] insert %p x=%" PRIu64 " y=%" PRIu64 "\n",
+		    i, n, n->n.n_x, n->n.n_y);
+#endif
+		rpst_insert_node(&t, &n->n);
+		n->next = h;
+		h = n;
+		i--;
+		done++;
+	}
+	gettimeofday(&end, NULL);
+	printf("%u nodes inserted in %ju usecs\n", done,
+	    (uintmax_t)end.tv_sec * 1000000 + end.tv_usec -
+	    start.tv_sec * 1000000 - start.tv_usec);
+
+	done = 0;
+	gettimeofday(&start, NULL);
+	for (rn = rpst_iterate_first(&t, 100000000, 10, 1000000, &it);
+	    rn != NULL;
+	    rn = rpst_iterate_next(&it)) {
+		done++;
+#if 0
+		printf("found %p x=%" PRIu64 " y=%" PRIu64 "\n",
+		    (void *)rn, rn->n_x, rn->n_y);
+#endif
+	}
+	gettimeofday(&end, NULL);
+	printf("%u nodes found in %ju usecs\n", done,
+	    (uintmax_t)end.tv_sec * 1000000 + end.tv_usec -
+	    start.tv_sec * 1000000 - start.tv_usec);
+
+	done = 0;
+	gettimeofday(&start, NULL);
+	while (h != NULL) {
+		n = h;
+		h = n->next;
+#if 0
+		printf("[%u] remove %p x=%" PRIu64 " y=%" PRIu64 "\n",
+		    n, n->n.n_x, n->n.n_y);
+#endif
+		rpst_remove_node(&t, &n->n);
+		i++;
+		done++;
+	}
+	gettimeofday(&end, NULL);
+	printf("%u nodes removed in %ju usecs\n", done,
+	    (uintmax_t)end.tv_sec * 1000000 + end.tv_usec -
+	    start.tv_sec * 1000000 - start.tv_usec);
+
+	rpst_dump_tree(&t);
+}
+#endif /* defined(UNITTEST) */

Index: src/sys/sys/rpst.h
diff -u /dev/null src/sys/sys/rpst.h:1.1
--- /dev/null	Tue May 19 12:39:56 2009
+++ src/sys/sys/rpst.h	Tue May 19 12:39:56 2009
@@ -0,0 +1,66 @@
+/*	$NetBSD: rpst.h,v 1.1 2009/05/19 12:39:56 yamt Exp $	*/
+
+/*-
+ * Copyright (c)2009 YAMAMOTO Takashi,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if !defined(_SYS_RPST_H_)
+#define	_SYS_RPST_H_
+
+#if defined(_KERNEL)
+#include <sys/types.h>
+#else /* defined(_KERNEL) */
+#include <stdint.h>
+#endif /* defined(_KERNEL) */
+
+struct rpst_tree {
+	struct rpst_node *t_root;
+	unsigned int t_height;
+};
+
+struct rpst_node {
+	struct rpst_node *n_children[2];
+	uint64_t n_y;
+	uint64_t n_x;
+};
+
+struct rpst_iterator {
+	struct rpst_tree *it_tree;
+	struct rpst_node *it_cur;
+	unsigned int it_idx;
+	unsigned int it_level;
+	uint64_t it_max_y;
+	uint64_t it_min_x;
+	uint64_t it_max_x;
+};
+
+void rpst_init_tree(struct rpst_tree *);
+struct rpst_node *rpst_insert_node(struct rpst_tree *, struct rpst_node *);
+void rpst_remove_node(struct rpst_tree *, struct rpst_node *);
+struct rpst_node *rpst_iterate_first(struct rpst_tree *, uint64_t, uint64_t,
+    uint64_t, struct rpst_iterator *);
+struct rpst_node *rpst_iterate_next(struct rpst_iterator *);
+
+#endif /* !defined(_SYS_RPST_H_) */

Reply via email to