start implementing generic b-tree interface
This commit is contained in:
247
src/b-tree.c
247
src/b-tree.c
@@ -0,0 +1,247 @@
|
||||
#include <blue/core/queue.h>
|
||||
|
||||
#include "b-tree.h"
|
||||
|
||||
#define SWAP(type, x, y) \
|
||||
do { \
|
||||
type tmp = x; \
|
||||
x = y; \
|
||||
y = tmp; \
|
||||
} while (0)
|
||||
|
||||
struct cache_entry {
|
||||
int c_allocated;
|
||||
b_queue_entry c_entry;
|
||||
/* ... node data here ... */
|
||||
};
|
||||
|
||||
static b_tree_node *cache_alloc_node(struct b_tree *tree)
|
||||
{
|
||||
b_queue_iterator it = { 0 };
|
||||
b_queue_foreach (&it, &tree->tree_cache) {
|
||||
struct cache_entry *e = b_unbox(struct cache_entry, it.entry, c_entry);
|
||||
if (!e->c_allocated) {
|
||||
e->c_allocated = 1;
|
||||
return (b_tree_node *)(e + 1);
|
||||
}
|
||||
}
|
||||
|
||||
struct cache_entry *e = malloc(sizeof *e + tree->tree_ops->node_size);
|
||||
if (!e) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(e, 0x0, sizeof *e);
|
||||
e->c_allocated = 1;
|
||||
e->c_entry = B_QUEUE_ENTRY_INIT;
|
||||
|
||||
b_queue_push_back(&tree->tree_cache, &e->c_entry);
|
||||
|
||||
return (b_tree_node *)(e + 1);
|
||||
}
|
||||
|
||||
static void cache_free_node(struct b_tree *tree, b_tree_node *node)
|
||||
{
|
||||
struct cache_entry *e = (struct cache_entry *)((unsigned char *)node - sizeof *e);
|
||||
e->c_allocated = 0;
|
||||
}
|
||||
|
||||
void b_tree_init(struct b_tree *tree, const struct b_tree_ops *ops, unsigned int order)
|
||||
{
|
||||
memset(tree, 0x0, sizeof *tree);
|
||||
tree->tree_ops = ops;
|
||||
tree->tree_order = order;
|
||||
}
|
||||
|
||||
static int node_read(struct b_tree *tree, long id, b_tree_node *out)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static unsigned long node_get_nr_entries(struct b_tree *tree, b_tree_node *node)
|
||||
{
|
||||
return tree->tree_ops->node_get_nr_entries(node);
|
||||
}
|
||||
|
||||
static long node_get_child(struct b_tree *tree, b_tree_node *node, unsigned long index)
|
||||
{
|
||||
return tree->tree_ops->node_get_child(node, index);
|
||||
}
|
||||
|
||||
static void node_set_child(struct b_tree *tree, b_tree_node *node, unsigned long index, long ptr)
|
||||
{
|
||||
tree->tree_ops->node_set_child(node, index, ptr);
|
||||
}
|
||||
|
||||
static b_tree_node_entry node_get_entry(struct b_tree *tree, b_tree_node *node, unsigned long index)
|
||||
{
|
||||
return tree->tree_ops->node_get_entry(node, index);
|
||||
}
|
||||
|
||||
static int node_is_leaf(struct b_tree *tree, b_tree_node *node)
|
||||
{
|
||||
long first_child = node_get_child(tree, node, 0);
|
||||
return first_child == B_TREE_INVALID_PTR;
|
||||
}
|
||||
|
||||
static void node_init(struct b_tree *tree, b_tree_node *out)
|
||||
{
|
||||
memset(out, 0x0, tree->tree_ops->node_size);
|
||||
for (unsigned int i = 0; i < tree->tree_order; i++) {
|
||||
node_set_child(tree, out, i, B_TREE_INVALID_PTR);
|
||||
}
|
||||
}
|
||||
|
||||
static int entry_compare(struct b_tree *tree, b_tree_node_entry *a, b_tree_node_entry *b)
|
||||
{
|
||||
return tree->tree_ops->entry_compare(a, b);
|
||||
}
|
||||
|
||||
int b_tree_put(struct b_tree *tree, const b_tree_node_entry *to_put)
|
||||
{
|
||||
int depth = 0;
|
||||
|
||||
long current_id = tree->tree_root;
|
||||
long next_id = -1;
|
||||
|
||||
b_tree_node *current = cache_alloc_node(tree);
|
||||
b_tree_node *next = cache_alloc_node(tree);
|
||||
|
||||
int err = node_read(tree, current_id, current);
|
||||
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
unsigned long nr_entries = node_get_nr_entries(tree, current);
|
||||
|
||||
if (nr_entries == tree->tree_order - 1) {
|
||||
/* root node is full. split it pre-emptively. */
|
||||
b_tree_node *a = next;
|
||||
b_tree_node *b = cache_alloc_node(tree);
|
||||
|
||||
node_init(tree, a);
|
||||
node_init(tree, b);
|
||||
|
||||
int median = nr_entries / 2;
|
||||
|
||||
node_move_entries(current, &a, 0, 0, median);
|
||||
node_move_entries(current, &b, 1, 0, median);
|
||||
|
||||
node_move_children(current, &a, 0, 0, median + 1);
|
||||
node_move_children(current, &b, median + 1, 0, median + 1);
|
||||
|
||||
int a_index = tree_alloc_id(tree);
|
||||
node_write(tree, a_index, a);
|
||||
|
||||
int b_index = tree_alloc_id(tree);
|
||||
node_write(tree, b_index, b);
|
||||
|
||||
node_set_child(tree, current, 0, a_index);
|
||||
node_set_child(tree, current, 1, b_index);
|
||||
|
||||
node_write(tree, current_id, current);
|
||||
|
||||
cache_free_node(tree, b);
|
||||
}
|
||||
|
||||
bool leaf = node_is_leaf(tree, current);
|
||||
|
||||
while (!leaf) {
|
||||
unsigned int i;
|
||||
bool found_bigger_key = false;
|
||||
|
||||
nr_entries = node_get_nr_entries(tree, current);
|
||||
next_id = node_get_child(tree, current, nr_entries);
|
||||
|
||||
for (i = 0; i < nr_entries; i++) {
|
||||
b_tree_node_entry *entry = node_get_entry(tree, current, i);
|
||||
int cmp = entry_compare(tree, entry, to_put);
|
||||
|
||||
if (cmp == 1) {
|
||||
next_id = node_get_child(tree, current, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err = node_read(tree, next_id, next);
|
||||
if (err != 0) {
|
||||
return err;
|
||||
}
|
||||
|
||||
nr_entries = node_get_nr_entries(tree, next);
|
||||
if (nr_entries < tree->tree_order - 1) {
|
||||
/* swap current and next pointers. */
|
||||
SWAP(b_tree_node *, current, next);
|
||||
current_id = next_id;
|
||||
|
||||
leaf = node_is_leaf(tree, current);
|
||||
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* this node is full, split it. */
|
||||
|
||||
/* half the nodes from `next` will be moved into `n3`. the other
|
||||
half will stay put. the median entry will be moved to
|
||||
`current`.
|
||||
|
||||
`n3` will become the right sibling of `next` */
|
||||
b_tree_node *n3 = cache_alloc_node(tree);
|
||||
node_init(tree, n3);
|
||||
|
||||
bool n3_is_next = false;
|
||||
nr_entries = node_get_nr_entries(tree, next);
|
||||
int median = nr_entries / 2;
|
||||
b_tree_node_entry *median_node = node_get_entry(tree, next, median);
|
||||
int cmp = entry_compare(tree, median_node, to_put);
|
||||
if (cmp == -1) {
|
||||
// median++;
|
||||
n3_is_next = true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
node_move_entries(
|
||||
next,
|
||||
&n3,
|
||||
median + 1,
|
||||
0,
|
||||
nr_entries - median - 1);
|
||||
node_move_children(
|
||||
next,
|
||||
&n3,
|
||||
median + 1,
|
||||
0,
|
||||
nr_entries - median);
|
||||
int index = node_put(current, &next->entries[median]);
|
||||
memset(&next->entries[median], 0x00, sizeof(struct entry));
|
||||
next->nr_entries--;
|
||||
node_shift_children(current, index + 1, 1);
|
||||
|
||||
int n3_id = tree_alloc_id(tree);
|
||||
|
||||
current->children[index + 1] = n3_id;
|
||||
node_write(tree->fp, current_id, current);
|
||||
node_write(tree->fp, next_id, next);
|
||||
node_write(tree->fp, n3_id, &n3);
|
||||
|
||||
if (n3_is_next) {
|
||||
memcpy(current, &n3, sizeof n3);
|
||||
current_id = n3_id;
|
||||
}
|
||||
else {
|
||||
SWAP(struct node *, current, next);
|
||||
current_id = next_id;
|
||||
}
|
||||
|
||||
depth++;
|
||||
leaf = node_is_leaf(current);
|
||||
#endif
|
||||
}
|
||||
#if 0
|
||||
node_put(current, e);
|
||||
node_write(tree->fp, current_id, current);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
21
src/b-tree.h
21
src/b-tree.h
@@ -2,6 +2,9 @@
|
||||
#define B_TREE_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <blue/core/queue.h>
|
||||
|
||||
#define B_TREE_INVALID_PTR -1
|
||||
|
||||
struct b_tree;
|
||||
typedef void *b_tree_node;
|
||||
@@ -11,19 +14,27 @@ struct b_tree_ops {
|
||||
size_t node_size;
|
||||
size_t entry_size;
|
||||
|
||||
int (*tree_get_node)(struct b_tree *, int, void *);
|
||||
int (*tree_put_node)(struct b_tree *, int, void *);
|
||||
int (*tree_get_node)(struct b_tree *, long, void *);
|
||||
int (*tree_put_node)(struct b_tree *, long, void *);
|
||||
int (*tree_alloc_node)(struct b_tree *);
|
||||
|
||||
int (*node_get_nr_entries)(b_tree_node *);
|
||||
b_tree_node_entry *(*node_get_entry)(b_tree_node *, int);
|
||||
int (*node_get_child)(b_tree_node *, int);
|
||||
long (*node_get_nr_entries)(b_tree_node *);
|
||||
b_tree_node_entry *(*node_get_entry)(b_tree_node *, unsigned long);
|
||||
void (*node_set_entry)(b_tree_node *, int, const b_tree_node_entry *);
|
||||
long (*node_get_child)(b_tree_node *, unsigned long);
|
||||
int (*node_set_child)(b_tree_node *, unsigned long, long);
|
||||
|
||||
int (*entry_compare)(b_tree_node_entry *, b_tree_node_entry *);
|
||||
};
|
||||
|
||||
struct b_tree {
|
||||
const struct b_tree_ops *tree_ops;
|
||||
b_queue tree_cache;
|
||||
unsigned int tree_order;
|
||||
long tree_root;
|
||||
};
|
||||
|
||||
extern void b_tree_init(struct b_tree *tree, const struct b_tree_ops *ops, unsigned int order);
|
||||
extern int b_tree_put(struct b_tree *tree, const b_tree_node_entry *entry);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user