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_) */