Compare commits

...

8 Commits

8 changed files with 528 additions and 7 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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}
};