moving a btree node is similar to simply using memmove() or memcpy(), with the added bonus of updating the pointers in the wider data structure to the new node memory and zeroing the old node memory so that it isn't used accidentally after the move is complete.
360 lines
14 KiB
C
360 lines
14 KiB
C
#ifndef BLUELIB_CORE_BTREE_H_
|
|
#define BLUELIB_CORE_BTREE_H_
|
|
|
|
#include <blue/core/iterator.h>
|
|
#include <blue/core/macros.h>
|
|
#include <blue/core/misc.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
B_DECLS_BEGIN;
|
|
|
|
#define B_BTREE_INIT {0}
|
|
#define B_TYPE_BTREE_ITERATOR (b_btree_iterator_get_type())
|
|
|
|
B_DECLARE_TYPE(b_btree_iterator);
|
|
|
|
B_TYPE_CLASS_DECLARATION_BEGIN(b_btree_iterator)
|
|
B_TYPE_CLASS_DECLARATION_END(b_btree_iterator)
|
|
|
|
/* defines a simple node insertion function.
|
|
this function assumes that your nodes have simple integer keys that can be
|
|
compared with the usual operators.
|
|
|
|
EXAMPLE:
|
|
if you have a tree node type like this:
|
|
|
|
struct my_tree_node {
|
|
int key;
|
|
b_btree_node base;
|
|
}
|
|
|
|
You would use the following call to generate an insert function for a tree
|
|
with this node type:
|
|
|
|
BTREE_DEFINE_SIMPLE_INSERT(
|
|
struct my_tree_node,
|
|
base,
|
|
key,
|
|
my_tree_node_insert);
|
|
|
|
Which would emit a function defined like:
|
|
|
|
static void my_tree_node_insert(b_btree *tree, struct my_tree_node *node);
|
|
|
|
@param node_type your custom tree node type. usually a structure that
|
|
contains a b_btree_node member.
|
|
@param container_node_member the name of the b_btree_node member variable
|
|
within your custom type.
|
|
@param container_key_member the name of the key member variable within your
|
|
custom type.
|
|
@param function_name the name of the function to generate.
|
|
*/
|
|
#define B_BTREE_DEFINE_SIMPLE_INSERT( \
|
|
node_type, container_node_member, container_key_member, function_name) \
|
|
void function_name(b_btree *tree, node_type *node) \
|
|
{ \
|
|
if (!tree->b_root) { \
|
|
tree->b_root = &node->container_node_member; \
|
|
b_btree_insert_fixup(tree, &node->container_node_member); \
|
|
return; \
|
|
} \
|
|
\
|
|
b_btree_node *cur = tree->b_root; \
|
|
while (1) { \
|
|
node_type *cur_node = b_unbox( \
|
|
node_type, cur, container_node_member); \
|
|
b_btree_node *next = NULL; \
|
|
\
|
|
if (node->container_key_member \
|
|
>= cur_node->container_key_member) { \
|
|
next = b_btree_right(cur); \
|
|
\
|
|
if (!next) { \
|
|
b_btree_put_right( \
|
|
cur, \
|
|
&node->container_node_member); \
|
|
break; \
|
|
} \
|
|
} else if ( \
|
|
node->container_key_member \
|
|
< cur_node->container_key_member) { \
|
|
next = b_btree_left(cur); \
|
|
\
|
|
if (!next) { \
|
|
b_btree_put_left( \
|
|
cur, \
|
|
&node->container_node_member); \
|
|
break; \
|
|
} \
|
|
} \
|
|
\
|
|
cur = next; \
|
|
} \
|
|
\
|
|
b_btree_insert_fixup(tree, &node->container_node_member); \
|
|
}
|
|
|
|
/* defines a node insertion function.
|
|
this function should be used for trees with complex node keys that cannot be
|
|
directly compared. a comparator for your keys must be supplied.
|
|
|
|
EXAMPLE:
|
|
if you have a tree node type like this:
|
|
|
|
struct my_tree_node {
|
|
complex_key_t key;
|
|
b_btree_node base;
|
|
}
|
|
|
|
You would need to define a comparator function or macro with the following
|
|
signature:
|
|
|
|
int my_comparator(struct my_tree_node *a, struct my_tree_node *b);
|
|
|
|
Which implements the following:
|
|
|
|
return -1 if a < b
|
|
return 0 if a == b
|
|
return 1 if a > b
|
|
|
|
You would use the following call to generate an insert function for a tree
|
|
with this node type:
|
|
|
|
BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert,
|
|
my_comparator);
|
|
|
|
Which would emit a function defined like:
|
|
|
|
static void my_tree_node_insert(b_btree *tree, struct my_tree_node *node);
|
|
|
|
@param node_type your custom tree node type. usually a structure that
|
|
contains a b_btree_node member.
|
|
@param container_node_member the name of the b_btree_node member variable
|
|
within your custom type.
|
|
@param container_key_member the name of the key member variable within your
|
|
custom type.
|
|
@param function_name the name of the function to generate.
|
|
@param comparator the name of a comparator function or functional-macro that
|
|
conforms to the requirements listed above.
|
|
*/
|
|
#define B_BTREE_DEFINE_INSERT( \
|
|
node_type, container_node_member, container_key_member, function_name, \
|
|
comparator) \
|
|
void function_name(b_btree *tree, node_type *node) \
|
|
{ \
|
|
if (!tree->b_root) { \
|
|
tree->b_root = &node->container_node_member; \
|
|
b_btree_insert_fixup(tree, &node->container_node_member); \
|
|
return; \
|
|
} \
|
|
\
|
|
b_btree_node *cur = tree->b_root; \
|
|
while (1) { \
|
|
node_type *cur_node = b_unbox( \
|
|
node_type, cur, container_node_member); \
|
|
b_btree_node *next = NULL; \
|
|
int cmp = comparator(node, cur_node); \
|
|
\
|
|
if (cmp >= 0) { \
|
|
next = b_btree_right(cur); \
|
|
\
|
|
if (!next) { \
|
|
b_btree_put_right( \
|
|
cur, \
|
|
&node->container_node_member); \
|
|
break; \
|
|
} \
|
|
} else if (cmp < 0) { \
|
|
next = b_btree_left(cur); \
|
|
\
|
|
if (!next) { \
|
|
b_btree_put_left( \
|
|
cur, \
|
|
&node->container_node_member); \
|
|
break; \
|
|
} \
|
|
} else { \
|
|
return; \
|
|
} \
|
|
\
|
|
cur = next; \
|
|
} \
|
|
\
|
|
b_btree_insert_fixup(tree, &node->container_node_member); \
|
|
}
|
|
|
|
/* defines a simple tree search function.
|
|
this function assumes that your nodes have simple integer keys that can be
|
|
compared with the usual operators.
|
|
|
|
EXAMPLE:
|
|
if you have a tree node type like this:
|
|
|
|
struct my_tree_node {
|
|
int key;
|
|
b_btree_node base;
|
|
}
|
|
|
|
You would use the following call to generate a search function for a tree
|
|
with this node type:
|
|
|
|
BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key,
|
|
my_tree_node_get);
|
|
|
|
Which would emit a function defined like:
|
|
|
|
static struct my_tree_node *my_tree_node_get(b_btree *tree, int key);
|
|
|
|
@param node_type your custom tree node type. usually a structure that
|
|
contains a b_btree_node member.
|
|
@param key_type the type name of the key embedded in your custom tree node
|
|
type. this type must be compatible with the builtin comparison operators.
|
|
@param container_node_member the name of the b_btree_node member variable
|
|
within your custom type.
|
|
@param container_key_member the name of the key member variable within your
|
|
custom type.
|
|
@param function_name the name of the function to generate.
|
|
*/
|
|
#define B_BTREE_DEFINE_SIMPLE_GET( \
|
|
node_type, key_type, container_node_member, container_key_member, \
|
|
function_name) \
|
|
node_type *function_name(const b_btree *tree, key_type key) \
|
|
{ \
|
|
b_btree_node *cur = tree->b_root; \
|
|
while (cur) { \
|
|
node_type *cur_node = b_unbox( \
|
|
node_type, cur, container_node_member); \
|
|
if (key > cur_node->container_key_member) { \
|
|
cur = b_btree_right(cur); \
|
|
} else if (key < cur_node->container_key_member) { \
|
|
cur = b_btree_left(cur); \
|
|
} else { \
|
|
return cur_node; \
|
|
} \
|
|
} \
|
|
\
|
|
return NULL; \
|
|
}
|
|
|
|
#define b_btree_foreach(it, btree) \
|
|
for (int z__b_unique_name() = b_btree_iterator_begin(btree, it); \
|
|
(it)->node != NULL; b_btree_iterator_next(it))
|
|
|
|
/* binary tree nodes. this *cannot* be used directly. you need to define a
|
|
custom node type that contains a member variable of type b_btree_node.
|
|
|
|
you would then use the supplied macros to define functions to manipulate your
|
|
custom binary tree.
|
|
*/
|
|
typedef struct b_btree_node {
|
|
struct b_btree_node *b_parent, *b_left, *b_right;
|
|
unsigned short b_height;
|
|
} b_btree_node;
|
|
|
|
/* binary tree. unlike b_btree_node, you can define variables of type b_btree.
|
|
*/
|
|
typedef struct b_btree {
|
|
b_btree_node *b_root;
|
|
} b_btree;
|
|
|
|
BLUE_API b_type b_btree_iterator_get_type(void);
|
|
|
|
/* re-balance a binary tree after an insertion operation.
|
|
|
|
NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or
|
|
similar, this function will automatically called for you.
|
|
|
|
@param tree the tree to re-balance.
|
|
@param node the node that was just inserted into the tree.
|
|
*/
|
|
BLUE_API void b_btree_insert_fixup(b_btree *tree, b_btree_node *node);
|
|
|
|
/* delete a node from a binary tree and re-balance the tree afterwards.
|
|
|
|
@param tree the tree to delete from
|
|
@param node the node to delete.
|
|
*/
|
|
BLUE_API void b_btree_delete(b_btree *tree, b_btree_node *node);
|
|
|
|
/* get the first node in a binary tree.
|
|
|
|
this will be the node with the smallest key (i.e. the node that is
|
|
furthest-left from the root)
|
|
*/
|
|
BLUE_API b_btree_node *b_btree_first(const b_btree *tree);
|
|
|
|
/* get the last node in a binary tree.
|
|
|
|
this will be the node with the largest key (i.e. the node that is
|
|
furthest-right from the root)
|
|
*/
|
|
BLUE_API b_btree_node *b_btree_last(const b_btree *tree);
|
|
/* for any binary tree node, this function returns the node with the
|
|
* next-largest key value */
|
|
BLUE_API b_btree_node *b_btree_next(const b_btree_node *node);
|
|
/* for any binary tree node, this function returns the node with the
|
|
* next-smallest key value */
|
|
BLUE_API b_btree_node *b_btree_prev(const b_btree_node *node);
|
|
/* return true if the btree is empty, false otherwise */
|
|
static inline bool b_btree_empty(const b_btree *tree)
|
|
{
|
|
return tree->b_root == NULL;
|
|
}
|
|
|
|
/* sets `child` as the immediate left-child of `parent` */
|
|
static inline void b_btree_put_left(b_btree_node *parent, b_btree_node *child)
|
|
{
|
|
parent->b_left = child;
|
|
child->b_parent = parent;
|
|
}
|
|
|
|
/* sets `child` as the immediate right-child of `parent` */
|
|
static inline void b_btree_put_right(b_btree_node *parent, b_btree_node *child)
|
|
{
|
|
parent->b_right = child;
|
|
child->b_parent = parent;
|
|
}
|
|
|
|
/* get the immediate left-child of `node` */
|
|
static inline b_btree_node *b_btree_left(b_btree_node *node)
|
|
{
|
|
return node->b_left;
|
|
}
|
|
|
|
/* get the immediate right-child of `node` */
|
|
static inline b_btree_node *b_btree_right(b_btree_node *node)
|
|
{
|
|
return node->b_right;
|
|
}
|
|
|
|
/* get the immediate parent of `node` */
|
|
static inline b_btree_node *b_btree_parent(b_btree_node *node)
|
|
{
|
|
return node->b_parent;
|
|
}
|
|
|
|
BLUE_API void b_btree_move(b_btree *tree, b_btree_node *dest, b_btree_node *src);
|
|
|
|
/* get the height of `node`.
|
|
|
|
the height of a node is defined as the length of the longest path
|
|
between the node and a leaf node.
|
|
|
|
this count includes the node itself, so the height of a leaf node will be 1.
|
|
*/
|
|
static inline unsigned short b_btree_height(b_btree_node *node)
|
|
{
|
|
return node->b_height;
|
|
}
|
|
|
|
BLUE_API b_iterator *b_btree_begin(b_btree *tree);
|
|
BLUE_API const b_iterator *b_btree_cbegin(const b_btree *tree);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif
|