feat(lsm): binary tree iterators
parent
8c2a7a640d
commit
5564e23ceb
|
@ -25,6 +25,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
* Trie-based router (no more RegEx)
|
||||
* Landerctl
|
||||
* `-c` flag to use custom config file (useful for testing)
|
||||
* LSM
|
||||
* Binary tree iterators
|
||||
|
||||
## Removed
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef LSM_BT
|
||||
#define LSM_BT
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "lsm.h"
|
||||
|
||||
/**
|
||||
|
@ -8,6 +10,11 @@
|
|||
*/
|
||||
typedef struct lsm_bt lsm_bt;
|
||||
|
||||
/**
|
||||
* A node inside an `lsm_bt` binary tree.
|
||||
*/
|
||||
typedef struct lsm_bt_node lsm_bt_node;
|
||||
|
||||
/**
|
||||
* Initialize a new binary tree
|
||||
*
|
||||
|
@ -68,4 +75,33 @@ lsm_error lsm_bt_remove(void **out, lsm_bt *bt, char key);
|
|||
*/
|
||||
lsm_error lsm_bt_replace(void **out, lsm_bt *bt, char key, void *data);
|
||||
|
||||
/**
|
||||
* Struct representing an in-flight iterator over the binary tree
|
||||
*/
|
||||
typedef struct lsm_bt_iterator {
|
||||
lsm_bt_node *next;
|
||||
} lsm_bt_iterator;
|
||||
|
||||
/**
|
||||
* Initialize the given iterator for the binary tree.
|
||||
*
|
||||
* The iterator is explicitely allowed to be allocated by the user, as these are
|
||||
* commonly used inside functions where they can simply be stored on the stack.
|
||||
*
|
||||
* @param out iterator to initialize
|
||||
* @param bt binary tree to iterate
|
||||
*/
|
||||
void lsm_bt_iter(lsm_bt_iterator *out, const lsm_bt *bt);
|
||||
|
||||
/**
|
||||
* Advance the iterator to the next element.
|
||||
*
|
||||
* @param out where to store pointer to data; ignored if NULL
|
||||
* @param key_out where to store key; ignored if NULL
|
||||
* @param iter iterator to advance
|
||||
* @return true if a new entry was returned, false if the iterator has no more
|
||||
* entries to return
|
||||
*/
|
||||
bool lsm_bt_iter_next(const void **out, char *key_out, lsm_bt_iterator *iter);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
/**
|
||||
* Node inside a binary tree
|
||||
*/
|
||||
typedef struct lsm_bt_node {
|
||||
struct lsm_bt_node {
|
||||
struct lsm_bt_node *left;
|
||||
struct lsm_bt_node *right;
|
||||
struct lsm_bt_node *parent;
|
||||
void *data;
|
||||
char key;
|
||||
} lsm_bt_node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a new binary tree node
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include "lsm/bt.h"
|
||||
#include "lsm/bt_internal.h"
|
||||
|
||||
lsm_error lsm_bt_node_init(lsm_bt_node **ptr, const char key, void *data) {
|
||||
|
@ -60,10 +61,12 @@ void lsm_bt_free(lsm_bt *bt) {
|
|||
uint64_t lsm_bt_size(const lsm_bt *bt) { return bt->size; }
|
||||
|
||||
lsm_error lsm_bt_insert(lsm_bt *bt, char key, void *data) {
|
||||
lsm_bt_node *parent = NULL;
|
||||
lsm_bt_node **dest = &bt->root;
|
||||
|
||||
// Traverse down the tree until we reach the new point to insert our node
|
||||
while ((*dest != NULL) && ((*dest)->key != key)) {
|
||||
parent = *dest;
|
||||
dest = key < (*dest)->key ? &(*dest)->left : &(*dest)->right;
|
||||
}
|
||||
|
||||
|
@ -71,9 +74,8 @@ lsm_error lsm_bt_insert(lsm_bt *bt, char key, void *data) {
|
|||
return lsm_error_already_present;
|
||||
}
|
||||
|
||||
if (lsm_bt_node_init(dest, key, data) != lsm_error_ok) {
|
||||
return lsm_error_failed_alloc;
|
||||
}
|
||||
LSM_RES(lsm_bt_node_init(dest, key, data));
|
||||
(*dest)->parent = parent;
|
||||
|
||||
bt->size++;
|
||||
|
||||
|
@ -125,13 +127,27 @@ lsm_error lsm_bt_remove(void **out, lsm_bt *bt, char key) {
|
|||
(*dest)->data = (*succ)->data;
|
||||
|
||||
lsm_bt_node *succ_replacement = (*succ)->right;
|
||||
lsm_bt_node *parent = (*succ)->parent;
|
||||
|
||||
lsm_bt_node_free(*succ);
|
||||
|
||||
*succ = succ_replacement;
|
||||
|
||||
if (*succ != NULL) {
|
||||
(*succ)->parent = parent;
|
||||
}
|
||||
} else {
|
||||
lsm_bt_node *replacement =
|
||||
(*dest)->left != NULL ? (*dest)->left : (*dest)->right;
|
||||
lsm_bt_node *parent = (*dest)->parent;
|
||||
|
||||
lsm_bt_node_free(*dest);
|
||||
|
||||
*dest = replacement;
|
||||
|
||||
if (*dest != NULL) {
|
||||
(*dest)->parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return lsm_error_ok;
|
||||
|
@ -156,3 +172,45 @@ lsm_error lsm_bt_replace(void **out, lsm_bt *bt, char key, void *data) {
|
|||
|
||||
return lsm_error_ok;
|
||||
}
|
||||
|
||||
void lsm_bt_iter(lsm_bt_iterator *out, const lsm_bt *bt) {
|
||||
out->next = bt->root;
|
||||
|
||||
if (bt->root != NULL) {
|
||||
// Initialize the iterator to the smallest element in the tree
|
||||
while (out->next->left != NULL) {
|
||||
out->next = out->next->left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool lsm_bt_iter_next(const void **out, char *key_out, lsm_bt_iterator *iter) {
|
||||
if (iter->next == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (out != NULL) {
|
||||
*out = iter->next->data;
|
||||
}
|
||||
|
||||
if (key_out != NULL) {
|
||||
*key_out = iter->next->key;
|
||||
}
|
||||
|
||||
if (iter->next->right != NULL) {
|
||||
iter->next = iter->next->right;
|
||||
|
||||
while (iter->next->left != NULL) {
|
||||
iter->next = iter->next->left;
|
||||
}
|
||||
} else {
|
||||
while ((iter->next->parent != NULL) &&
|
||||
(iter->next->parent->right == iter->next)) {
|
||||
iter->next = iter->next->parent;
|
||||
}
|
||||
|
||||
iter->next = iter->next->parent;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -104,6 +104,33 @@ void test_remove_multiple() {
|
|||
lsm_bt_free(bt);
|
||||
}
|
||||
|
||||
void test_iter() {
|
||||
char chars[] = "falcoep";
|
||||
size_t char_count = sizeof(chars) / sizeof(char) - 1;
|
||||
|
||||
char sorted_chars[] = "aceflop";
|
||||
|
||||
BT_INIT();
|
||||
|
||||
for (size_t i = 0; i < char_count; i++) {
|
||||
TEST_CHECK(lsm_bt_insert(bt, chars[i], (void *)(i + 1)) == lsm_error_ok);
|
||||
}
|
||||
|
||||
lsm_bt_iterator iter;
|
||||
lsm_bt_iter(&iter, bt);
|
||||
|
||||
char key;
|
||||
const void *data;
|
||||
size_t i = 0;
|
||||
|
||||
while (lsm_bt_iter_next(&data, &key, &iter)) {
|
||||
TEST_CHECK_(key == sorted_chars[i], "%c == %c", key, sorted_chars[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
TEST_CHECK(i == char_count);
|
||||
}
|
||||
|
||||
TEST_LIST = {
|
||||
{ "bt init", test_init },
|
||||
{ "bt insert first", test_insert_first },
|
||||
|
@ -111,5 +138,6 @@ TEST_LIST = {
|
|||
{ "bt insert multiple", test_insert_multiple },
|
||||
{ "bt remove root", test_remove_root },
|
||||
{ "bt remove multiple", test_remove_multiple },
|
||||
{ "bt iter", test_iter },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue