initial commit

This commit is contained in:
2024-08-03 07:54:28 +01:00
commit 7eb0fc5581
26 changed files with 3418 additions and 0 deletions

0
core/include/blue/core.h Normal file
View File

View 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

View File

View 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

View 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_

View 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

View 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

View 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