diff --git a/src/b-tree.c b/src/b-tree.c index e69de29..c075c2d 100644 --- a/src/b-tree.c +++ b/src/b-tree.c @@ -0,0 +1,247 @@ +#include + +#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; +} \ No newline at end of file diff --git a/src/b-tree.h b/src/b-tree.h index 29b2665..8471a9c 100644 --- a/src/b-tree.h +++ b/src/b-tree.h @@ -2,6 +2,9 @@ #define B_TREE_H_ #include +#include + +#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