Skip to content

Commit

Permalink
lib: Red/Black balanced tree data structure
Browse files Browse the repository at this point in the history
A balanced tree implementation for Zephyr as we grow into bigger
regimes where simpler data structures aren't appropriate.

This implements an intrusive balanced tree that guarantees O(log2(N))
runtime for all operations and amortized O(1) behavior for creation
and destruction of whole trees.  The algorithms and naming are
conventional per existing academic and didactic implementations, c.f.:

https://en.wikipedia.org/wiki/Red%E2%80%93black_tree

The implementation is size-optimized to prioritize runtime memory
usage.  The data structure is intrusive, which is to say the struct
rbnode handle is intended to be placed in a separate struct the same
way other such structures (e.g. Zephyr's dlist list) and requires no
data pointer to be stored in the node.  The color bit is unioned with
a pointer (fairly common for such libraries).  Most notably, there is
no "parent" pointer stored in the node, the upper structure of the
tree being generated dynamically via a stack as the tree is recursed.
So the overall memory overhead of a node is just two pointers,
identical with a doubly-linked list.

Code size above dlist is about 2-2.5k on most architectures, which is
significant by Zephyr standards but probably still worthwhile in many
situations.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
  • Loading branch information
Andy Ross authored and Anas Nashif committed Apr 10, 2018
1 parent e2924ab commit 193f4fe
Show file tree
Hide file tree
Showing 4 changed files with 601 additions and 0 deletions.
100 changes: 100 additions & 0 deletions include/misc/rb.h
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/

/**
* @file
* @brief Red/Black balanced tree data structure
*
* This implements an intrusive balanced tree that guarantees
* O(log2(N)) runtime for all operations and amortized O(1) behavior
* for creation and destruction of whole trees. The algorithms and
* naming are conventional per existing academic and didactic
* implementations, c.f.:
*
* https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
*
* The implementation is size-optimized to prioritize runtime memory
* usage. The data structure is intrusive, which is to say the struct
* rbnode handle is intended to be placed in a separate struct the
* same way other such structures (e.g. Zephyr's dlist list) and
* requires no data pointer to be stored in the node. The color bit
* is unioned with a pointer (fairly common for such libraries). Most
* notably, there is no "parent" pointer stored in the node, the upper
* structure of the tree being generated dynamically via a stack as
* the tree is recursed. So the overall memory overhead of a node is
* just two pointers, identical with a doubly-linked list.
*/

#ifndef _RB_H
#define _RB_H

struct rbnode {
struct rbnode *children[2];
};

/**
* @typedef rb_lessthan_t
* @brief Red/black tree comparison predicate
*
* Compares the two nodes and returns 1 if node A is strictly less
* than B according to the tree's sorting criteria, 0 otherwise.
*/
typedef int (*rb_lessthan_t)(struct rbnode *a, struct rbnode *b);

struct rbtree {
struct rbnode *root;
rb_lessthan_t lessthan_fn;
int max_depth;
};

typedef void (*rb_visit_t)(struct rbnode *node, void *cookie);

struct rbnode *_rb_child(struct rbnode *node, int side);
int _rb_is_black(struct rbnode *node);
void _rb_walk(struct rbnode *node, rb_visit_t visit_fn, void *cookie);
struct rbnode *_rb_get_minmax(struct rbtree *tree, int side);

/**
* @brief Insert node into tree
*/
void rb_insert(struct rbtree *tree, struct rbnode *node);

/**
* @brief Remove node from tree
*/
void rb_remove(struct rbtree *tree, struct rbnode *node);

/**
* @brief Returns the lowest-sorted member of the tree
*/
static inline struct rbnode *rb_get_min(struct rbtree *tree)
{
return _rb_get_minmax(tree, 0);
}

/**
* @brief Returns the highest-sorted member of the tree
*/
static inline struct rbnode *rb_get_max(struct rbtree *tree)
{
return _rb_get_minmax(tree, 1);
}

/**
* @brief Walk/enumerate a rbtree
*
* Very simple recursive enumeration implementation. A rather more
* subtle (have to alloca() a stack to manage manually) iterative one
* is possible that uses a FOREACH-style macro API, but this is good
* enough for many purposes and much smaller.
*/
static inline void rb_walk(struct rbtree *tree, rb_visit_t visit_fn,
void *cookie)
{
_rb_walk(tree->root, visit_fn, cookie);
}

#endif /* _RB_H */
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Expand Up @@ -7,3 +7,4 @@ add_subdirectory_if_kconfig(ring_buffer)
add_subdirectory_if_kconfig(base64)
add_subdirectory(mempool)
add_subdirectory_ifdef(CONFIG_PTHREAD_IPC posix)
add_subdirectory(rbtree)
1 change: 1 addition & 0 deletions lib/rbtree/CMakeLists.txt
@@ -0,0 +1 @@
zephyr_sources(rb.c)

0 comments on commit 193f4fe

Please sign in to comment.