-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhbtree.c
More file actions
238 lines (193 loc) · 6.63 KB
/
hbtree.c
File metadata and controls
238 lines (193 loc) · 6.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
#include "hbtree.h"
#include "common.h"
#include <sys/mman.h>
static const void *leaf_to_insert;
static void (*callback_to_call)(uint64_t, const void *);
static const union hbtree_node zeroed_node __attribute__((aligned(4096)));
static inline union hbtree_node *alloc_node(void)
{
void *const node = mmap(NULL, sizeof(union hbtree_node),
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (unlikely(node == MAP_FAILED)) {
log_errno("Cannot allocate hbtree node");
return NULL;
}
return node;
}
static inline bool free_node(union hbtree_node *const node)
{
if (unlikely(munmap(node, sizeof(union hbtree_node)))) {
log_errno("Cannot unmap hbtree node");
return false;
}
return true;
}
static bool insert(union hbtree_node *const node, const uint64_t width, const uint64_t idx, const uint64_t key_offset)
{
if (width == 1) {
node->leaf_data[idx] = leaf_to_insert;
return true;
}
if (!node->children[idx] && unlikely(!(node->children[idx] = alloc_node()))) {
return false;
}
const uint64_t narrowed_width = width / HBTREE_FANOUT;
return insert(node->children[idx], narrowed_width, key_offset / narrowed_width, key_offset % narrowed_width);
}
static const void *get(const union hbtree_node *const node, const uint64_t width, const uint64_t idx, const uint64_t key_offset)
{
if (width == 1) {
return node->leaf_data[idx];
}
if (node->children[idx]) {
const uint64_t narrowed_width = width / HBTREE_FANOUT;
return get(node->children[idx], narrowed_width, key_offset / narrowed_width, key_offset % narrowed_width);
}
return NULL;
}
static void delete(union hbtree_node *const node, const uint64_t width, const uint64_t idx, const uint64_t key_offset)
{
if (width == 1) {
node->leaf_data[idx] = NULL;
return;
}
if (node->children[idx]) {
const uint64_t narrowed_width = width / HBTREE_FANOUT;
delete(node->children[idx], narrowed_width, key_offset / narrowed_width, key_offset % narrowed_width);
}
}
static bool cleanup(union hbtree_node *const node, const uint64_t width)
{
if (width == 1) {
return !memcmp(node, &zeroed_node, sizeof(union hbtree_node));
}
bool all_zero = true;
for (size_t idx = 0; idx < HBTREE_FANOUT; ++idx) {
if (node->children[idx]) {
if (cleanup(node->children[idx], width / HBTREE_FANOUT)) {
if (likely(free_node(node->children[idx]))) {
node->children[idx] = NULL;
} else {
all_zero = false;
}
} else {
all_zero = false;
}
}
}
return all_zero;
}
static void walk(union hbtree_node *const node, const uint64_t width, const uint64_t key_base)
{
if (width == 1) {
for (size_t leaf_idx = 0; leaf_idx < HBTREE_FANOUT; ++leaf_idx) {
if (node->leaf_data[leaf_idx]) {
callback_to_call(key_base + leaf_idx, node->leaf_data[leaf_idx]);
}
}
return;
}
for (size_t idx = 0; idx < HBTREE_FANOUT; ++idx) {
if (node->children[idx]) {
walk(node->children[idx], width / HBTREE_FANOUT, key_base + width * idx);
}
}
}
static bool destroy(union hbtree_node *const node, const uint64_t width, const uint64_t key_base)
{
if (width == 1) {
for (size_t leaf_idx = 0; leaf_idx < HBTREE_FANOUT; ++leaf_idx) {
if (node->leaf_data[leaf_idx]) {
if (callback_to_call) {
callback_to_call(key_base + leaf_idx, node->leaf_data[leaf_idx]);
}
node->leaf_data[leaf_idx] = NULL;
}
}
return true;
}
bool all_zero = true;
for (size_t idx = 0; idx < HBTREE_FANOUT; ++idx) {
if (node->children[idx]) {
if (destroy(node->children[idx], width / HBTREE_FANOUT, key_base + width * idx)) {
if (likely(free_node(node->children[idx]))) {
node->children[idx] = NULL;
} else {
all_zero = false;
}
} else {
all_zero = false;
}
}
}
return all_zero;
}
bool hbtree_init(struct hbtree *const tree, const uint8_t depth)
{
assert(depth >= 1 && depth <= 7);
if (unlikely(!(tree->top = alloc_node()))) {
return false;
}
tree->top_width = (const uint64_t []) {
1,
HBTREE_FANOUT,
HBTREE_FANOUT * HBTREE_FANOUT,
HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT,
HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT,
HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT,
HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT * HBTREE_FANOUT,
}[depth - 1];
return true;
}
bool hbtree_insert(struct hbtree *const tree, const uint64_t key, const void *const value)
{
assert(tree != NULL);
assert(tree->top_width != 0);
assert(tree->top != NULL);
assert(key < tree->top_width * HBTREE_FANOUT);
assert(value != NULL);
leaf_to_insert = value;
return insert(tree->top, tree->top_width, key / tree->top_width, key % tree->top_width);
}
const void *hbtree_get(const struct hbtree *const tree, const uint64_t key)
{
assert(tree != NULL);
assert(tree->top_width != 0);
assert(tree->top != NULL);
assert(key < tree->top_width * HBTREE_FANOUT);
return get(tree->top, tree->top_width, key / tree->top_width, key % tree->top_width);
}
void hbtree_delete(struct hbtree *const tree, const uint64_t key)
{
assert(tree != NULL);
assert(tree->top_width != 0);
assert(tree->top != NULL);
assert(key < tree->top_width * HBTREE_FANOUT);
delete(tree->top, tree->top_width, key / tree->top_width, key % tree->top_width);
}
void hbtree_cleanup(struct hbtree *tree)
{
assert(tree != NULL);
assert(tree->top_width != 0);
assert(tree->top != NULL);
cleanup(tree->top, tree->top_width);
}
void hbtree_walk(struct hbtree *tree, void (*const cb)(uint64_t, const void *))
{
assert(tree != NULL);
assert(tree->top_width != 0);
assert(tree->top != NULL);
assert(cb != NULL);
callback_to_call = cb;
walk(tree->top, tree->top_width, 0);
}
void hbtree_destroy(struct hbtree *tree, void (*const dtor)(uint64_t, const void *))
{
assert(tree != NULL);
assert(tree->top_width != 0);
assert(tree->top != NULL);
callback_to_call = dtor;
if (destroy(tree->top, tree->top_width, 0) && likely(free_node(tree->top))) {
tree->top = NULL;
}
}