summaryrefslogtreecommitdiff
path: root/source/tb_db/btree_impl.h
diff options
context:
space:
mode:
authornasr <nsrddyn@gmail.com>2026-04-13 14:58:49 +0200
committernasr <nsrddyn@gmail.com>2026-04-13 14:59:10 +0200
commita9cb228861a6b0fad4d508c05c0614757a7f0a34 (patch)
tree281ae48c7248413cae727b403a1cd802741b061d /source/tb_db/btree_impl.h
parent65907835d9835d85cff31269db19e18045cb3392 (diff)
refactor(main): refactor directory structuremain
Diffstat (limited to 'source/tb_db/btree_impl.h')
-rw-r--r--source/tb_db/btree_impl.h209
1 files changed, 209 insertions, 0 deletions
diff --git a/source/tb_db/btree_impl.h b/source/tb_db/btree_impl.h
new file mode 100644
index 0000000..b7015bd
--- /dev/null
+++ b/source/tb_db/btree_impl.h
@@ -0,0 +1,209 @@
1/**
2 * >> api notes
3 * >> by Abdellah El Morabit
4 *
5 * - creation of a btree happens when doing an insertion on an
6 * empty btree where the root is null or nil
7 *
8 **/
9
10/* single header b-tree implementation by Abdellah El Morabit */
11#ifndef BTREE_H
12#define BTREE_H
13
14// maximum height of the tree the lower the lower the lower amount
15// of disk reads which translates into faster?
16#if 0
17global_variable read_only s16 btree_ORDER = 4;
18#endif
19#define BTREE_ORDER 4
20
21//- NOTE(nasr): defining a key to improve sorting
22// i think saying that a key is a combination of the column + row is a good way of appraoching this
23typedef struct key key;
24struct key
25{
26 s32 header_index; //- col number
27 s32 row_index;
28};
29
30typedef struct btree_node btree_node;
31struct btree_node
32{
33 // store the key values of the sub nodes? if they are leaves?
34 key keys[BTREE_ORDER - 1];
35 // TODO(nasr): replace with something more generic?
36 // NOTE(nasr): cons of void * -> no type safety
37 // is there a way to still have some sort of that? size not variable
38 void *payload_per_key[BTREE_ORDER - 1];
39 btree_node *parent;
40 // handle to store children faster than linked list
41 // because now we can iterate over the nodes instead of having cache misses
42 // when jumping through linked nodes
43 btree_node *children[BTREE_ORDER];
44 // this shouldn't change things but could be a performance improvement on a bigger scale??
45 s32 *max_children;
46 // NOTE(nasr): reference count ::: check how many leaves are using this node
47 // also not needed for now because we don't free individual node because of arena allocator
48 // s32 *refc;
49 s32 key_count;
50 b32 leaf;
51
52
53 // NOTE(nasr): do we hold the reference to the arena? or do we pass is it as a reference?
54 // this could solve memory location issues?
55};
56
57
58typedef struct btree btree;
59struct btree
60{
61 // NOTE(nasr): make sure this always stays in memory
62 // so that initial fetch never requires a disk read
63 btree_node *root;
64};
65
66
67
68#ifdef BTREE_IMPLEMENTATION
69///////////////////////////////////////////////////////////////////////////////
70//- btree.c
71
72
73internal b32
74is_nil_btree_node(btree_node *node)
75{
76 // NOTE(nasr): looks like a useless function but could be usefull when we implement a nil_btree_node
77 return (node == NULL);
78}
79
80// TODO(nasr): 1. splitting the tree when getting too big? (horizontally) 2. joining trees?
81
82internal b8
83key_compare(key destination_key, key source_key)
84{
85 s32 source_index = source_key.header_index + source_key.row_index;
86 s32 destination_index = destination_key.header_index + destination_key.row_index;
87
88 if(destination_index > source_index) return 1;
89 else if(destination_index < source_index) return -1;
90 else return 0;
91}
92
93// NOTE(nasr): @return the index of the found element
94//- @param: start node could be the root node for all we care. but this enables optimizations in the future if we need them
95internal s32
96btree_find_ipos(key k, btree_node *start_node)
97{
98 s32 index = 0;
99 // scan right while the node's key at [index] is strictly less than k
100 while (index < start_node->key_count && key_compare(start_node->keys[index], k) < 0)
101 {
102 ++index;
103 }
104 return index;
105}
106
107// NOTE(nasr): nodes that get passed as parameters should've already been loaded into memory
108internal void *
109btree_search(btree_node *node, key key)
110{
111 s32 index = btree_find_ipos(key, node);
112
113 if (index < node->key_count && key_compare(node->keys[index], key) == 0)
114 {
115 return node->payload_per_key[index];
116 }
117 if (node->leaf)
118 {
119 return NULL;
120 }
121 return btree_search(node->children[index], key);
122}
123
124// TODO(nasr): split node when key_count == btree_ORDER - 1 (node is full)
125// -- each node has max btree_ORDER - 1 keys
126//
127internal void
128btree_insert(mem_arena *arena, btree *tree, key key, void *payload)
129{
130 if(is_nil_btree_node(tree->root))
131 {
132 tree->root = PushStructZero(arena, btree_node);
133 tree->root->leaf = 1;
134 tree->root->key_count = 0;
135 }
136
137 btree_node *current_node = tree->root;
138
139 if (current_node->leaf)
140 {
141 // find the insertion position and shift keys right to make room
142 //- after: but what is this. we are expecting a new key.but we are looking for
143 //- its position in the tree is useless because it's non existent
144
145 s32 i = btree_find_ipos(key, current_node);
146
147 for (s32 j = current_node->key_count; j > i; --j)
148 {
149 current_node->keys[j] = current_node->keys[j - 1];
150 current_node->payload_per_key[j] = current_node->payload_per_key[j - 1];
151 }
152
153 current_node->keys[i] = key;
154 current_node->payload_per_key[i] = payload;
155
156 if(current_node->key_count < (BTREE_ORDER - 1))
157 {
158 current_node->key_count += 1;
159 }
160 else {
161 // TODO(nasr): creating a new branch / tree?
162 // make a seperate function for this
163 // NOTE(nasr): only split through the parent when one actually exists;
164 // root splits need to be handled separately (promote mid key, create two children)
165 if(current_node->parent != NULL)
166 {
167 s32 split_pos = current_node->parent->key_count / 2 - 1;
168 s32 updated_keycount_p = 0;
169 for(s32 index = 0; index <= split_pos; ++index)
170 {
171 ++updated_keycount_p;
172 }
173 current_node->parent->key_count = updated_keycount_p;
174 }
175 }
176 }
177 // TODO(nasr): internal node case walk down then split on the way back up
178}
179
180
181internal void
182btree_write(btree *bt)
183{
184 unused(bt);
185#if 0
186 temp_arena *temp_arena = temp_arena_begin(arena);
187 btree_node *root = bt->root;
188
189 for(btree_node *bt = PushStruct(arena); root->next; next = root;)
190 {
191
192 }
193
194 temp_arena_end(temp);
195#endif
196}
197
198// - load the entire db into memory.
199#if 0
200internal void
201btree_load(mem_arena *arena)
202{
203
204
205}
206#endif
207
208#endif /* BTREE_IMPLEMENTATION */
209#endif /* BTREE_H */