LibC: Implement POSIX binary search trees
I used RB tree for this. The implementation is based on the code on wikipedia about RB trees.
This commit is contained in:
@@ -32,6 +32,7 @@ set(LIBC_SOURCES
|
||||
regex.cpp
|
||||
scanf_impl.cpp
|
||||
sched.cpp
|
||||
search.cpp
|
||||
semaphore.cpp
|
||||
setjmp.cpp
|
||||
signal.cpp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef _SEARCH_H
|
||||
#define _SEARCH_H 1
|
||||
|
||||
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/search.h.html
|
||||
// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/search.h.html
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
@@ -19,6 +19,14 @@ typedef struct
|
||||
typedef enum { FIND, ENTER } ACTION;
|
||||
typedef enum { preorder, postorder, endorder, leaf } VISIT;
|
||||
|
||||
// NOTE: POSIX requires posix_tnode to be void but
|
||||
// defining an internal type avoids casting
|
||||
#ifdef __is_libc
|
||||
typedef struct __posix_tnode posix_tnode;
|
||||
#else
|
||||
typedef void posix_tnode;
|
||||
#endif
|
||||
|
||||
int hcreate(size_t nel);
|
||||
void hdestroy(void);
|
||||
ENTRY* hsearch(ENTRY item, ACTION action);
|
||||
@@ -26,10 +34,10 @@ void insque(void* element, void* pred);
|
||||
void* lfind(const void* key, const void* base, size_t* nelp, size_t width, int (*compar)(const void*, const void*));
|
||||
void* lsearch(const void* key, void* base, size_t* nelp, size_t width, int (*compar)(const void*, const void*));
|
||||
void remque(void* element);
|
||||
void* tdelete(const void* __restrict key, void** __restrict rootp, int(*compar)(const void*, const void*));
|
||||
void* tfind(const void* key, void* const* rootp, int(*compar)(const void*, const void*));
|
||||
void* tsearch(const void* key, void** rootp, int(*compar)(const void*, const void*));
|
||||
void twalk(const void* root, void (*action)(const void*, VISIT, int));
|
||||
void* tdelete(const void* __restrict key, posix_tnode** __restrict rootp, int (*compar)(const void*, const void*));
|
||||
posix_tnode* tfind(const void* key, posix_tnode* const* rootp, int (*compar)(const void*, const void*));
|
||||
posix_tnode* tsearch(const void* key, posix_tnode** rootp, int (*compar)(const void*, const void*));
|
||||
void twalk(const posix_tnode* root, void (*action)(const posix_tnode*, VISIT, int));
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
#include <search.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// RB Tree implementation based on the code in wikipedia https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
||||
|
||||
struct __posix_tnode
|
||||
{
|
||||
const void* key;
|
||||
posix_tnode* parent;
|
||||
posix_tnode* children[2];
|
||||
enum { RED, BLACK } color;
|
||||
};
|
||||
|
||||
posix_tnode* tfind(const void* key, posix_tnode* const* rootp, int (*compar)(const void*, const void*))
|
||||
{
|
||||
if (*rootp == nullptr)
|
||||
return nullptr;
|
||||
|
||||
posix_tnode* node = *rootp;
|
||||
while (node)
|
||||
{
|
||||
const int result = compar(key, node->key);
|
||||
if (result == 0)
|
||||
return node;
|
||||
node = node->children[result > 0];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static posix_tnode* _trotate(posix_tnode** root, posix_tnode* node, int dir)
|
||||
{
|
||||
posix_tnode* old_parent = node->parent;
|
||||
posix_tnode* new_root = node->children[1 - dir];
|
||||
posix_tnode* new_child = new_root->children[dir];
|
||||
|
||||
node->children[1 - dir] = new_child;
|
||||
|
||||
if (new_child)
|
||||
new_child->parent = node;
|
||||
|
||||
new_root->children[dir] = node;
|
||||
|
||||
new_root->parent = old_parent;
|
||||
node->parent = new_root;
|
||||
if (old_parent)
|
||||
old_parent->children[node == old_parent->children[1]] = new_root;
|
||||
else
|
||||
*root = new_root;
|
||||
|
||||
return new_root;
|
||||
}
|
||||
|
||||
static void _tinsert(posix_tnode** root, posix_tnode* node, posix_tnode* parent, int dir)
|
||||
{
|
||||
node->color = posix_tnode::RED;
|
||||
node->parent = parent;
|
||||
|
||||
if (!parent)
|
||||
{
|
||||
*root = node;
|
||||
return;
|
||||
}
|
||||
|
||||
parent->children[dir] = node;
|
||||
|
||||
do {
|
||||
if (parent->color == posix_tnode::BLACK)
|
||||
return;
|
||||
|
||||
posix_tnode* grandparent = parent->parent;
|
||||
if (!grandparent)
|
||||
{
|
||||
parent->color = posix_tnode::BLACK;
|
||||
return;
|
||||
}
|
||||
|
||||
dir = (parent == grandparent->children[1]);
|
||||
|
||||
posix_tnode* uncle = grandparent->children[1 - dir];
|
||||
if (!uncle || uncle->color == posix_tnode::BLACK)
|
||||
{
|
||||
if (node == parent->children[1 - dir])
|
||||
{
|
||||
_trotate(root, parent, dir);
|
||||
node = parent;
|
||||
parent = grandparent->children[dir];
|
||||
}
|
||||
|
||||
_trotate(root, grandparent, 1 - dir);
|
||||
parent->color = posix_tnode::BLACK;
|
||||
grandparent->color = posix_tnode::RED;
|
||||
return;
|
||||
}
|
||||
|
||||
parent->color = posix_tnode::BLACK;
|
||||
uncle->color = posix_tnode::BLACK;
|
||||
grandparent->color = posix_tnode::RED;
|
||||
node = grandparent;
|
||||
} while ((parent = node->parent));
|
||||
}
|
||||
|
||||
posix_tnode* tsearch(const void* key, posix_tnode** root, int (*compar)(const void*, const void*))
|
||||
{
|
||||
posix_tnode* parent = nullptr;
|
||||
posix_tnode* node = *root;
|
||||
int dir = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
const int result = compar(key, node->key);
|
||||
if (result == 0)
|
||||
return node;
|
||||
parent = node;
|
||||
dir = (result > 0);
|
||||
node = node->children[dir];
|
||||
}
|
||||
|
||||
node = static_cast<posix_tnode*>(malloc(sizeof(posix_tnode)));
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
|
||||
*node = {
|
||||
.key = key,
|
||||
.parent = nullptr,
|
||||
.children = {},
|
||||
.color = posix_tnode::RED,
|
||||
};
|
||||
|
||||
_tinsert(root, node, parent, dir);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void _tremove_black_leaf(posix_tnode** __restrict root, posix_tnode* node)
|
||||
{
|
||||
const auto case6 = [&](posix_tnode* parent, posix_tnode* sibling, posix_tnode* far_nephew, int dir) -> bool {
|
||||
_trotate(root, parent, dir);
|
||||
sibling->color = parent->color;
|
||||
parent->color = posix_tnode::BLACK;
|
||||
far_nephew->color = posix_tnode::BLACK;
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto case5 = [&](posix_tnode* parent, posix_tnode* sibling, posix_tnode* near_nephew, int dir) -> bool {
|
||||
_trotate(root, sibling, 1 - dir);
|
||||
sibling->color = posix_tnode::RED;
|
||||
near_nephew->color = posix_tnode::BLACK;
|
||||
case6(parent, near_nephew, sibling, dir);
|
||||
return true;
|
||||
};
|
||||
|
||||
const auto try_case56 = [&](posix_tnode* parent, posix_tnode* sibling, int dir) -> bool {
|
||||
if (posix_tnode* far_nephew = sibling->children[1 - dir]; far_nephew && far_nephew->color == posix_tnode::RED)
|
||||
return case6(parent, sibling, far_nephew, dir);
|
||||
if (posix_tnode* near_nephew = sibling->children[ dir]; near_nephew && near_nephew->color == posix_tnode::RED)
|
||||
return case5(parent, sibling, near_nephew, dir);
|
||||
return false;
|
||||
};
|
||||
|
||||
posix_tnode* parent = node->parent;
|
||||
|
||||
int dir = (node == parent->children[1]);
|
||||
|
||||
parent->children[dir] = nullptr;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
posix_tnode* sibling = parent->children[1 - dir];
|
||||
|
||||
if (sibling->color == posix_tnode::RED)
|
||||
{
|
||||
posix_tnode* near_nephew = sibling->children[dir];
|
||||
|
||||
_trotate(root, parent, dir);
|
||||
parent->color = posix_tnode::RED;
|
||||
sibling->color = posix_tnode::BLACK;
|
||||
sibling = near_nephew;
|
||||
|
||||
if (try_case56(parent, sibling, dir))
|
||||
return;
|
||||
|
||||
sibling->color = posix_tnode::RED;
|
||||
parent->color = posix_tnode::BLACK;
|
||||
return;
|
||||
}
|
||||
|
||||
if (try_case56(parent, sibling, dir))
|
||||
return;
|
||||
|
||||
if (parent->color == posix_tnode::RED)
|
||||
{
|
||||
sibling->color = posix_tnode::RED;
|
||||
parent->color = posix_tnode::BLACK;
|
||||
return;
|
||||
}
|
||||
|
||||
sibling->color = posix_tnode::RED;
|
||||
|
||||
node = parent;
|
||||
parent = node->parent;
|
||||
if (parent == nullptr)
|
||||
return;
|
||||
|
||||
dir = (node == parent->children[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void* tdelete(const void* __restrict key, posix_tnode** __restrict root, int (*compar)(const void*, const void*))
|
||||
{
|
||||
posix_tnode* node = tfind(key, root, compar);
|
||||
if (node == nullptr)
|
||||
return nullptr;
|
||||
|
||||
// The tdelete() function shall return a pointer to the parent of the deleted node, or an unspecified non-null pointer
|
||||
// if the deleted node was the root node, or a null pointer if the node is not found.
|
||||
static posix_tnode dummy {};
|
||||
posix_tnode* return_value = node->parent ? node->parent : &dummy;
|
||||
|
||||
if (node->children[0] && node->children[1])
|
||||
{
|
||||
posix_tnode* succ = node->children[1];
|
||||
while (succ->children[0])
|
||||
succ = succ->children[0];
|
||||
node->key = succ->key;
|
||||
node = succ;
|
||||
}
|
||||
|
||||
posix_tnode* parent = node->parent;
|
||||
posix_tnode* child = node->children[0]
|
||||
? node->children[0]
|
||||
: node->children[1];
|
||||
|
||||
if (parent == nullptr || child)
|
||||
{
|
||||
posix_tnode* parent = node->parent;
|
||||
|
||||
if (child)
|
||||
{
|
||||
child->parent = parent;
|
||||
child->color = posix_tnode::BLACK;
|
||||
}
|
||||
|
||||
if (parent == nullptr)
|
||||
*root = child;
|
||||
else
|
||||
{
|
||||
int dir = (node == parent->children[1]);
|
||||
parent->children[dir] = child;
|
||||
}
|
||||
}
|
||||
else if (node->color == posix_tnode::RED)
|
||||
{
|
||||
int dir = (node == parent->children[1]);
|
||||
parent->children[dir] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
_tremove_black_leaf(root, node);
|
||||
}
|
||||
|
||||
free(node);
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static void _twalk(const posix_tnode* node, void (*action)(const posix_tnode*, VISIT, int), int depth)
|
||||
{
|
||||
if (!node->children[0] && !node->children[1])
|
||||
return action(node, leaf, depth);
|
||||
action(node, preorder, depth);
|
||||
if (node->children[0])
|
||||
_twalk(node->children[0], action, depth + 1);
|
||||
action(node, postorder, depth);
|
||||
if (node->children[1])
|
||||
_twalk(node->children[1], action, depth + 1);
|
||||
action(node, endorder, depth);
|
||||
}
|
||||
|
||||
void twalk(const posix_tnode* root, void (*action)(const posix_tnode*, VISIT, int))
|
||||
{
|
||||
if (root == nullptr)
|
||||
return;
|
||||
_twalk(root, action, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user