forked from vieter-v/libvieter
Compare commits
8 Commits
e4e92b3e57
...
3b94bc4491
Author | SHA1 | Date |
---|---|---|
Jef Roosens | 3b94bc4491 | |
Jef Roosens | b685c72e6e | |
Jef Roosens | f56ba20343 | |
Jef Roosens | 6a6c91c64d | |
Jef Roosens | 0a6c58be53 | |
Jef Roosens | e88bfadf2c | |
Jef Roosens | 2ecd2eae4d | |
Jef Roosens | 7fb172c291 |
18
Makefile
18
Makefile
|
@ -8,6 +8,8 @@ SRC_DIR ?= src
|
|||
TEST_DIR ?= test
|
||||
INC_DIRS ?= include
|
||||
|
||||
LIB := $(BUILD_DIR)/$(LIB_FILENAME)
|
||||
|
||||
SRCS != find '$(SRC_DIR)' -iname '*.c'
|
||||
SRCS_H != find $(INC_DIRS) -iname '*.h'
|
||||
SRCS_TEST != find '$(TEST_DIR)' -iname '*.c'
|
||||
|
@ -25,7 +27,7 @@ INC_FLAGS := $(addprefix -I,$(INC_DIRS))
|
|||
# object file is also recompiled if only a header is changed.
|
||||
# -MP: generate a dummy target for every header file (according to the docs it
|
||||
# prevents some errors when removing header files)
|
||||
CFLAGS ?= -MMD -MP -Wall -Werror -Wextra
|
||||
CFLAGS ?= -MMD -MP -Wall -Wextra -g
|
||||
CFLAGS += $(INC_FLAGS)
|
||||
|
||||
.PHONY: all
|
||||
|
@ -34,8 +36,8 @@ all: vieter
|
|||
|
||||
# =====COMPILATION=====
|
||||
.PHONY: vieter
|
||||
vieter: $(BUILD_DIR)/$(LIB_FILENAME)
|
||||
$(BUILD_DIR)/$(LIB_FILENAME): $(OBJS)
|
||||
vieter: $(LIB)
|
||||
$(LIB): $(OBJS)
|
||||
ar -rcs $@ $(OBJS)
|
||||
|
||||
$(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
|
||||
|
@ -48,13 +50,15 @@ $(BUILD_DIR)/$(SRC_DIR)/%.c.o: $(SRC_DIR)/%.c
|
|||
test: build-test
|
||||
@ $(foreach bin,$(BINS_TEST),./$(bin);)
|
||||
|
||||
test-mem: build-test
|
||||
@ $(foreach bin,$(BINS_TEST),valgrind --tool=memcheck --error-exitcode=1 \
|
||||
--track-origins=yes --leak-check=full ./$(bin);)
|
||||
|
||||
.PHONY: build-test
|
||||
build-test: $(BINS_TEST)
|
||||
|
||||
# For simplicity, we link every object file to each of the test files. This
|
||||
# might be changed later if this starts to become too slow.
|
||||
$(BINS_TEST): %: %.c.o $(OBJS)
|
||||
$(CC) $^ -o $@
|
||||
$(BINS_TEST): %: %.c.o $(LIB)
|
||||
$(CC) -L$(BUILD_DIR) -lvieter $^ -o $@
|
||||
|
||||
# Each test includes the test directory, which contains the acutest header file
|
||||
$(BUILD_DIR)/$(TEST_DIR)/%.c.o: $(TEST_DIR)/%.c
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef VIETER_TREE
|
||||
#define VIETER_TREE
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct vieter_tree vieter_tree;
|
||||
|
||||
typedef enum vieter_tree_error {
|
||||
vieter_tree_ok = 0,
|
||||
vieter_tree_already_present = 1,
|
||||
vieter_tree_not_present = 2
|
||||
} vieter_tree_error;
|
||||
|
||||
vieter_tree *vieter_tree_init();
|
||||
|
||||
void vieter_tree_free(vieter_tree *tree);
|
||||
|
||||
vieter_tree_error vieter_tree_insert(vieter_tree *tree, uint64_t key,
|
||||
void *data);
|
||||
|
||||
vieter_tree_error vieter_tree_search(void **out, vieter_tree *tree,
|
||||
uint64_t key);
|
||||
|
||||
vieter_tree_error vieter_tree_remove(void **out, vieter_tree *tree,
|
||||
uint64_t key);
|
||||
|
||||
uint64_t vieter_tree_size(vieter_tree *tree);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,100 @@
|
|||
#include "vieter_tree.h"
|
||||
#include "vieter_tree_node.h"
|
||||
|
||||
struct vieter_tree {
|
||||
uint64_t size;
|
||||
vieter_tree_node *root;
|
||||
};
|
||||
|
||||
vieter_tree *vieter_tree_init() { return calloc(1, sizeof(vieter_tree)); }
|
||||
|
||||
uint64_t vieter_tree_size(vieter_tree *tree) { return tree->size; }
|
||||
|
||||
vieter_tree_error vieter_tree_insert(vieter_tree *tree, uint64_t key,
|
||||
void *data) {
|
||||
if (tree->size == 0) {
|
||||
tree->root = vieter_tree_node_init();
|
||||
tree->root->key = key;
|
||||
tree->root->data = data;
|
||||
vieter_tree_node_set(tree->root, vieter_node_black);
|
||||
tree->size = 1;
|
||||
|
||||
return vieter_tree_ok;
|
||||
}
|
||||
|
||||
vieter_tree_error res = vieter_tree_node_insert(tree->root, key, data);
|
||||
|
||||
if (res != vieter_tree_ok) {
|
||||
return res;
|
||||
}
|
||||
|
||||
tree->size++;
|
||||
|
||||
return vieter_tree_ok;
|
||||
}
|
||||
|
||||
vieter_tree_error vieter_tree_search(void **out, vieter_tree *tree,
|
||||
uint64_t key) {
|
||||
if (tree->size == 0) {
|
||||
return vieter_tree_not_present;
|
||||
}
|
||||
|
||||
return vieter_tree_node_search(out, tree->root, key);
|
||||
}
|
||||
|
||||
vieter_tree_error vieter_tree_remove(void **out, vieter_tree *tree,
|
||||
uint64_t key) {
|
||||
if (tree->size == 0) {
|
||||
return vieter_tree_not_present;
|
||||
}
|
||||
|
||||
vieter_tree_error res = vieter_tree_node_remove(out, tree->root, key);
|
||||
|
||||
if (res != vieter_tree_ok) {
|
||||
return res;
|
||||
}
|
||||
|
||||
tree->size--;
|
||||
|
||||
return vieter_tree_ok;
|
||||
}
|
||||
|
||||
void vieter_tree_free(vieter_tree *tree) {
|
||||
if (tree->size == 0) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
uint64_t capacity = 4;
|
||||
uint64_t size = 1;
|
||||
vieter_tree_node **stack = malloc(capacity * sizeof(vieter_tree_node *));
|
||||
stack[0] = tree->root;
|
||||
|
||||
vieter_tree_node *node;
|
||||
|
||||
while (size > 0) {
|
||||
node = stack[size - 1];
|
||||
size--;
|
||||
|
||||
if (size + 2 > capacity) {
|
||||
capacity *= 2;
|
||||
stack = realloc(stack, capacity * sizeof(vieter_tree_node *));
|
||||
}
|
||||
|
||||
if (node->left != NULL) {
|
||||
stack[size] = node->left;
|
||||
size++;
|
||||
}
|
||||
|
||||
if (node->right != NULL) {
|
||||
stack[size] = node->right;
|
||||
size++;
|
||||
}
|
||||
|
||||
vieter_tree_node_free(node);
|
||||
}
|
||||
|
||||
free(stack);
|
||||
|
||||
end:
|
||||
free(tree);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
#include "vieter_tree_balancing.h"
|
||||
|
||||
vieter_tree_node *vieter_tree_node_balance(vieter_tree_node *node) {
|
||||
vieter_tree_node *parent = node->parent;
|
||||
vieter_tree_node *grand_parent = parent->parent;
|
||||
vieter_tree_node *root = grand_parent;
|
||||
vieter_tree_node *left, *right;
|
||||
vieter_tree_node *children[4];
|
||||
|
||||
uint64_t key_root = root->key;
|
||||
void *data_root = root->data;
|
||||
|
||||
if (node->key < parent->key && parent->key < grand_parent->key) {
|
||||
root->key = parent->key;
|
||||
root->data = parent->data;
|
||||
|
||||
parent->key = key_root;
|
||||
parent->data = data_root;
|
||||
|
||||
left = node;
|
||||
right = parent;
|
||||
|
||||
children[0] = node->left;
|
||||
children[1] = node->right;
|
||||
children[2] = parent->right;
|
||||
children[3] = grand_parent->right;
|
||||
} else if (node->key < parent->key && parent->key > grand_parent->key) {
|
||||
root->key = node->key;
|
||||
root->data = node->data;
|
||||
|
||||
node->key = key_root;
|
||||
node->data = data_root;
|
||||
|
||||
left = node;
|
||||
right = parent;
|
||||
|
||||
children[0] = grand_parent->left;
|
||||
children[1] = node->left;
|
||||
children[2] = node->right;
|
||||
children[3] = parent->right;
|
||||
} else if (node->key > parent->key && parent->key < grand_parent->key) {
|
||||
root->key = node->key;
|
||||
root->data = node->data;
|
||||
|
||||
node->key = key_root;
|
||||
node->data = data_root;
|
||||
|
||||
left = parent;
|
||||
right = node;
|
||||
|
||||
children[0] = parent->left;
|
||||
children[1] = node->left;
|
||||
children[2] = node->right;
|
||||
children[3] = grand_parent->right;
|
||||
} else {
|
||||
root->key = parent->key;
|
||||
root->data = parent->data;
|
||||
|
||||
parent->key = key_root;
|
||||
parent->data = data_root;
|
||||
|
||||
left = parent;
|
||||
right = node;
|
||||
|
||||
children[0] = grand_parent->left;
|
||||
children[1] = parent->left;
|
||||
children[2] = node->left;
|
||||
children[3] = node->right;
|
||||
}
|
||||
|
||||
vieter_tree_node_set(left, vieter_node_black);
|
||||
vieter_tree_node_set(right, vieter_node_black);
|
||||
vieter_tree_node_unset(root, vieter_node_black);
|
||||
|
||||
left->left = children[0];
|
||||
left->right = children[1];
|
||||
|
||||
if (children[0] != NULL)
|
||||
children[0]->parent = left;
|
||||
|
||||
if (children[1] != NULL)
|
||||
children[1]->parent = left;
|
||||
|
||||
right->left = children[2];
|
||||
right->right = children[3];
|
||||
|
||||
if (children[2] != NULL)
|
||||
children[2]->parent = right;
|
||||
|
||||
if (children[3] != NULL)
|
||||
children[3]->parent = right;
|
||||
|
||||
root->left = left;
|
||||
root->right = right;
|
||||
left->parent = root;
|
||||
right->parent = root;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void vieter_tree_node_balance_after_insert(vieter_tree_node *node) {
|
||||
while (!vieter_tree_node_get(node, vieter_node_black) &&
|
||||
node->parent != NULL &&
|
||||
!vieter_tree_node_get(node->parent, vieter_node_black)) {
|
||||
node = vieter_tree_node_balance(node);
|
||||
}
|
||||
|
||||
// The root must always be black
|
||||
if (node->parent == NULL) {
|
||||
vieter_tree_node_set(node, vieter_node_black);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef VIETER_TREE_BALANCING
|
||||
#define VIETER_TREE_BALANCING
|
||||
|
||||
#include "vieter_tree_node.h"
|
||||
|
||||
void vieter_tree_node_balance_after_insert(vieter_tree_node *node);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,166 @@
|
|||
#include "vieter_tree_node.h"
|
||||
#include "vieter_tree_balancing.h"
|
||||
|
||||
vieter_tree_node *vieter_tree_node_init() {
|
||||
return calloc(1, sizeof(vieter_tree_node));
|
||||
}
|
||||
|
||||
void vieter_tree_node_free(vieter_tree_node *node) { free(node); }
|
||||
|
||||
void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key,
|
||||
vieter_tree_node *child) {
|
||||
if (parent == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key < parent->key) {
|
||||
parent->left = child;
|
||||
} else {
|
||||
parent->right = child;
|
||||
}
|
||||
|
||||
if (child != NULL) {
|
||||
child->parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
void vieter_tree_node_replace_with_child(vieter_tree_node *to_replace,
|
||||
vieter_tree_node *replacement) {
|
||||
to_replace->key = replacement->key;
|
||||
to_replace->data = replacement->data;
|
||||
to_replace->left = replacement->left;
|
||||
to_replace->right = replacement->right;
|
||||
|
||||
if (to_replace->left != NULL)
|
||||
to_replace->left->parent = to_replace;
|
||||
|
||||
if (to_replace->right != NULL)
|
||||
to_replace->right->parent = to_replace;
|
||||
}
|
||||
|
||||
vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key,
|
||||
void *data) {
|
||||
vieter_tree_node *node = root;
|
||||
vieter_tree_node *parent = root;
|
||||
|
||||
while (node != NULL) {
|
||||
if (node->key == key) {
|
||||
return vieter_tree_already_present;
|
||||
}
|
||||
|
||||
parent = node;
|
||||
|
||||
if (key < parent->key) {
|
||||
node = parent->left;
|
||||
} else {
|
||||
node = parent->right;
|
||||
}
|
||||
}
|
||||
|
||||
vieter_tree_node *new_node = vieter_tree_node_init();
|
||||
new_node->key = key;
|
||||
new_node->data = data;
|
||||
|
||||
vieter_tree_node_add_child(parent, key, new_node);
|
||||
vieter_tree_node_balance_after_insert(new_node);
|
||||
|
||||
return vieter_tree_ok;
|
||||
}
|
||||
|
||||
vieter_tree_error vieter_tree_node_search_node(vieter_tree_node **out,
|
||||
vieter_tree_node *root,
|
||||
uint64_t key) {
|
||||
vieter_tree_node *node = root;
|
||||
|
||||
while (node != NULL) {
|
||||
if (node->key == key) {
|
||||
*out = node;
|
||||
|
||||
return vieter_tree_ok;
|
||||
}
|
||||
|
||||
if (key < node->key) {
|
||||
node = node->left;
|
||||
} else {
|
||||
node = node->right;
|
||||
}
|
||||
}
|
||||
|
||||
return vieter_tree_not_present;
|
||||
}
|
||||
|
||||
vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *root,
|
||||
uint64_t key) {
|
||||
vieter_tree_node *target;
|
||||
vieter_tree_error res = vieter_tree_node_search_node(&target, root, key);
|
||||
|
||||
if (res != vieter_tree_ok) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*out = target->data;
|
||||
|
||||
return vieter_tree_ok;
|
||||
}
|
||||
|
||||
vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root,
|
||||
uint64_t key) {
|
||||
vieter_tree_node *target;
|
||||
vieter_tree_error res = vieter_tree_node_search_node(&target, root, key);
|
||||
|
||||
if (res != vieter_tree_ok) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*out = target->data;
|
||||
|
||||
if (target->left == NULL && target->right == NULL) {
|
||||
vieter_tree_node_add_child(target->parent, target->key, NULL);
|
||||
|
||||
vieter_tree_node_free(target);
|
||||
} else if ((target->left == NULL) ^ (target->right == NULL)) {
|
||||
vieter_tree_node *child =
|
||||
target->left != NULL ? target->left : target->right;
|
||||
|
||||
if (target->parent != NULL) {
|
||||
vieter_tree_node_add_child(target->parent, child->key, child);
|
||||
vieter_tree_node_free(target);
|
||||
} else {
|
||||
vieter_tree_node_replace_with_child(target, child);
|
||||
vieter_tree_node_free(child);
|
||||
}
|
||||
|
||||
} else {
|
||||
vieter_tree_node *replacement = target->right;
|
||||
|
||||
while (replacement->left != NULL) {
|
||||
replacement = replacement->left;
|
||||
}
|
||||
|
||||
// We use replacement->key here because the right child can be NULL, so
|
||||
// reading its key isn't safe. Using replacement->key however, the child
|
||||
// will still get placed into the right location because of how binary
|
||||
// trees work.
|
||||
vieter_tree_node_add_child(replacement->parent, replacement->key,
|
||||
replacement->right);
|
||||
|
||||
target->key = replacement->key;
|
||||
target->data = replacement->data;
|
||||
|
||||
vieter_tree_node_free(replacement);
|
||||
}
|
||||
|
||||
return vieter_tree_ok;
|
||||
}
|
||||
|
||||
void vieter_tree_node_set(vieter_tree_node *node, vieter_node_flag flag) {
|
||||
node->flags |= flag;
|
||||
}
|
||||
|
||||
void vieter_tree_node_unset(vieter_tree_node *node, vieter_node_flag flag) {
|
||||
node->flags &= ~flag;
|
||||
}
|
||||
|
||||
bool vieter_tree_node_get(vieter_tree_node *node, vieter_node_flag flag) {
|
||||
return (node->flags & flag) != 0;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef VIETER_TREE_NODE
|
||||
#define VIETER_TREE_NODE
|
||||
|
||||
#include "vieter_tree.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define VIETER_NODE_BLACK ((uint8_t)1)
|
||||
|
||||
typedef enum vieter_node_flag {
|
||||
vieter_node_black = ((uint8_t)1) << 0
|
||||
} vieter_node_flag;
|
||||
|
||||
typedef struct vieter_tree_node {
|
||||
uint64_t key;
|
||||
void *data;
|
||||
struct vieter_tree_node *parent;
|
||||
struct vieter_tree_node *left;
|
||||
struct vieter_tree_node *right;
|
||||
uint8_t flags;
|
||||
} vieter_tree_node;
|
||||
|
||||
vieter_tree_node *vieter_tree_node_init();
|
||||
|
||||
void vieter_tree_node_free(vieter_tree_node *node);
|
||||
|
||||
vieter_tree_error vieter_tree_node_insert(vieter_tree_node *root, uint64_t key, void *data);
|
||||
|
||||
vieter_tree_error vieter_tree_node_search_node(vieter_tree_node **out, vieter_tree_node *root, uint64_t key);
|
||||
vieter_tree_error vieter_tree_node_search(void **out, vieter_tree_node *root, uint64_t key);
|
||||
|
||||
vieter_tree_error vieter_tree_node_remove(void **out, vieter_tree_node *root, uint64_t key);
|
||||
|
||||
void vieter_tree_node_replace(vieter_tree_node *to_replace, vieter_tree_node *replacement);
|
||||
|
||||
void vieter_tree_node_set(vieter_tree_node *node, vieter_node_flag flag);
|
||||
void vieter_tree_node_unset(vieter_tree_node *node, vieter_node_flag flag);
|
||||
bool vieter_tree_node_get(vieter_tree_node *node, vieter_node_flag flag);
|
||||
|
||||
void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key,
|
||||
vieter_tree_node *child);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
#include "acutest.h"
|
||||
#include "vieter_tree.h"
|
||||
|
||||
#define TEST_SIZE(tree, size) \
|
||||
TEST_CHECK(vieter_tree_size(tree) == size); \
|
||||
TEST_MSG("Size: %zu", vieter_tree_size(tree))
|
||||
|
||||
void test_init() {
|
||||
vieter_tree *tree = vieter_tree_init();
|
||||
TEST_CHECK(tree != NULL);
|
||||
TEST_SIZE(tree, 0);
|
||||
vieter_tree_free(tree);
|
||||
}
|
||||
|
||||
void test_insert() {
|
||||
vieter_tree *tree = vieter_tree_init();
|
||||
|
||||
for (uint64_t i = 0; i < 250; i++) {
|
||||
TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok);
|
||||
TEST_SIZE(tree, i + 1);
|
||||
}
|
||||
|
||||
void *out;
|
||||
|
||||
for (uint64_t i = 0; i < 250; i++) {
|
||||
TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok);
|
||||
TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_already_present);
|
||||
TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok);
|
||||
}
|
||||
|
||||
vieter_tree_free(tree);
|
||||
}
|
||||
|
||||
void test_remove() {
|
||||
vieter_tree *tree = vieter_tree_init();
|
||||
|
||||
for (uint64_t i = 0; i < 250; i++) {
|
||||
TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok);
|
||||
}
|
||||
|
||||
void *out;
|
||||
|
||||
for (uint64_t i = 0; i < 250; i++) {
|
||||
TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok);
|
||||
TEST_CHECK(vieter_tree_remove(&out, tree, i) == vieter_tree_ok);
|
||||
TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_not_present);
|
||||
TEST_SIZE(tree, 250 - i - 1);
|
||||
}
|
||||
|
||||
vieter_tree_free(tree);
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{"test_init", test_init},
|
||||
{"test_insert", test_insert},
|
||||
{"test_remove", test_remove},
|
||||
{NULL, NULL}
|
||||
};
|
Loading…
Reference in New Issue