Update comments, function name and prepare for multimap / multiset
This commit is contained in:
1 parent
32b3e21564
commit
6408b28513
1 file changed
+75
-30
+75
-30
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
#include "m-core.h"
|
#include "m-core.h"
|
||||||
|
|
||||||
/* Define a B+tree which maps a key to a value, of size N
|
/* Define a B+tree of size 'N' that maps a 'key' to a 'value'
|
||||||
with its associated functions.
|
with its associated functions.
|
||||||
USAGE:
|
USAGE:
|
||||||
BPTREE_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
|
BPTREE_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
|
||||||
@@ -49,6 +49,31 @@
|
|||||||
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
|
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 0, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
|
||||||
|
|
||||||
|
|
||||||
|
/* Define a B+tree of size 'N' that maps a 'key' to a 'value',
|
||||||
|
allowing multiple equal keys to exist,
|
||||||
|
with its associated functions.
|
||||||
|
USAGE:
|
||||||
|
BPTREE_MULTI_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
|
||||||
|
OR
|
||||||
|
BPTREE_MULTI_DEF2(name, N, key_t, value_t)
|
||||||
|
*/
|
||||||
|
#define BPTREE_MULTI_DEF2(name, N, key_type, ...) \
|
||||||
|
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||||
|
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
|
||||||
|
(name, N, key_type, __VA_ARGS__, 1, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t))))
|
||||||
|
|
||||||
|
|
||||||
|
/* Define a B+tree of a given type, of size N.
|
||||||
|
allowing multiple equal keys to exist,
|
||||||
|
with its associated functions
|
||||||
|
USAGE: BPTREE_MULTI_DEF(name, N, type, [, oplist_of_the_type]) */
|
||||||
|
#define BPTREE_MULTI_DEF(name, N, ...) \
|
||||||
|
BPTREEI_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
||||||
|
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) ), \
|
||||||
|
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 1, M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t), M_C(name, _it_t) )))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Define the oplist of a rbtree of type (from BPTREE_DEF).
|
/* Define the oplist of a rbtree of type (from BPTREE_DEF).
|
||||||
USAGE: BPTREE_OPLIST(name [, oplist_of_the_type]) */
|
USAGE: BPTREE_OPLIST(name [, oplist_of_the_type]) */
|
||||||
#define BPTREE_OPLIST(...) \
|
#define BPTREE_OPLIST(...) \
|
||||||
@@ -205,12 +230,15 @@
|
|||||||
#define BPTREEI_DEF_P2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t) \
|
#define BPTREEI_DEF_P2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t) \
|
||||||
\
|
\
|
||||||
M_IF(isMap)( \
|
M_IF(isMap)( \
|
||||||
typedef struct M_C(name, _pair_s) { \
|
/* Type returned by the iterator. Due to having key and value \
|
||||||
key_t *key_ptr; \
|
separated in their own array in the node, it is pointers to \
|
||||||
value_t *value_ptr; \
|
the objects, not a global pointer to both objects. */ \
|
||||||
} M_C(name, _type_t); \
|
typedef struct M_C(name, _pair_s) { \
|
||||||
, \
|
key_t *key_ptr; \
|
||||||
typedef key_t M_C(name, _type_t); \
|
value_t *value_ptr; \
|
||||||
|
} M_C(name, _type_t); \
|
||||||
|
, \
|
||||||
|
typedef key_t M_C(name, _type_t); \
|
||||||
) \
|
) \
|
||||||
typedef key_t M_C(name, _key_type_t); \
|
typedef key_t M_C(name, _key_type_t); \
|
||||||
typedef value_t M_C(name, _value_type_t); \
|
typedef value_t M_C(name, _value_type_t); \
|
||||||
@@ -442,8 +470,8 @@
|
|||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline int \
|
static inline int \
|
||||||
M_C(name, _search_and_insert_in_leaf)(node_t n, key_t const key \
|
M_C(name, _search_and_insert_in_leaf)(node_t n, key_t const key \
|
||||||
M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
|
M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
|
||||||
{ \
|
{ \
|
||||||
assert (M_C(name, _is_leaf)(n)); \
|
assert (M_C(name, _is_leaf)(n)); \
|
||||||
int i, num = M_C(name, _get_num)(n); \
|
int i, num = M_C(name, _get_num)(n); \
|
||||||
@@ -451,7 +479,7 @@
|
|||||||
for(i = 0; i < num; i++) { \
|
for(i = 0; i < num; i++) { \
|
||||||
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
||||||
if (cmp <= 0) { \
|
if (cmp <= 0) { \
|
||||||
M_IF(isMulti)( /* Nothing to do */, \
|
M_IF(isMulti)( /* Nothing to do : fallthrough */, \
|
||||||
/* Update value if keys are equal */ \
|
/* Update value if keys are equal */ \
|
||||||
if (M_UNLIKELY (cmp == 0)) { \
|
if (M_UNLIKELY (cmp == 0)) { \
|
||||||
M_IF(isMap)(M_CALL_SET(value_oplist, n->kind.value[i], value);,) \
|
M_IF(isMap)(M_CALL_SET(value_oplist, n->kind.value[i], value);,) \
|
||||||
@@ -485,12 +513,15 @@
|
|||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
|
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
|
||||||
M_CALL_INIT_SET(key_oplist, n->key[i], key); \
|
M_CALL_INIT_SET(key_oplist, n->key[i], key); \
|
||||||
n->num += 1; \
|
n->num += 1; \
|
||||||
return i; \
|
return i; \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline void M_IF(isMap)(M_C(name, _set_at),M_C(name,_push))(tree_t b, key_t const key M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
|
static inline void \
|
||||||
|
M_IF(isMap)(M_C(name, _set_at),M_C(name,_push))(tree_t b, key_t const key \
|
||||||
|
M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
|
||||||
{ \
|
{ \
|
||||||
pit_t pit; \
|
pit_t pit; \
|
||||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||||
@@ -514,7 +545,9 @@
|
|||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
assert (num == N+1); \
|
assert (num == N+1); \
|
||||||
/* leaf is full: need to slip it in two */ \
|
\
|
||||||
|
/* Needs to rebalance the B+TREE */ \
|
||||||
|
/* leaf is full: need to slip the leaf in two */ \
|
||||||
int nnum = (N + 1) / 2; \
|
int nnum = (N + 1) / 2; \
|
||||||
num = N + 1 - nnum; \
|
num = N + 1 - nnum; \
|
||||||
node_t nleaf = M_C(name, _new_node)(); \
|
node_t nleaf = M_C(name, _new_node)(); \
|
||||||
@@ -527,13 +560,14 @@
|
|||||||
leaf->next = nleaf; \
|
leaf->next = nleaf; \
|
||||||
BPTREEI_NODE_CONTRACT(N, key_oplist, leaf, b->root); \
|
BPTREEI_NODE_CONTRACT(N, key_oplist, leaf, b->root); \
|
||||||
BPTREEI_NODE_CONTRACT(N, key_oplist, nleaf, b->root); \
|
BPTREEI_NODE_CONTRACT(N, key_oplist, nleaf, b->root); \
|
||||||
/* Update parent to inject *key_ptr which split between (leaf, nleaf) */ \
|
/* Update parent to inject *key_ptr that splits between (leaf, nleaf) */ \
|
||||||
key_t *key_ptr = &leaf->key[num-1]; \
|
key_t *key_ptr = &leaf->key[num-1]; \
|
||||||
while (true) { \
|
while (true) { \
|
||||||
if (pit->num == 0) { \
|
if (pit->num == 0) { \
|
||||||
/* We reach root. Need to increase the height of the tree.*/ \
|
/* We reach root ==> Need to increase the height of the tree.*/ \
|
||||||
node_t parent = M_C(name, _new_node)(); \
|
node_t parent = M_C(name, _new_node)(); \
|
||||||
parent->num = 1; \
|
parent->num = 1; \
|
||||||
|
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
|
||||||
M_CALL_INIT_SET(key_oplist, parent->key[0], *key_ptr); \
|
M_CALL_INIT_SET(key_oplist, parent->key[0], *key_ptr); \
|
||||||
parent->kind.node[0] = leaf; \
|
parent->kind.node[0] = leaf; \
|
||||||
parent->kind.node[1] = nleaf; \
|
parent->kind.node[1] = nleaf; \
|
||||||
@@ -541,18 +575,19 @@
|
|||||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
/* Non root node. Get parent */ \
|
/* Non root node. Get the parent node */ \
|
||||||
node_t parent = pit->parent[--pit->num]; \
|
node_t parent = pit->parent[--pit->num]; \
|
||||||
/* Insert into parent (It is big enough to receive temporary one more) */ \
|
/* Insert into parent (It is big enough to receive temporary one more) */ \
|
||||||
i = M_C(name, _search_and_insert_in_node)(parent, *key_ptr); \
|
i = M_C(name, _search_and_insert_in_node)(parent, *key_ptr); \
|
||||||
parent->kind.node[i] = leaf; \
|
parent->kind.node[i] = leaf; \
|
||||||
parent->kind.node[i+1] = nleaf; \
|
parent->kind.node[i+1] = nleaf; \
|
||||||
|
/* Test if parent node is full? */ \
|
||||||
if (M_LIKELY (parent->num <= N)) { \
|
if (M_LIKELY (parent->num <= N)) { \
|
||||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||||
return; /* No need to split parent.*/ \
|
return; /* No need to split parent.*/ \
|
||||||
} \
|
} \
|
||||||
assert (parent->num == N+1); \
|
assert (parent->num == N+1); \
|
||||||
/* Need to split parent in {np} {1} {nnp} */ \
|
/* Need to split parent in {np} {med} {nnp} */ \
|
||||||
int nnp = N / 2; \
|
int nnp = N / 2; \
|
||||||
int np = N - nnp; \
|
int np = N - nnp; \
|
||||||
assert (nnp > 0 && np > 0 && nnp+np+1 == N+1); \
|
assert (nnp > 0 && np > 0 && nnp+np+1 == N+1); \
|
||||||
@@ -574,13 +609,15 @@
|
|||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
\
|
\
|
||||||
static inline int M_C(name, _search_and_remove_leaf)(node_t n, key_t const key) \
|
static inline int \
|
||||||
|
M_C(name, _search_and_remove_in_leaf)(node_t n, key_t const key) \
|
||||||
{ \
|
{ \
|
||||||
assert(M_C(name, _is_leaf)(n)); \
|
assert(M_C(name, _is_leaf)(n)); \
|
||||||
int num = M_C(name, _get_num)(n); \
|
const int num = M_C(name, _get_num)(n); \
|
||||||
for(int i = 0; i < num; i++) { \
|
for(int i = 0; i < num; i++) { \
|
||||||
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
const int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
||||||
if (cmp == 0) { \
|
if (cmp == 0) { \
|
||||||
|
/* found key ==> delete it */ \
|
||||||
M_CALL_CLEAR(key_oplist, n->key[i]); \
|
M_CALL_CLEAR(key_oplist, n->key[i]); \
|
||||||
M_IF(isMap)(M_CALL_CLEAR(value_oplist, n->kind.value[i]);,) \
|
M_IF(isMap)(M_CALL_CLEAR(value_oplist, n->kind.value[i]);,) \
|
||||||
memmove(&n->key[i], &n->key[i+1], sizeof(key_t)*(num-1-i)); \
|
memmove(&n->key[i], &n->key[i+1], sizeof(key_t)*(num-1-i)); \
|
||||||
@@ -603,6 +640,7 @@
|
|||||||
int num_right = M_C(name, _get_num)(right); \
|
int num_right = M_C(name, _get_num)(right); \
|
||||||
assert (num_left > N/2); \
|
assert (num_left > N/2); \
|
||||||
assert (num_right < N/2); \
|
assert (num_right < N/2); \
|
||||||
|
\
|
||||||
/* Move one item from the left node to the right node */ \
|
/* Move one item from the left node to the right node */ \
|
||||||
memmove(&right->key[1], &right->key[0], sizeof(key_t)*num_right); \
|
memmove(&right->key[1], &right->key[0], sizeof(key_t)*num_right); \
|
||||||
if (M_C(name, _is_leaf)(left)) { \
|
if (M_C(name, _is_leaf)(left)) { \
|
||||||
@@ -614,7 +652,7 @@
|
|||||||
M_CALL_SET(key_oplist, parent->key[k], left->key[num_left-2]); \
|
M_CALL_SET(key_oplist, parent->key[k], left->key[num_left-2]); \
|
||||||
} else { \
|
} else { \
|
||||||
memmove(&right->kind.node[1], &right->kind.node[0], sizeof(node_t)*(num_right+1)); \
|
memmove(&right->kind.node[1], &right->kind.node[0], sizeof(node_t)*(num_right+1)); \
|
||||||
/* parent[k] is move to right[0] (clear). parent[k] is therefore clear */ \
|
/* parent[k] is moved to right[0] (clear). parent[k] is therefore clear */ \
|
||||||
memmove(&right->key[0], &parent->key[k], sizeof(key_t)); \
|
memmove(&right->key[0], &parent->key[k], sizeof(key_t)); \
|
||||||
right->kind.node[0] = left->kind.node[num_left]; \
|
right->kind.node[0] = left->kind.node[num_left]; \
|
||||||
right->num = num_right + 1; \
|
right->num = num_right + 1; \
|
||||||
@@ -637,7 +675,8 @@
|
|||||||
int num_right = M_C(name, _get_num)(right); \
|
int num_right = M_C(name, _get_num)(right); \
|
||||||
assert (num_left < N/2); \
|
assert (num_left < N/2); \
|
||||||
assert (num_right > N/2); \
|
assert (num_right > N/2); \
|
||||||
/* Move one item from right to left. */ \
|
\
|
||||||
|
/* Move one item from the right node to the left node. */ \
|
||||||
if (M_C(name, _is_leaf)(right)) { \
|
if (M_C(name, _is_leaf)(right)) { \
|
||||||
memmove (&left->key[num_left], &right->key[0], sizeof(key_t)); \
|
memmove (&left->key[num_left], &right->key[0], sizeof(key_t)); \
|
||||||
memmove (&right->key[0], &right->key[1], sizeof(key_t)*(num_right-1)); \
|
memmove (&right->key[0], &right->key[1], sizeof(key_t)*(num_right-1)); \
|
||||||
@@ -670,6 +709,7 @@
|
|||||||
int num_left = M_C(name, _get_num)(left); \
|
int num_left = M_C(name, _get_num)(left); \
|
||||||
int num_right = M_C(name, _get_num)(right); \
|
int num_right = M_C(name, _get_num)(right); \
|
||||||
\
|
\
|
||||||
|
/* Merge node 'k' and 'k+1' into a single one */ \
|
||||||
if (leaf) { \
|
if (leaf) { \
|
||||||
assert (num_left + num_right <= N); \
|
assert (num_left + num_right <= N); \
|
||||||
memmove(&left->key[num_left], &right->key[0], sizeof(key_t)*num_right); \
|
memmove(&left->key[num_left], &right->key[0], sizeof(key_t)*num_right); \
|
||||||
@@ -693,10 +733,11 @@
|
|||||||
\
|
\
|
||||||
/* We can also cache the index when we descend the tree. \
|
/* We can also cache the index when we descend the tree. \
|
||||||
TODO: Bench if this is worth the effort.*/ \
|
TODO: Bench if this is worth the effort.*/ \
|
||||||
static inline int M_C(name, _search_node)(node_t parent, node_t child) \
|
static inline int \
|
||||||
|
M_C(name, _search_for_node)(node_t parent, node_t child) \
|
||||||
{ \
|
{ \
|
||||||
assert (!M_C(name, _is_leaf)(parent)); \
|
assert (!M_C(name, _is_leaf)(parent)); \
|
||||||
int num = M_C(name, _get_num)(parent); \
|
const int num = M_C(name, _get_num)(parent); \
|
||||||
for(int i = 0; i <= num; i++) { \
|
for(int i = 0; i <= num; i++) { \
|
||||||
if (parent->kind.node[i] == child) \
|
if (parent->kind.node[i] == child) \
|
||||||
return i; \
|
return i; \
|
||||||
@@ -710,11 +751,14 @@
|
|||||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||||
pit_t pit; \
|
pit_t pit; \
|
||||||
node_t leaf = M_C(name, _search_for_leaf)(pit, b, key); \
|
node_t leaf = M_C(name, _search_for_leaf)(pit, b, key); \
|
||||||
int k = M_C(name, _search_and_remove_leaf)(leaf, key); \
|
int k = M_C(name, _search_and_remove_in_leaf)(leaf, key); \
|
||||||
/* If not found, or number of keys greater than N>2 or root */ \
|
/* If key is not found ==> erase failed */ \
|
||||||
if (k < 0) return false; \
|
if (k < 0) return false; \
|
||||||
|
/* Remove one item from the B+TREE */ \
|
||||||
b->size --; \
|
b->size --; \
|
||||||
if (M_LIKELY (M_C(name, _get_num)(leaf) >= N/2) || pit->num == 0) return true; \
|
/* If number of keys greater than N>2 or root ==> Nothing more to do */ \
|
||||||
|
if (M_LIKELY (M_C(name, _get_num)(leaf) >= N/2) || pit->num == 0) \
|
||||||
|
return true; \
|
||||||
/* Leaf is too small. Needs rebalancing */ \
|
/* Leaf is too small. Needs rebalancing */ \
|
||||||
assert (M_C(name, _get_num)(leaf) == N/2-1); \
|
assert (M_C(name, _get_num)(leaf) == N/2-1); \
|
||||||
bool pass1 = true; \
|
bool pass1 = true; \
|
||||||
@@ -723,7 +767,7 @@
|
|||||||
/* Search for node 'leaf' in parent */ \
|
/* Search for node 'leaf' in parent */ \
|
||||||
node_t parent = pit->parent[--pit->num]; \
|
node_t parent = pit->parent[--pit->num]; \
|
||||||
assert (parent != NULL); \
|
assert (parent != NULL); \
|
||||||
k = M_C(name, _search_node)(parent, leaf); \
|
k = M_C(name, _search_for_node)(parent, leaf); \
|
||||||
/* Look for the neighboor of the removed key. */ \
|
/* Look for the neighboor of the removed key. */ \
|
||||||
/* if we can steal one key from them to keep our node balanced */ \
|
/* if we can steal one key from them to keep our node balanced */ \
|
||||||
if (k > 0 && M_C(name, _get_num)(parent->kind.node[k-1]) > N/2) { \
|
if (k > 0 && M_C(name, _get_num)(parent->kind.node[k-1]) > N/2) { \
|
||||||
@@ -740,8 +784,9 @@
|
|||||||
assert(k >= 0 && k < M_C(name, _get_num)(parent)); \
|
assert(k >= 0 && k < M_C(name, _get_num)(parent)); \
|
||||||
/* Merge 'k' & 'k+1' & remove 'k' from parent */ \
|
/* Merge 'k' & 'k+1' & remove 'k' from parent */ \
|
||||||
M_C(name, _merge_node)(parent, k, pass1); \
|
M_C(name, _merge_node)(parent, k, pass1); \
|
||||||
/* Check if we need to backport */ \
|
/* Check if we need to continue */ \
|
||||||
if (M_C(name, _get_num)(parent) >= N/2) return true; \
|
if (M_C(name, _get_num)(parent) >= N/2) \
|
||||||
|
return true; \
|
||||||
if (pit->num == 0) { \
|
if (pit->num == 0) { \
|
||||||
/* We reach the root */ \
|
/* We reach the root */ \
|
||||||
if (M_C(name, _get_num)(parent) == 0) { \
|
if (M_C(name, _get_num)(parent) == 0) { \
|
||||||
|
|||||||
Reference in new issue
Block a user