initial commit
This commit is contained in:
3
core/CMakeLists.txt
Normal file
3
core/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
include(../cmake/Templates.cmake)
|
||||
|
||||
add_bluelib_module(core)
|
||||
821
core/btree.c
Normal file
821
core/btree.c
Normal file
@@ -0,0 +1,821 @@
|
||||
/*
|
||||
The Clear BSD License
|
||||
|
||||
Copyright (c) 2023 Max Wash
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted (subject to the limitations in the disclaimer
|
||||
below) provided that the following conditions are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
- 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.
|
||||
|
||||
- Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
*/
|
||||
|
||||
/* templated AVL binary tree implementation
|
||||
|
||||
this file implements an extensible AVL binary tree data structure.
|
||||
|
||||
the primary rule of an AVL binary tree is that for a given node N,
|
||||
the heights of N's left and right subtrees can differ by at most 1.
|
||||
|
||||
the height of a subtree is the length of the longest path between
|
||||
the root of the subtree and a leaf node, including the root node itself.
|
||||
|
||||
the height of a leaf node is 1.
|
||||
|
||||
when a node is inserted into or deleted from the tree, this rule may
|
||||
be broken, in which the tree must be rotated to restore the balance.
|
||||
|
||||
no more than one rotation is required for any insert operations,
|
||||
while multiple rotations may be required for a delete operation.
|
||||
|
||||
there are four types of rotations that can be applied to a tree:
|
||||
- left rotation
|
||||
- right rotation
|
||||
- double left rotations
|
||||
- double right rotations
|
||||
|
||||
by enforcing the balance rule, for a tree with n nodes, the worst-case
|
||||
performance for insert, delete, and search operations is guaranteed
|
||||
to be O(log n).
|
||||
|
||||
this file intentionally excludes any kind of search function implementation.
|
||||
it is up to the programmer to implement their own tree node type
|
||||
using b_btree_node, and their own search function using b_btree.
|
||||
this allows the programmer to define their own node types with complex
|
||||
non-integer key types. btree.h contains a number of macros to help
|
||||
define these functions. the macros do all the work, you just have to
|
||||
provide a comparator function.
|
||||
*/
|
||||
|
||||
#include <blue/core/btree.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define IS_LEFT_CHILD(p, c) ((p) && (c) && ((p)->b_left == (c)))
|
||||
#define IS_RIGHT_CHILD(p, c) ((p) && (c) && ((p)->b_right == (c)))
|
||||
|
||||
#define HAS_LEFT_CHILD(x) ((x) && ((x)->b_left))
|
||||
#define HAS_RIGHT_CHILD(x) ((x) && ((x)->b_right))
|
||||
|
||||
#define HAS_NO_CHILDREN(x) ((x) && (!(x)->b_left) && (!(x)->b_right))
|
||||
#define HAS_ONE_CHILD(x) \
|
||||
((HAS_LEFT_CHILD(x) && !HAS_RIGHT_CHILD(x)) \
|
||||
|| (!HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x)))
|
||||
#define HAS_TWO_CHILDREN(x) (HAS_LEFT_CHILD(x) && HAS_RIGHT_CHILD(x))
|
||||
|
||||
#define HEIGHT(x) ((x) ? (x)->b_height : 0)
|
||||
|
||||
static inline void update_height(struct b_btree_node *x)
|
||||
{
|
||||
x->b_height = MAX(HEIGHT(x->b_left), HEIGHT((x->b_right))) + 1;
|
||||
}
|
||||
|
||||
static inline int bf(struct b_btree_node *x)
|
||||
{
|
||||
int bf = 0;
|
||||
|
||||
if (!x) {
|
||||
return bf;
|
||||
}
|
||||
|
||||
if (x->b_right) {
|
||||
bf += x->b_right->b_height;
|
||||
}
|
||||
|
||||
if (x->b_left) {
|
||||
bf -= x->b_left->b_height;
|
||||
}
|
||||
|
||||
return bf;
|
||||
}
|
||||
|
||||
/* perform a left rotation on a subtree
|
||||
|
||||
if you have a tree like this:
|
||||
|
||||
Z
|
||||
/ \
|
||||
X .
|
||||
/ \
|
||||
. Y
|
||||
/ \
|
||||
. .
|
||||
|
||||
and you perform a left rotation on node X,
|
||||
you will get the following tree:
|
||||
|
||||
Z
|
||||
/ \
|
||||
Y .
|
||||
/ \
|
||||
X .
|
||||
/ \
|
||||
. .
|
||||
|
||||
note that this function does NOT update b_height for the rotated
|
||||
nodes. it is up to you to call update_height_to_root().
|
||||
*/
|
||||
static void rotate_left(struct b_btree *tree, struct b_btree_node *x)
|
||||
{
|
||||
struct b_btree_node *y = x->b_right;
|
||||
struct b_btree_node *p = x->b_parent;
|
||||
|
||||
if (y->b_left) {
|
||||
y->b_left->b_parent = x;
|
||||
}
|
||||
|
||||
x->b_right = y->b_left;
|
||||
|
||||
if (!p) {
|
||||
tree->b_root = y;
|
||||
} else if (x == p->b_left) {
|
||||
p->b_left = y;
|
||||
} else {
|
||||
p->b_right = y;
|
||||
}
|
||||
|
||||
x->b_parent = y;
|
||||
y->b_left = x;
|
||||
y->b_parent = p;
|
||||
}
|
||||
|
||||
static void update_height_to_root(struct b_btree_node *x)
|
||||
{
|
||||
while (x) {
|
||||
update_height(x);
|
||||
x = x->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform a right rotation on a subtree
|
||||
|
||||
if you have a tree like this:
|
||||
|
||||
Z
|
||||
/ \
|
||||
. X
|
||||
/ \
|
||||
Y .
|
||||
/ \
|
||||
. .
|
||||
|
||||
and you perform a right rotation on node X,
|
||||
you will get the following tree:
|
||||
|
||||
Z
|
||||
/ \
|
||||
. Y
|
||||
/ \
|
||||
. X
|
||||
/ \
|
||||
. .
|
||||
|
||||
note that this function does NOT update b_height for the rotated
|
||||
nodes. it is up to you to call update_height_to_root().
|
||||
*/
|
||||
static void rotate_right(struct b_btree *tree, struct b_btree_node *y)
|
||||
{
|
||||
struct b_btree_node *x = y->b_left;
|
||||
struct b_btree_node *p = y->b_parent;
|
||||
|
||||
if (x->b_right) {
|
||||
x->b_right->b_parent = y;
|
||||
}
|
||||
|
||||
y->b_left = x->b_right;
|
||||
|
||||
if (!p) {
|
||||
tree->b_root = x;
|
||||
} else if (y == p->b_left) {
|
||||
p->b_left = x;
|
||||
} else {
|
||||
p->b_right = x;
|
||||
}
|
||||
|
||||
y->b_parent = x;
|
||||
x->b_right = y;
|
||||
x->b_parent = p;
|
||||
}
|
||||
|
||||
/* for a given node Z, perform a right rotation on Z's right child,
|
||||
followed by a left rotation on Z itself.
|
||||
|
||||
if you have a tree like this:
|
||||
|
||||
Z
|
||||
/ \
|
||||
. X
|
||||
/ \
|
||||
Y .
|
||||
/ \
|
||||
. .
|
||||
|
||||
and you perform a double-left rotation on node Z,
|
||||
you will get the following tree:
|
||||
|
||||
Y
|
||||
/ \
|
||||
/ \
|
||||
Z X
|
||||
/ \ / \
|
||||
. . . .
|
||||
|
||||
note that, unlike rotate_left and rotate_right, this function
|
||||
DOES update b_height for the rotated nodes (since it needs to be
|
||||
done in a certain order).
|
||||
*/
|
||||
static void rotate_double_left(struct b_btree *tree, struct b_btree_node *z)
|
||||
{
|
||||
struct b_btree_node *x = z->b_right;
|
||||
struct b_btree_node *y = x->b_left;
|
||||
|
||||
rotate_right(tree, x);
|
||||
rotate_left(tree, z);
|
||||
|
||||
update_height(z);
|
||||
update_height(x);
|
||||
|
||||
while (y) {
|
||||
update_height(y);
|
||||
y = y->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* for a given node Z, perform a left rotation on Z's left child,
|
||||
followed by a right rotation on Z itself.
|
||||
|
||||
if you have a tree like this:
|
||||
|
||||
Z
|
||||
/ \
|
||||
X .
|
||||
/ \
|
||||
. Y
|
||||
/ \
|
||||
. .
|
||||
|
||||
and you perform a double-right rotation on node Z,
|
||||
you will get the following tree:
|
||||
|
||||
Y
|
||||
/ \
|
||||
/ \
|
||||
X Z
|
||||
/ \ / \
|
||||
. . . .
|
||||
|
||||
note that, unlike rotate_left and rotate_right, this function
|
||||
DOES update b_height for the rotated nodes (since it needs to be
|
||||
done in a certain order).
|
||||
*/
|
||||
static void rotate_double_right(struct b_btree *tree, struct b_btree_node *z)
|
||||
{
|
||||
struct b_btree_node *x = z->b_left;
|
||||
struct b_btree_node *y = x->b_right;
|
||||
|
||||
rotate_left(tree, x);
|
||||
rotate_right(tree, z);
|
||||
|
||||
update_height(z);
|
||||
update_height(x);
|
||||
|
||||
while (y) {
|
||||
update_height(y);
|
||||
y = y->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* run after an insert operation. checks that the balance factor
|
||||
of the local subtree is within the range -1 <= BF <= 1. if it
|
||||
is not, rotate the subtree to restore balance.
|
||||
|
||||
note that at most one rotation should be required after a node
|
||||
is inserted into the tree.
|
||||
|
||||
this function depends on all nodes in the tree having
|
||||
correct b_height values.
|
||||
|
||||
@param w the node that was just inserted into the tree
|
||||
*/
|
||||
static void insert_fixup(struct b_btree *tree, struct b_btree_node *w)
|
||||
{
|
||||
struct b_btree_node *z = NULL, *y = NULL, *x = NULL;
|
||||
|
||||
z = w;
|
||||
while (z) {
|
||||
if (bf(z) >= -1 && bf(z) <= 1) {
|
||||
goto next_ancestor;
|
||||
}
|
||||
|
||||
if (IS_LEFT_CHILD(z, y)) {
|
||||
if (IS_LEFT_CHILD(y, x)) {
|
||||
rotate_right(tree, z);
|
||||
update_height_to_root(z);
|
||||
} else {
|
||||
rotate_double_right(tree, z);
|
||||
}
|
||||
} else {
|
||||
if (IS_LEFT_CHILD(y, x)) {
|
||||
rotate_double_left(tree, z);
|
||||
} else {
|
||||
rotate_left(tree, z);
|
||||
update_height_to_root(z);
|
||||
}
|
||||
}
|
||||
|
||||
next_ancestor:
|
||||
x = y;
|
||||
y = z;
|
||||
z = z->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* run after a delete operation. checks that the balance factor
|
||||
of the local subtree is within the range -1 <= BF <= 1. if it
|
||||
is not, rotate the subtree to restore balance.
|
||||
|
||||
note that, unlike insert_fixup, multiple rotations may be required
|
||||
to restore balance after a node is deleted.
|
||||
|
||||
this function depends on all nodes in the tree having
|
||||
correct b_height values.
|
||||
|
||||
@param w one of the following:
|
||||
- the parent of the node that was deleted if the node
|
||||
had no children.
|
||||
- the parent of the node that replaced the deleted node
|
||||
if the deleted node had two children.
|
||||
- the node that replaced the node that was deleted, if
|
||||
the node that was deleted had one child.
|
||||
*/
|
||||
static void delete_fixup(struct b_btree *tree, struct b_btree_node *w)
|
||||
{
|
||||
struct b_btree_node *z = w;
|
||||
|
||||
while (z) {
|
||||
if (bf(z) > 1) {
|
||||
if (bf(z->b_right) >= 0) {
|
||||
rotate_left(tree, z);
|
||||
update_height_to_root(z);
|
||||
} else {
|
||||
rotate_double_left(tree, z);
|
||||
}
|
||||
} else if (bf(z) < -1) {
|
||||
if (bf(z->b_left) <= 0) {
|
||||
rotate_right(tree, z);
|
||||
update_height_to_root(z);
|
||||
} else {
|
||||
rotate_double_right(tree, z);
|
||||
}
|
||||
}
|
||||
|
||||
z = z->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
/* updates b_height for all nodes between the inserted node and the root
|
||||
of the tree, and calls insert_fixup.
|
||||
|
||||
@param node the node that was just inserted into the tree.
|
||||
*/
|
||||
void b_btree_insert_fixup(struct b_btree *tree, struct b_btree_node *node)
|
||||
{
|
||||
node->b_height = 0;
|
||||
|
||||
struct b_btree_node *cur = node;
|
||||
while (cur) {
|
||||
update_height(cur);
|
||||
cur = cur->b_parent;
|
||||
}
|
||||
|
||||
insert_fixup(tree, node);
|
||||
}
|
||||
|
||||
/* remove a node from a tree.
|
||||
|
||||
this function assumes that `node` has no children, and therefore
|
||||
doesn't need to be replaced.
|
||||
|
||||
updates b_height for all nodes between `node` and the tree root.
|
||||
|
||||
@param node the node to delete.
|
||||
*/
|
||||
static struct b_btree_node *remove_node_with_no_children(
|
||||
struct b_btree *tree,
|
||||
struct b_btree_node *node)
|
||||
{
|
||||
struct b_btree_node *w = node->b_parent;
|
||||
struct b_btree_node *p = node->b_parent;
|
||||
node->b_parent = NULL;
|
||||
|
||||
if (!p) {
|
||||
tree->b_root = NULL;
|
||||
} else if (IS_LEFT_CHILD(p, node)) {
|
||||
p->b_left = NULL;
|
||||
} else {
|
||||
p->b_right = NULL;
|
||||
}
|
||||
|
||||
while (p) {
|
||||
update_height(p);
|
||||
p = p->b_parent;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/* remove a node from a tree.
|
||||
|
||||
this function assumes that `node` has one child.
|
||||
the child of `node` is inherited by `node`'s parent, and `node` is removed.
|
||||
|
||||
updates b_height for all nodes between the node that replaced
|
||||
`node` and the tree root.
|
||||
|
||||
@param node the node to delete.
|
||||
*/
|
||||
static struct b_btree_node *replace_node_with_one_subtree(
|
||||
struct b_btree *tree,
|
||||
struct b_btree_node *node)
|
||||
{
|
||||
struct b_btree_node *p = node->b_parent;
|
||||
struct b_btree_node *z = NULL;
|
||||
|
||||
if (HAS_LEFT_CHILD(node)) {
|
||||
z = node->b_left;
|
||||
} else {
|
||||
z = node->b_right;
|
||||
}
|
||||
|
||||
struct b_btree_node *w = z;
|
||||
if (!p) {
|
||||
tree->b_root = z;
|
||||
} else if (IS_LEFT_CHILD(p, node)) {
|
||||
p->b_left = z;
|
||||
} else if (IS_RIGHT_CHILD(p, node)) {
|
||||
p->b_right = z;
|
||||
}
|
||||
|
||||
z->b_parent = p;
|
||||
|
||||
node->b_parent = NULL;
|
||||
node->b_left = node->b_right = NULL;
|
||||
|
||||
while (z) {
|
||||
update_height(z);
|
||||
z = z->b_parent;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/* remove a node from a tree.
|
||||
|
||||
this function assumes that `node` has two children.
|
||||
find the in-order successor Y of `node` (the largest node in `node`'s left
|
||||
sub-tree), removes `node` from the tree and moves Y to where `node` used to
|
||||
be.
|
||||
|
||||
if Y has a child (it will never have more than one), have Y's parent inherit
|
||||
Y's child.
|
||||
|
||||
updates b_height for all nodes between the deepest node that was modified
|
||||
and the tree root.
|
||||
|
||||
@param z the node to delete.
|
||||
*/
|
||||
static struct b_btree_node *replace_node_with_two_subtrees(
|
||||
struct b_btree *tree,
|
||||
struct b_btree_node *z)
|
||||
{
|
||||
/* x will replace z */
|
||||
struct b_btree_node *x = z->b_left;
|
||||
|
||||
while (x->b_right) {
|
||||
x = x->b_right;
|
||||
}
|
||||
|
||||
/* y is the node that will replace x (if x has a left child) */
|
||||
struct b_btree_node *y = x->b_left;
|
||||
|
||||
/* w is the starting point for the height update and fixup */
|
||||
struct b_btree_node *w = x;
|
||||
if (w->b_parent != z) {
|
||||
w = w->b_parent;
|
||||
}
|
||||
|
||||
if (y) {
|
||||
w = y;
|
||||
}
|
||||
|
||||
if (IS_LEFT_CHILD(x->b_parent, x)) {
|
||||
x->b_parent->b_left = y;
|
||||
} else if (IS_RIGHT_CHILD(x->b_parent, x)) {
|
||||
x->b_parent->b_right = y;
|
||||
}
|
||||
|
||||
if (y) {
|
||||
y->b_parent = x->b_parent;
|
||||
}
|
||||
|
||||
if (IS_LEFT_CHILD(z->b_parent, z)) {
|
||||
z->b_parent->b_left = x;
|
||||
} else if (IS_RIGHT_CHILD(z->b_parent, z)) {
|
||||
z->b_parent->b_right = x;
|
||||
}
|
||||
|
||||
x->b_parent = z->b_parent;
|
||||
x->b_left = z->b_left;
|
||||
x->b_right = z->b_right;
|
||||
|
||||
if (x->b_left) {
|
||||
x->b_left->b_parent = x;
|
||||
}
|
||||
|
||||
if (x->b_right) {
|
||||
x->b_right->b_parent = x;
|
||||
}
|
||||
|
||||
if (!x->b_parent) {
|
||||
tree->b_root = x;
|
||||
}
|
||||
|
||||
struct b_btree_node *cur = w;
|
||||
while (cur) {
|
||||
update_height(cur);
|
||||
cur = cur->b_parent;
|
||||
}
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
/* delete a node from the tree and re-balance it afterwards */
|
||||
void b_btree_delete(struct b_btree *tree, struct b_btree_node *node)
|
||||
{
|
||||
struct b_btree_node *w = NULL;
|
||||
|
||||
if (HAS_NO_CHILDREN(node)) {
|
||||
w = remove_node_with_no_children(tree, node);
|
||||
} else if (HAS_ONE_CHILD(node)) {
|
||||
w = replace_node_with_one_subtree(tree, node);
|
||||
} else if (HAS_TWO_CHILDREN(node)) {
|
||||
w = replace_node_with_two_subtrees(tree, node);
|
||||
}
|
||||
|
||||
if (w) {
|
||||
delete_fixup(tree, w);
|
||||
}
|
||||
|
||||
node->b_left = node->b_right = node->b_parent = NULL;
|
||||
}
|
||||
|
||||
static struct b_btree_node *first_node(const struct b_btree *tree, int *depth)
|
||||
{
|
||||
/* the first node in the tree is the node with the smallest key.
|
||||
we keep moving left until we can't go any further */
|
||||
struct b_btree_node *cur = tree->b_root;
|
||||
int d = 0;
|
||||
|
||||
if (!cur) {
|
||||
*depth = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (cur->b_left) {
|
||||
d++;
|
||||
cur = cur->b_left;
|
||||
}
|
||||
|
||||
*depth = d;
|
||||
return cur;
|
||||
}
|
||||
|
||||
struct b_btree_node *b_btree_first(const struct b_btree *tree)
|
||||
{
|
||||
int d;
|
||||
return first_node(tree, &d);
|
||||
}
|
||||
|
||||
static struct b_btree_node *last_node(const struct b_btree *tree, int *depth)
|
||||
{
|
||||
/* the first node in the tree is the node with the largest key.
|
||||
we keep moving right until we can't go any further */
|
||||
struct b_btree_node *cur = tree->b_root;
|
||||
int d = 0;
|
||||
if (!cur) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (cur->b_right) {
|
||||
d++;
|
||||
cur = cur->b_right;
|
||||
}
|
||||
|
||||
*depth = d;
|
||||
return cur;
|
||||
}
|
||||
|
||||
b_btree_node *b_btree_last(const struct b_btree *tree)
|
||||
{
|
||||
int d;
|
||||
return last_node(tree, &d);
|
||||
}
|
||||
|
||||
static b_btree_node *next_node(const struct b_btree_node *node, int *depth_diff)
|
||||
{
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int depth = 0;
|
||||
|
||||
/* there are two possibilities for the next node:
|
||||
|
||||
1. if `node` has a right sub-tree, every node in this sub-tree is
|
||||
bigger than node. the in-order successor of `node` is the smallest
|
||||
node in this subtree.
|
||||
2. if `node` has no right sub-tree, we've reached the largest node in
|
||||
the sub-tree rooted at `node`. we need to go back to our parent
|
||||
and continue the search elsewhere.
|
||||
*/
|
||||
if (node->b_right) {
|
||||
/* case 1: step into `node`'s right sub-tree and keep going
|
||||
left to find the smallest node */
|
||||
struct b_btree_node *cur = node->b_right;
|
||||
depth++;
|
||||
while (cur->b_left) {
|
||||
cur = cur->b_left;
|
||||
depth++;
|
||||
}
|
||||
|
||||
*depth_diff = depth;
|
||||
return cur;
|
||||
}
|
||||
|
||||
/* case 2: keep stepping back up towards the root of the tree.
|
||||
if we encounter a step where we are our parent's left child,
|
||||
we've found a parent with a value larger than us. this parent
|
||||
is the in-order successor of `node` */
|
||||
while (node->b_parent && node->b_parent->b_left != node) {
|
||||
node = node->b_parent;
|
||||
depth--;
|
||||
}
|
||||
|
||||
*depth_diff = depth - 1;
|
||||
return node->b_parent;
|
||||
}
|
||||
|
||||
static b_btree_node *prev_node(const struct b_btree_node *node, int *depth_diff)
|
||||
{
|
||||
if (!node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int depth = 0;
|
||||
|
||||
/* there are two possibilities for the previous node:
|
||||
|
||||
1. if `node` has a left sub-tree, every node in this sub-tree is
|
||||
smaller than `node`. the in-order predecessor of `node` is the
|
||||
largest node in this subtree.
|
||||
2. if `node` has no left sub-tree, we've reached the smallest node in
|
||||
the sub-tree rooted at `node`. we need to go back to our parent
|
||||
and continue the search elsewhere.
|
||||
*/
|
||||
if (node->b_left) {
|
||||
/* case 1: step into `node`'s left sub-tree and keep going
|
||||
right to find the largest node */
|
||||
b_btree_node *cur = node->b_left;
|
||||
depth++;
|
||||
while (cur->b_right) {
|
||||
cur = cur->b_right;
|
||||
depth++;
|
||||
}
|
||||
|
||||
*depth_diff = depth;
|
||||
return cur;
|
||||
}
|
||||
|
||||
/* case 2: keep stepping back up towards the root of the tree.
|
||||
if we encounter a step where we are our parent's right child,
|
||||
we've found a parent with a value smaller than us. this parent
|
||||
is the in-order predecessor of `node`. */
|
||||
while (node->b_parent && node->b_parent->b_right != node) {
|
||||
node = node->b_parent;
|
||||
depth--;
|
||||
}
|
||||
|
||||
*depth_diff = depth - 1;
|
||||
return node->b_parent;
|
||||
}
|
||||
|
||||
b_btree_node *b_btree_next(const struct b_btree_node *node)
|
||||
{
|
||||
int d;
|
||||
return next_node(node, &d);
|
||||
}
|
||||
|
||||
b_btree_node *b_btree_prev(const struct b_btree_node *node)
|
||||
{
|
||||
int d;
|
||||
return prev_node(node, &d);
|
||||
}
|
||||
|
||||
static bool btree_iterator_next(struct b_iterator *it)
|
||||
{
|
||||
return b_btree_iterator_next((struct b_btree_iterator *)it);
|
||||
}
|
||||
|
||||
static b_status btree_iterator_erase(struct b_iterator *it)
|
||||
{
|
||||
return b_btree_iterator_erase((struct b_btree_iterator *)it);
|
||||
}
|
||||
|
||||
static bool btree_iterator_is_valid(const struct b_iterator *it)
|
||||
{
|
||||
return b_btree_iterator_is_valid((struct b_btree_iterator *)it);
|
||||
}
|
||||
|
||||
static const b_iterator_ops btree_iterator_ops = {
|
||||
.it_next = btree_iterator_next,
|
||||
.it_erase = btree_iterator_erase,
|
||||
.it_close = NULL,
|
||||
.it_is_valid = btree_iterator_is_valid,
|
||||
};
|
||||
|
||||
int b_btree_iterator_begin(
|
||||
const struct b_btree *tree,
|
||||
struct b_btree_iterator *it)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
it->_b = (struct b_btree *)tree;
|
||||
it->i = 0;
|
||||
it->node = first_node(tree, &depth);
|
||||
it->depth = depth;
|
||||
it->_base.it_ops = &btree_iterator_ops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool b_btree_iterator_next(struct b_btree_iterator *it)
|
||||
{
|
||||
int depth_diff = 0;
|
||||
struct b_btree_node *next = next_node(it->node, &depth_diff);
|
||||
|
||||
if (!next) {
|
||||
it->node = NULL;
|
||||
it->depth = 0;
|
||||
it->i++;
|
||||
return false;
|
||||
}
|
||||
|
||||
it->node = next;
|
||||
it->i++;
|
||||
it->depth += depth_diff;
|
||||
return true;
|
||||
}
|
||||
|
||||
b_status b_btree_iterator_erase(struct b_btree_iterator *it)
|
||||
{
|
||||
if (!it->node) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
int depth_diff = 0;
|
||||
struct b_btree_node *next = next_node(it->node, &depth_diff);
|
||||
|
||||
b_btree_delete(it->_b, it->node);
|
||||
if (!next) {
|
||||
it->node = NULL;
|
||||
it->depth = 0;
|
||||
} else {
|
||||
it->node = next;
|
||||
it->depth = 0;
|
||||
|
||||
struct b_btree_node *cur = next->b_parent;
|
||||
while (cur) {
|
||||
it->depth++;
|
||||
cur = cur->b_parent;
|
||||
}
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
bool b_btree_iterator_is_valid(const struct b_btree_iterator *it)
|
||||
{
|
||||
return it->node != NULL;
|
||||
}
|
||||
0
core/include/blue/core.h
Normal file
0
core/include/blue/core.h
Normal file
357
core/include/blue/core/btree.h
Normal file
357
core/include/blue/core/btree.h
Normal file
@@ -0,0 +1,357 @@
|
||||
#ifndef BLUELIB_CORE_BTREE_H_
|
||||
#define BLUELIB_CORE_BTREE_H_
|
||||
|
||||
#include <blue/core/iterator.h>
|
||||
#include <blue/core/misc.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* 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 == -1) { \
|
||||
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;
|
||||
|
||||
typedef struct b_btree_iterator {
|
||||
b_iterator _base;
|
||||
size_t i, depth;
|
||||
b_btree_node *node;
|
||||
b_btree *_b;
|
||||
} b_btree_iterator;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
extern 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.
|
||||
*/
|
||||
extern 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)
|
||||
*/
|
||||
extern 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)
|
||||
*/
|
||||
extern 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 */
|
||||
extern 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 */
|
||||
extern 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
extern int b_btree_iterator_begin(const b_btree *tree, b_btree_iterator *it);
|
||||
extern bool b_btree_iterator_next(b_btree_iterator *it);
|
||||
extern b_status b_btree_iterator_erase(b_btree_iterator *it);
|
||||
extern bool b_btree_iterator_is_valid(const b_btree_iterator *it);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
0
core/include/blue/core/exception.h
Normal file
0
core/include/blue/core/exception.h
Normal file
25
core/include/blue/core/iterator.h
Normal file
25
core/include/blue/core/iterator.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef BLUELIB_CORE_ITERATOR_H_
|
||||
#define BLUELIB_CORE_ITERATOR_H_
|
||||
|
||||
#include <blue/core/status.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct b_iterator;
|
||||
|
||||
typedef struct b_iterator_ops {
|
||||
b_status (*it_close)(struct b_iterator *);
|
||||
bool (*it_next)(struct b_iterator *);
|
||||
b_status (*it_erase)(struct b_iterator *);
|
||||
bool (*it_is_valid)(const struct b_iterator *);
|
||||
} b_iterator_ops;
|
||||
|
||||
typedef struct b_iterator {
|
||||
const b_iterator_ops *it_ops;
|
||||
} b_iterator;
|
||||
|
||||
extern b_status b_iterator_close(b_iterator *it);
|
||||
extern bool b_iterator_next(b_iterator *it);
|
||||
extern b_status b_iterator_erase(b_iterator *it);
|
||||
extern bool b_iterator_is_valid(const b_iterator *it);
|
||||
|
||||
#endif
|
||||
13
core/include/blue/core/misc.h
Normal file
13
core/include/blue/core/misc.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef BLUELIB_MISC_H_
|
||||
#define BLUELIB_MISC_H_
|
||||
|
||||
#define b_unbox(type, box, member) \
|
||||
((type *_Nonnull)((box) ? (uintptr_t)(box) - (offsetof(type, member)) : 0))
|
||||
|
||||
#define z__b_merge_(a, b) a##b
|
||||
#define z__b_label_(a) z__b_merge_(__unique_name_, a)
|
||||
#define z__b_unique_name() z__b_label_(__LINE__)
|
||||
#define z__b_numargs(arg_type, ...) \
|
||||
(sizeof((arg_type[]) {__VA_ARGS__}) / sizeof(arg_type))
|
||||
|
||||
#endif // C_MISC_H_
|
||||
88
core/include/blue/core/queue.h
Normal file
88
core/include/blue/core/queue.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef BLUELIB_CORE_QUEUE_H_
|
||||
#define BLUELIB_CORE_QUEUE_H_
|
||||
|
||||
#include <blue/core/iterator.h>
|
||||
#include <blue/core/status.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define B_QUEUE_INIT ((b_queue) {.q_first = NULL, .q_last = NULL})
|
||||
#define B_QUEUE_ENTRY_INIT ((b_queue_entry) {.qe_next = NULL, .qe_prev = NULL})
|
||||
|
||||
#define b_queue_foreach(it, q) \
|
||||
for (int z__b_unique_name() = b_queue_iterator_begin(q, it); \
|
||||
(it)->entry != NULL; b_queue_iterator_next(it))
|
||||
|
||||
typedef struct b_queue_entry {
|
||||
struct b_queue_entry *qe_next;
|
||||
struct b_queue_entry *qe_prev;
|
||||
} b_queue_entry;
|
||||
|
||||
typedef struct b_queue {
|
||||
b_queue_entry *q_first;
|
||||
b_queue_entry *q_last;
|
||||
} b_queue;
|
||||
|
||||
typedef struct b_queue_iterator {
|
||||
b_iterator _base;
|
||||
size_t i;
|
||||
b_queue_entry *entry;
|
||||
b_queue *_q;
|
||||
} b_queue_iterator;
|
||||
|
||||
static inline void b_queue_init(b_queue *q)
|
||||
{
|
||||
memset(q, 0x00, sizeof *q);
|
||||
}
|
||||
static inline bool b_queue_empty(b_queue *q)
|
||||
{
|
||||
return q->q_first == NULL;
|
||||
}
|
||||
|
||||
static inline b_queue_entry *b_queue_first(const b_queue *q)
|
||||
{
|
||||
return q->q_first;
|
||||
}
|
||||
static inline b_queue_entry *b_queue_last(const b_queue *q)
|
||||
{
|
||||
return q->q_last;
|
||||
}
|
||||
static inline b_queue_entry *b_queue_next(const b_queue_entry *entry)
|
||||
{
|
||||
return entry->qe_next;
|
||||
}
|
||||
static inline b_queue_entry *b_queue_prev(const b_queue_entry *entry)
|
||||
{
|
||||
return entry->qe_prev;
|
||||
}
|
||||
|
||||
extern size_t b_queue_length(b_queue *q);
|
||||
|
||||
extern void b_queue_insert_before(
|
||||
b_queue *q, b_queue_entry *entry, b_queue_entry *before);
|
||||
extern void b_queue_insert_after(
|
||||
b_queue *q, b_queue_entry *entry, b_queue_entry *after);
|
||||
|
||||
extern void b_queue_push_front(b_queue *q, b_queue_entry *entry);
|
||||
extern void b_queue_push_back(b_queue *q, b_queue_entry *entry);
|
||||
|
||||
extern b_queue_entry *b_queue_pop_front(b_queue *q);
|
||||
extern b_queue_entry *b_queue_pop_back(b_queue *q);
|
||||
|
||||
extern void b_queue_delete(b_queue *q, b_queue_entry *entry);
|
||||
extern void b_queue_delete_all(b_queue *q);
|
||||
|
||||
extern int b_queue_iterator_begin(const b_queue *q, b_queue_iterator *it);
|
||||
extern bool b_queue_iterator_next(b_queue_iterator *it);
|
||||
extern b_status b_queue_iterator_erase(b_queue_iterator *it);
|
||||
extern bool b_queue_iterator_is_valid(const b_queue_iterator *it);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
23
core/include/blue/core/status.h
Normal file
23
core/include/blue/core/status.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef BLUELIB_CORE_STATUS_H_
|
||||
#define BLUELIB_CORE_STATUS_H_
|
||||
|
||||
#define B_OK(status) ((status) == B_SUCCESS)
|
||||
#define B_ERR(status) ((status) != B_SUCCESS)
|
||||
|
||||
typedef enum {
|
||||
B_SUCCESS = 0x00u,
|
||||
B_ERR_NO_MEMORY,
|
||||
B_ERR_OUT_OF_BOUNDS,
|
||||
B_ERR_INVALID_ARGUMENT,
|
||||
B_ERR_NAME_EXISTS,
|
||||
B_ERR_NOT_SUPPORTED,
|
||||
B_ERR_BAD_STATE,
|
||||
B_ERR_NO_ENTRY,
|
||||
B_ERR_NO_DATA,
|
||||
B_ERR_UNKNOWN_FUNCTION,
|
||||
B_ERR_BAD_FORMAT,
|
||||
} b_status;
|
||||
|
||||
extern const char *b_status_to_string(b_status status);
|
||||
|
||||
#endif
|
||||
30
core/include/blue/core/stringstream.h
Normal file
30
core/include/blue/core/stringstream.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#ifndef BLUELIB_CORE_STRINGSTREAM_H_
|
||||
#define BLUELIB_CORE_STRINGSTREAM_H_
|
||||
|
||||
#include <blue/core/status.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct b_stringstream {
|
||||
char *ss_buf;
|
||||
size_t ss_len;
|
||||
size_t ss_max;
|
||||
unsigned char ss_alloc;
|
||||
} b_stringstream;
|
||||
|
||||
extern void b_stringstream_begin(b_stringstream *strv, char *buf, size_t max);
|
||||
extern void b_stringstream_begin_dynamic(b_stringstream *strv);
|
||||
|
||||
extern b_status b_stringstream_add(b_stringstream *strv, const char *str);
|
||||
extern b_status b_stringstream_addf(
|
||||
b_stringstream *strv,
|
||||
const char *format,
|
||||
...);
|
||||
extern b_status b_stringstream_addv(b_stringstream *strv, const char **strs);
|
||||
extern b_status b_stringstream_addvl(
|
||||
b_stringstream *strv,
|
||||
const char **strs,
|
||||
size_t count);
|
||||
extern b_status b_stringstream_add_many(b_stringstream *strv, ...);
|
||||
extern char *b_stringstream_end(b_stringstream *strv);
|
||||
|
||||
#endif
|
||||
37
core/iterator.c
Normal file
37
core/iterator.c
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <blue/core/iterator.h>
|
||||
|
||||
b_status b_iterator_cleanup(struct b_iterator *it)
|
||||
{
|
||||
if (it->it_ops && it->it_ops->it_close) {
|
||||
return it->it_ops->it_close(it);
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
bool b_iterator_next(struct b_iterator *it)
|
||||
{
|
||||
if (it->it_ops && it->it_ops->it_next) {
|
||||
return it->it_ops->it_next(it);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
b_status b_iterator_erase(struct b_iterator *it)
|
||||
{
|
||||
if (it->it_ops && it->it_ops->it_erase) {
|
||||
return it->it_ops->it_erase(it);
|
||||
}
|
||||
|
||||
return B_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
bool b_iterator_is_valid(const struct b_iterator *it)
|
||||
{
|
||||
if (it->it_ops && it->it_ops->it_is_valid) {
|
||||
return it->it_ops->it_is_valid(it);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
196
core/queue.c
Normal file
196
core/queue.c
Normal file
@@ -0,0 +1,196 @@
|
||||
#include <blue/core/queue.h>
|
||||
|
||||
size_t b_queue_length(struct b_queue *q)
|
||||
{
|
||||
size_t i = 0;
|
||||
struct b_queue_entry *x = q->q_first;
|
||||
while (x) {
|
||||
i++;
|
||||
x = x->qe_next;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
void b_queue_insert_before(
|
||||
struct b_queue *q, struct b_queue_entry *entry, struct b_queue_entry *before)
|
||||
{
|
||||
struct b_queue_entry *x = before->qe_prev;
|
||||
if (x) {
|
||||
x->qe_next = entry;
|
||||
} else {
|
||||
q->q_first = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = x;
|
||||
|
||||
before->qe_prev = entry;
|
||||
entry->qe_next = before;
|
||||
}
|
||||
|
||||
void b_queue_insert_after(
|
||||
struct b_queue *q, struct b_queue_entry *entry, struct b_queue_entry *after)
|
||||
{
|
||||
struct b_queue_entry *x = after->qe_next;
|
||||
if (x) {
|
||||
x->qe_prev = entry;
|
||||
} else {
|
||||
q->q_last = entry;
|
||||
}
|
||||
|
||||
entry->qe_next = x;
|
||||
|
||||
after->qe_next = entry;
|
||||
entry->qe_prev = after;
|
||||
}
|
||||
|
||||
void b_queue_push_front(struct b_queue *q, struct b_queue_entry *entry)
|
||||
{
|
||||
if (q->q_first) {
|
||||
q->q_first->qe_prev = entry;
|
||||
}
|
||||
|
||||
entry->qe_next = q->q_first;
|
||||
entry->qe_prev = NULL;
|
||||
|
||||
q->q_first = entry;
|
||||
|
||||
if (!q->q_last) {
|
||||
q->q_last = entry;
|
||||
}
|
||||
}
|
||||
|
||||
void b_queue_push_back(struct b_queue *q, struct b_queue_entry *entry)
|
||||
{
|
||||
if (q->q_last) {
|
||||
q->q_last->qe_next = entry;
|
||||
}
|
||||
|
||||
entry->qe_prev = q->q_last;
|
||||
entry->qe_next = NULL;
|
||||
|
||||
q->q_last = entry;
|
||||
|
||||
if (!q->q_first) {
|
||||
q->q_first = entry;
|
||||
}
|
||||
}
|
||||
|
||||
struct b_queue_entry *b_queue_pop_front(struct b_queue *q)
|
||||
{
|
||||
struct b_queue_entry *x = q->q_first;
|
||||
if (x) {
|
||||
b_queue_delete(q, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
struct b_queue_entry *b_queue_pop_back(struct b_queue *q)
|
||||
{
|
||||
struct b_queue_entry *x = q->q_last;
|
||||
if (x) {
|
||||
b_queue_delete(q, x);
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void b_queue_delete(struct b_queue *q, struct b_queue_entry *entry)
|
||||
{
|
||||
if (!entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entry == q->q_first) {
|
||||
q->q_first = q->q_first->qe_next;
|
||||
}
|
||||
|
||||
if (entry == q->q_last) {
|
||||
q->q_last = q->q_last->qe_prev;
|
||||
}
|
||||
|
||||
if (entry->qe_next) {
|
||||
entry->qe_next->qe_prev = entry->qe_prev;
|
||||
}
|
||||
|
||||
if (entry->qe_prev) {
|
||||
entry->qe_prev->qe_next = entry->qe_next;
|
||||
}
|
||||
|
||||
entry->qe_next = entry->qe_prev = NULL;
|
||||
}
|
||||
|
||||
void b_queue_delete_all(struct b_queue *q)
|
||||
{
|
||||
struct b_queue_entry *x = q->q_first;
|
||||
while (x) {
|
||||
struct b_queue_entry *next = x->qe_next;
|
||||
x->qe_next = x->qe_prev = NULL;
|
||||
x = next;
|
||||
}
|
||||
|
||||
q->q_first = q->q_last = NULL;
|
||||
}
|
||||
|
||||
static bool queue_iterator_next(struct b_iterator *it)
|
||||
{
|
||||
return b_queue_iterator_next((struct b_queue_iterator *)it);
|
||||
}
|
||||
|
||||
static b_status queue_iterator_erase(struct b_iterator *it)
|
||||
{
|
||||
return b_queue_iterator_erase((struct b_queue_iterator *)it);
|
||||
}
|
||||
|
||||
static bool queue_iterator_is_valid(const struct b_iterator *it)
|
||||
{
|
||||
return b_queue_iterator_is_valid((const struct b_queue_iterator *)it);
|
||||
}
|
||||
|
||||
static const b_iterator_ops queue_iterator_ops = {
|
||||
.it_next = queue_iterator_next,
|
||||
.it_erase = queue_iterator_erase,
|
||||
.it_close = NULL,
|
||||
.it_is_valid = queue_iterator_is_valid,
|
||||
};
|
||||
|
||||
int b_queue_iterator_begin(const struct b_queue *q, struct b_queue_iterator *it)
|
||||
{
|
||||
it->_q = (struct b_queue *)q;
|
||||
it->_base.it_ops = &queue_iterator_ops;
|
||||
it->entry = q->q_first;
|
||||
it->i = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool b_queue_iterator_next(struct b_queue_iterator *it)
|
||||
{
|
||||
if (!it->entry) {
|
||||
return false;
|
||||
}
|
||||
|
||||
it->entry = it->entry->qe_next;
|
||||
it->i++;
|
||||
|
||||
return it->entry != NULL;
|
||||
}
|
||||
|
||||
b_status b_queue_iterator_erase(struct b_queue_iterator *it)
|
||||
{
|
||||
if (!it->entry) {
|
||||
return B_ERR_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
struct b_queue_entry *next = it->entry->qe_next;
|
||||
b_queue_delete(it->_q, it->entry);
|
||||
it->entry = next;
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
bool b_queue_iterator_is_valid(const struct b_queue_iterator *it)
|
||||
{
|
||||
return it->entry != NULL;
|
||||
}
|
||||
15
core/status.c
Normal file
15
core/status.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include <blue/core/status.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define ENUM_STR(s) \
|
||||
case s: \
|
||||
return #s;
|
||||
|
||||
const char *b_status_to_string(b_status status)
|
||||
{
|
||||
switch (status) {
|
||||
ENUM_STR(B_SUCCESS);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
118
core/stringstream.c
Normal file
118
core/stringstream.c
Normal file
@@ -0,0 +1,118 @@
|
||||
#include <blue/core/stringstream.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void b_stringstream_begin(b_stringstream *ss, char *buf, size_t max)
|
||||
{
|
||||
ss->ss_buf = buf;
|
||||
ss->ss_max = max;
|
||||
ss->ss_len = 0;
|
||||
ss->ss_alloc = 0;
|
||||
}
|
||||
|
||||
void b_stringstream_begin_dynamic(b_stringstream *ss)
|
||||
{
|
||||
ss->ss_buf = NULL;
|
||||
ss->ss_max = 0;
|
||||
ss->ss_len = 0;
|
||||
ss->ss_alloc = 1;
|
||||
}
|
||||
|
||||
static b_status ss_builder_push_string(b_stringstream *ss, const char *s, size_t len)
|
||||
{
|
||||
if (ss->ss_len + len >= ss->ss_max && ss->ss_alloc == 1) {
|
||||
char *new_buf = realloc(ss->ss_buf, ss->ss_len + len + 1);
|
||||
if (!new_buf) {
|
||||
return B_ERR_NO_MEMORY;
|
||||
}
|
||||
|
||||
ss->ss_buf = new_buf;
|
||||
ss->ss_max = ss->ss_len + len + 1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (ss->ss_len < ss->ss_max) {
|
||||
ss->ss_buf[ss->ss_len++] = s[i];
|
||||
ss->ss_buf[ss->ss_len] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_stringstream_add(struct b_stringstream *ss, const char *str)
|
||||
{
|
||||
return ss_builder_push_string(ss, str, strlen(str));
|
||||
}
|
||||
|
||||
b_status b_stringstream_addf(struct b_stringstream *ss, const char *format, ...)
|
||||
{
|
||||
char str[1024];
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
size_t len = vsnprintf(str, sizeof str, format, arg);
|
||||
va_end(arg);
|
||||
|
||||
return ss_builder_push_string(ss, str, len);
|
||||
}
|
||||
|
||||
b_status b_stringstream_addv(b_stringstream *ss, const char **strs)
|
||||
{
|
||||
for (size_t i = 0; strs[i]; i++) {
|
||||
size_t len = strlen(strs[i]);
|
||||
b_status status = ss_builder_push_string(ss, strs[i], len);
|
||||
if (B_ERR(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_stringstream_addvl(b_stringstream *ss, const char **strs, size_t count)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
if (!strs[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t len = strlen(strs[i]);
|
||||
b_status status = ss_builder_push_string(ss, strs[i], len);
|
||||
if (B_ERR(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
b_status b_stringstream_add_many(b_stringstream *ss, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, ss);
|
||||
const char *s = NULL;
|
||||
|
||||
while ((s = va_arg(arg, const char *))) {
|
||||
size_t len = strlen(s);
|
||||
b_status status = ss_builder_push_string(ss, s, len);
|
||||
if (B_ERR(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return B_SUCCESS;
|
||||
}
|
||||
|
||||
char *b_stringstream_end(b_stringstream *ss)
|
||||
{
|
||||
char *out = ss->ss_buf;
|
||||
|
||||
ss->ss_alloc = 0;
|
||||
ss->ss_len = 0;
|
||||
ss->ss_max = 0;
|
||||
ss->ss_buf = NULL;
|
||||
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user