1 parent
7936dbbf8c
commit
6127371920
2 files changed
+649
No files matched your search
+524
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* M*LIB - B+TREE module
|
||||
*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#ifndef __M_BPTREE_H
|
||||
#define __M_BPTREE_H
|
||||
|
||||
#include "m-core.h"
|
||||
|
||||
/* Define a B+tree of a given type, of size N.
|
||||
USAGE: BPTREE_DEF(name, type, N, [, oplist_of_the_type]) */
|
||||
#define BPTREE_DEF2(name, N, key_t, key_oplist, value_t, value_oplist) \
|
||||
BPTREEI_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, \
|
||||
M_C(name, _t), M_C(name, _node_t), M_C(name, _pit_t))
|
||||
|
||||
|
||||
/********************************** INTERNAL ************************************/
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define BPTREEI_NODE_CONTRACT(N, key_oplist, node, root) do { } while (0)
|
||||
#else
|
||||
# define BPTREEI_NODE_CONTRACT(N, key_oplist, node, root) do { \
|
||||
assert ((node) != NULL); \
|
||||
assert ((root) != NULL); \
|
||||
int num2 = (node)->num; \
|
||||
bool is_leaf2 = num2 <= 0; \
|
||||
num2 = num2 < 0 ? -num2 : num2; \
|
||||
if ((node) == (root)) { \
|
||||
assert( 0 <= num2 && num2 <= N); \
|
||||
if (num2 == 0) assert (is_leaf2); \
|
||||
} else { \
|
||||
int c2 = N / 2; \
|
||||
assert (c2 > 0); \
|
||||
assert (c2 <= num2 && num2 <= N); \
|
||||
} \
|
||||
for(int i2 = 1; i2 < num2 ; i2++) { \
|
||||
assert (M_GET_CMP key_oplist((node)->key[i2-1], (node)->key[i2]) < 0); \
|
||||
} \
|
||||
if ((node)->next != NULL) \
|
||||
assert (M_GET_CMP key_oplist((node)->key[num2-1], (node)->next->key[0]) < 0); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#define BPTREEI_CONTRACT(N, key_oplist, b) do { \
|
||||
assert (N >= 3); \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, (b)->root, (b)->root); \
|
||||
assert ((b)->root->next == NULL); \
|
||||
} while (0)
|
||||
|
||||
/* Max depth of the B+tree
|
||||
*/
|
||||
#define BPTREEI_MAX_STACK ((int)(CHAR_BIT*sizeof (size_t)))
|
||||
|
||||
#define BPTREEI_DEF2(name, N, key_t, key_oplist, value_t, value_oplist, tree_t, node_t, pit_t) \
|
||||
\
|
||||
/* Node of a B+TREE */ \
|
||||
typedef struct M_C(name, _node_s) { \
|
||||
int num; /* Abs=Number of keys. Sign <0 is leaf */ \
|
||||
key_t key[N+1]; /* We can temporary push one more key */ \
|
||||
struct M_C(name, _node_s) *next; /* next node */ \
|
||||
union kind_s { /* either value or pointer to other nodes */ \
|
||||
value_t value[N+1]; \
|
||||
struct M_C(name, _node_s) *node[N+2]; \
|
||||
} kind; \
|
||||
} *node_t; \
|
||||
\
|
||||
/* A B+TREE is just a pointer to the root node */ \
|
||||
typedef struct M_C(name, _s) { \
|
||||
node_t root; \
|
||||
} tree_t[1]; \
|
||||
\
|
||||
/* Parent Iterator */ \
|
||||
typedef struct M_C(name, _parent_it_s) { \
|
||||
int num; \
|
||||
node_t parent[BPTREEI_MAX_STACK]; \
|
||||
} pit_t[1]; \
|
||||
\
|
||||
/* Can be optimized later to alloc for leaf or for node */ \
|
||||
static inline node_t M_C(name, _new_node)(void) \
|
||||
{ \
|
||||
node_t n = M_GET_NEW key_oplist (struct M_C(name, _node_s)); \
|
||||
if (M_UNLIKELY (n == NULL)) { \
|
||||
M_MEMORY_FULL(sizeof (node_t)); \
|
||||
return NULL; \
|
||||
} \
|
||||
n->next = NULL; \
|
||||
n->num = 0; \
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _init)(tree_t b) \
|
||||
{ \
|
||||
b->root = M_C(name, _new_node)(); \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
static inline bool M_C(name, _is_leaf)(node_t n) \
|
||||
{ \
|
||||
return n->num <= 0; \
|
||||
} \
|
||||
\
|
||||
static inline int M_C(name, _get_num)(node_t n) \
|
||||
{ \
|
||||
int num = n->num; \
|
||||
num = num < 0 ? -num : num; \
|
||||
assert (num <= N); \
|
||||
return num; \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _clean)(tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
node_t next, n = b->root; \
|
||||
pit_t pit; \
|
||||
int np = 0; \
|
||||
/* Scan down the nodes */ \
|
||||
while (!M_C(name, _is_leaf)(n)) { \
|
||||
pit->parent[np++] = n; \
|
||||
assert (np <= BPTREEI_MAX_STACK); \
|
||||
n = n->kind.node[0]; \
|
||||
} \
|
||||
pit->parent[np++] = n; \
|
||||
assert (np <= BPTREEI_MAX_STACK); \
|
||||
/* Clean & free non root */ \
|
||||
for(int i = 0; i < np; i++) { \
|
||||
n = pit->parent[i]; \
|
||||
while (n != NULL) { \
|
||||
/* Clear key (& value for leaf) */ \
|
||||
int num = M_C(name, _get_num)(n); \
|
||||
bool is_leaf = M_C(name, _is_leaf)(n); \
|
||||
for(int j = 0; j < num; j++) { \
|
||||
M_GET_CLEAR key_oplist(n->key[i]); \
|
||||
if (is_leaf) { \
|
||||
M_GET_CLEAR value_oplist (n->kind.value[i]); \
|
||||
} \
|
||||
} \
|
||||
/* Next node of the same height */ \
|
||||
next = n->next; \
|
||||
if (i != 0) { \
|
||||
M_GET_DEL key_oplist (n); \
|
||||
} \
|
||||
n = next; \
|
||||
} \
|
||||
} \
|
||||
/* Clean root */ \
|
||||
b->root->num = 0; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _clear)(tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
M_C(name, _clean)(b); \
|
||||
M_GET_DEL key_oplist (b->root); \
|
||||
b->root = NULL; \
|
||||
} \
|
||||
\
|
||||
static inline bool M_C(name, _empty_p)(tree_t b) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
/* root shall be an empty leaf */ \
|
||||
return b->root->num == 0; \
|
||||
} \
|
||||
\
|
||||
static inline node_t M_C(name, _search_leaf)(pit_t pit, tree_t b, key_t key) \
|
||||
{ \
|
||||
node_t n = b->root; \
|
||||
int np = 0; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
|
||||
while (!M_C(name, _is_leaf)(n)) { \
|
||||
assert (np <= BPTREEI_MAX_STACK); \
|
||||
int i, hi = n->num; \
|
||||
assert (hi > 0); \
|
||||
/* Linear search is usually faster than binary search for \
|
||||
B+TREE (due to cache effect). If a binary tree is faster for \
|
||||
the choosen type and size , it simply means that the \
|
||||
size of B+TREE is too big (TBC) and shall be reduced. */ \
|
||||
for(i = 0; i < hi; i++) { \
|
||||
if (M_GET_CMP key_oplist (key, n->key[i]) < 0) \
|
||||
break; \
|
||||
} \
|
||||
pit->parent[np++] = n; \
|
||||
n = n->kind.node[i]; \
|
||||
assert (n != NULL); \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
|
||||
} \
|
||||
pit->num = np; \
|
||||
return n; \
|
||||
} \
|
||||
\
|
||||
static inline value_t *M_C(name, _get)(tree_t b, key_t key) \
|
||||
{ \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
pit_t pit; \
|
||||
node_t n = M_C(name, _search_leaf)(pit, b, key); \
|
||||
int cmp = 0; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, n, b->root); \
|
||||
for(int i = 0; cmp >= 0 && i < -n->num; i++) { \
|
||||
cmp = M_GET_CMP key_oplist (key, n->key[i]); \
|
||||
if (cmp == 0) \
|
||||
return &n->kind.value[i]; \
|
||||
} \
|
||||
return NULL; \
|
||||
} \
|
||||
\
|
||||
static inline int M_C(name, _search_and_insert_leaf)(node_t n, key_t key, value_t value) \
|
||||
{ \
|
||||
assert (M_C(name, _is_leaf)(n)); \
|
||||
int i, num = M_C(name, _get_num)(n); \
|
||||
assert (num <= N); \
|
||||
for(i = 0; i < num; i++) { \
|
||||
int cmp = M_GET_CMP key_oplist (key, n->key[i]); \
|
||||
if (M_UNLIKELY (cmp == 0)) { \
|
||||
M_GET_SET value_oplist (n->kind.value[i], value); \
|
||||
return i; \
|
||||
} else if (cmp < 0) { \
|
||||
/* Move tables to make space for insertion */ \
|
||||
memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(num-i)); \
|
||||
memmove(&n->kind.value[i+1], &n->kind.value[i], sizeof(value_t)*(num-i)); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
M_GET_INIT_SET key_oplist (n->key[i], key); \
|
||||
M_GET_INIT_SET value_oplist (n->kind.value[i], value); \
|
||||
n->num += -1; /* Increase num as num<0 for leaf */ \
|
||||
return i; \
|
||||
} \
|
||||
\
|
||||
static inline int M_C(name, _search_and_insert_node)(node_t n, key_t key) \
|
||||
{ \
|
||||
assert (!M_C(name, _is_leaf)(n)); \
|
||||
int i, num = M_C(name, _get_num)(n); \
|
||||
assert (num <= N); \
|
||||
for(i = 0; i < num; i++) { \
|
||||
int cmp = M_GET_CMP key_oplist (key, n->key[i]); \
|
||||
assert (cmp != 0); \
|
||||
if (cmp < 0) { \
|
||||
/* Move tables to make space for insertion */ \
|
||||
memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(num-i)); \
|
||||
memmove(&n->kind.node[i+1], &n->kind.node[i], sizeof(node_t)*(num-i+1)); \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
M_GET_INIT_SET key_oplist (n->key[i], key); \
|
||||
n->num += 1; \
|
||||
return i; \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _set_at)(tree_t b, key_t key, value_t value) \
|
||||
{ \
|
||||
pit_t pit; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
node_t leaf = M_C(name, _search_leaf)(pit, b, key); \
|
||||
/* Insert key into the leaf.*/ \
|
||||
/* NOTE: Even if there is N elements, we can still add one more.*/ \
|
||||
int i = M_C(name, _search_and_insert_leaf)(leaf, key, value); \
|
||||
/* Most likely case: leaf can accept key!*/ \
|
||||
int num = -leaf->num; \
|
||||
assert (num > 0); \
|
||||
if (M_LIKELY (num <= N)) { \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
return; \
|
||||
} \
|
||||
assert (num == N+1); \
|
||||
/* leaf is full: need to slip it in two */ \
|
||||
int nnum = (N + 1) / 2; \
|
||||
num = N + 1 - nnum; \
|
||||
node_t nleaf = M_C(name, _new_node)(); \
|
||||
/* Move half objects to the new node */ \
|
||||
memmove(&nleaf->key[0], &leaf->key[num], sizeof(key_t)*nnum); \
|
||||
memmove(&nleaf->kind.value[0], &leaf->kind.value[num], sizeof(value_t)*nnum); \
|
||||
leaf->num = -num; \
|
||||
nleaf->num = -nnum; \
|
||||
nleaf->next = leaf->next; \
|
||||
leaf->next = nleaf; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, leaf, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, nleaf, b->root); \
|
||||
/* Update parent to inject *key_ptr which split between (leaf, nleaf) */ \
|
||||
key_t *key_ptr = &nleaf->key[0]; \
|
||||
while (true) { \
|
||||
if (pit->num == 0) { \
|
||||
/* We reach root. Need to increase the height of the tree.*/ \
|
||||
node_t parent = M_C(name, _new_node)(); \
|
||||
parent->num = 1; \
|
||||
parent->key[0] = *key_ptr; \
|
||||
parent->kind.node[0] = leaf; \
|
||||
parent->kind.node[1] = nleaf; \
|
||||
b->root = parent; \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
return; \
|
||||
} \
|
||||
/* Non root node. Get parent */ \
|
||||
node_t parent = pit->parent[--pit->num]; \
|
||||
/* Insert into parent (It is big enough to receive temporary one more) */ \
|
||||
i = M_C(name, _search_and_insert_node)(parent, *key_ptr); \
|
||||
parent->kind.node[i] = leaf; \
|
||||
parent->kind.node[i+1] = nleaf; \
|
||||
if (M_LIKELY (parent->num <= N)) { \
|
||||
BPTREEI_CONTRACT(N, key_oplist, b); \
|
||||
return; /* No need to split parent.*/ \
|
||||
} \
|
||||
assert (parent->num == N+1); \
|
||||
/* Need to split parent in {np} {1} {nnp} */ \
|
||||
int nnp = N / 2; \
|
||||
int np = N - nnp; \
|
||||
assert (nnp > 0 && np > 0 && nnp+np+1 == N+1); \
|
||||
node_t nparent = M_C(name, _new_node)(); \
|
||||
/* Move half values to new node (Like a classic B-TREEE) \
|
||||
and the median key to the grand-parent*/ \
|
||||
memmove(&nparent->key[0], &parent->key[np+1], sizeof(key_t)*nnp); \
|
||||
memmove(&nparent->kind.node[0], &parent->kind.node[np+1], sizeof(node_t)*(nnp+1)); \
|
||||
parent->num = np; \
|
||||
nparent->num = nnp; \
|
||||
nparent->next = parent->next; \
|
||||
parent->next = nparent; \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, parent, b->root); \
|
||||
BPTREEI_NODE_CONTRACT(N, key_oplist, nparent, b->root); \
|
||||
/* Prepare for the next step */ \
|
||||
key_ptr = &parent->key[np]; \
|
||||
leaf = parent; \
|
||||
nleaf = nparent; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
static inline int M_C(name, _search_and_remove_leaf)(node_t n, key_t key) \
|
||||
{ \
|
||||
assert(M_C(name, _is_leaf)(n)); \
|
||||
int num = M_C(name, _get_num)(n); \
|
||||
for(int i = 0; i < num; i++) { \
|
||||
int cmp = M_GET_CMP key_oplist(key, n->key[i]); \
|
||||
if (cmp == 0) { \
|
||||
M_GET_CLEAR key_oplist (n->key[i]); \
|
||||
M_GET_CLEAR value_oplist (n->kind.value[i]); \
|
||||
memmove(&n->key[i], &n->key[i+1], sizeof(key_t)*(num-1-i)); \
|
||||
memmove(&n->kind.value[i], &n->kind.value[i+1], sizeof(value_t)*(num-1-i)); \
|
||||
n->num -= -1; /* decrease number as num is < 0 */ \
|
||||
return i; \
|
||||
} \
|
||||
} \
|
||||
return -1; /* Not found */ \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _left_shift)(node_t parent, int k) \
|
||||
{ \
|
||||
assert (parent != NULL && !M_C(name, _is_leaf)(parent)); \
|
||||
assert (0 <= k && k < M_C(name, _get_num)(parent)); \
|
||||
node_t left = parent->kind.node[k]; \
|
||||
node_t right = parent->kind.node[k+1]; \
|
||||
assert (left != NULL && right != NULL); \
|
||||
int num_left = M_C(name, _get_num)(left); \
|
||||
int num_right = M_C(name, _get_num)(right); \
|
||||
assert (num_left > N/2); \
|
||||
assert (num_right < N/2); \
|
||||
/* Move one item from the left node to the right node */ \
|
||||
memmove(&right->key[1], &right->key[0], sizeof(key_t)*num_right); \
|
||||
if (M_C(name, _is_leaf)(left)) { \
|
||||
memmove (&right->kind.value[1], &right->kind.value[0], sizeof(value_t)*num_right); \
|
||||
memmove (&right->key[0], &left->key[num_left-1], sizeof (key_t)); \
|
||||
memmove (&right->kind.value[0], &left->kind.value[num_left-1], sizeof (value_t)); \
|
||||
right->num = -num_right - 1; \
|
||||
left->num = -num_left + 1; \
|
||||
M_GET_SET key_oplist (parent->key[k], right->key[0]); \
|
||||
} else { \
|
||||
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 */ \
|
||||
memmove(&right->key[0], &parent->key[k], sizeof(key_t)); \
|
||||
right->kind.node[0] = left->kind.node[num_left]; \
|
||||
right->num = num_right + 1; \
|
||||
left->num = num_left - 1; \
|
||||
/* left[n-1] is move to parent[k] (clear). left[n-1] is therefore clear */ \
|
||||
memmove(&parent->key[k], &left->key[num_left-1], sizeof (key_t)); \
|
||||
} \
|
||||
assert (right->num != 0); \
|
||||
assert (left->num != 0); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _right_shift)(node_t parent, int k) \
|
||||
{ \
|
||||
assert (parent != NULL && !M_C(name, _is_leaf)(parent)); \
|
||||
assert (0 <= k && k < M_C(name, _get_num)(parent)); \
|
||||
node_t left = parent->kind.node[k]; \
|
||||
node_t right = parent->kind.node[k+1]; \
|
||||
assert (left != NULL && right != NULL); \
|
||||
int num_left = M_C(name, _get_num)(left); \
|
||||
int num_right = M_C(name, _get_num)(right); \
|
||||
assert (num_left < N/2); \
|
||||
assert (num_right > N/2); \
|
||||
/* Move one item from right to left. */ \
|
||||
if (M_C(name, _is_leaf)(right)) { \
|
||||
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 (&left->kind.value[num_left], &right->kind.value[0], sizeof (value_t)); \
|
||||
memmove (&right->kind.value[0], &right->kind.value[1], sizeof(value_t)*(num_right-1)); \
|
||||
right->num = -num_right + 1; \
|
||||
left->num = -num_left - 1; \
|
||||
M_GET_SET key_oplist (parent->key[k], right->key[0]); \
|
||||
} else { \
|
||||
memmove (&left->key[num_left], &parent->key[k], sizeof (key_t)); \
|
||||
memmove (&parent->key[k], &right->key[0], sizeof (key_t)); \
|
||||
memmove (&right->key[0], &right->key[1], sizeof(key_t)*(num_right-1)); \
|
||||
left->kind.node[num_left+1] = right->kind.node[0]; \
|
||||
memmove (&right->kind.node[0], &right->kind.node[1], sizeof(node_t)*num_right); \
|
||||
right->num = num_right - 1; \
|
||||
left->num = num_left + 1; \
|
||||
} \
|
||||
assert (right->num != 0); \
|
||||
assert (left->num != 0); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _merge_node)(node_t parent, int k, bool leaf) \
|
||||
{ \
|
||||
assert (parent != NULL && !M_C(name, _is_leaf)(parent)); \
|
||||
assert (0 <= k && k < M_C(name, _get_num(parent))); \
|
||||
node_t left = parent->kind.node[k]; \
|
||||
node_t right = parent->kind.node[k+1]; \
|
||||
assert (left != NULL && right != NULL); \
|
||||
int num_parent = M_C(name, _get_num)(parent); \
|
||||
int num_left = M_C(name, _get_num)(left); \
|
||||
int num_right = M_C(name, _get_num)(right); \
|
||||
\
|
||||
if (leaf) { \
|
||||
assert (num_left + num_right <= N); \
|
||||
memmove(&left->key[num_left], &right->key[0], sizeof(key_t)*num_right); \
|
||||
memmove(&left->kind.value[num_left], &right->kind.value[0], sizeof(value_t)*num_right); \
|
||||
left->num = -num_left - num_right; \
|
||||
} else { \
|
||||
assert (num_left + num_right + 1 <= N); \
|
||||
memmove(&left->key[num_left+1], &right->key[0], sizeof(key_t)*num_right); \
|
||||
memmove(&left->kind.node[num_left+1], &right->kind.node[0], sizeof(node_t)*(num_right+1)); \
|
||||
left->key[num_left] = parent->key[k]; \
|
||||
left->num = num_left + 1 + num_right; \
|
||||
} \
|
||||
left->next = right->next; \
|
||||
M_GET_DEL key_oplist (right); \
|
||||
/* remove k'th key from the parent */ \
|
||||
M_GET_CLEAR key_oplist (parent->key[k]); \
|
||||
memmove(&parent->key[k], &parent->key[k+1], sizeof(key_t)*(num_parent - k - 1)); \
|
||||
memmove(&parent->kind.node[k+1], &parent->kind.node[k+2], sizeof(node_t)*(num_parent - k -1)); \
|
||||
parent->num --; \
|
||||
} \
|
||||
\
|
||||
/* We can also cache the index when we descend the tree. \
|
||||
TODO: Bench if it worth it.*/ \
|
||||
static inline int M_C(name, _search_node)(node_t parent, node_t child) \
|
||||
{ \
|
||||
assert (!M_C(name, _is_leaf)(parent)); \
|
||||
int num = M_C(name, _get_num)(parent); \
|
||||
for(int i = 0; i <= num; i++) { \
|
||||
if (parent->kind.node[i] == child) \
|
||||
return i; \
|
||||
} \
|
||||
assert(false); \
|
||||
} \
|
||||
\
|
||||
static inline void M_C(name, _remove)(tree_t b, key_t key) \
|
||||
{ \
|
||||
pit_t pit; \
|
||||
node_t leaf = M_C(name, _search_leaf)(pit, b, key); \
|
||||
int k = M_C(name, _search_and_remove_leaf)(leaf, key); \
|
||||
/* If not found, or number of keys greater than N>2 or root */ \
|
||||
if (M_LIKELY (M_C(name, _get_num)(leaf) >= N/2) || k < 0 || pit->num == 0) return; \
|
||||
/* Leaf is too small. Needs rebalancing */ \
|
||||
assert (M_C(name, _get_num)(leaf) == N/2-1); \
|
||||
bool pass1 = true; \
|
||||
while (true) { \
|
||||
assert (pit->num > 0); \
|
||||
/* Search for node 'leaf' in parent */ \
|
||||
node_t parent = pit->parent[--pit->num]; \
|
||||
assert (parent != NULL); \
|
||||
k = M_C(name, _search_node)(parent, leaf); \
|
||||
/* Look for the neighboor of the removed key. */ \
|
||||
/* if we can still one key to keep our node balanced */ \
|
||||
if (k > 0 && M_C(name, _get_num)(parent->kind.node[k-1]) > N/2) { \
|
||||
M_C(name, _left_shift)(parent, k-1); \
|
||||
return; \
|
||||
} else if (k < M_C(name, _get_num)(parent) \
|
||||
&& M_C(name, _get_num)(parent->kind.node[k+1]) > N/2) { \
|
||||
M_C(name, _right_shift)(parent, k); \
|
||||
return; \
|
||||
} \
|
||||
/* Merge both nodes, removing 'k' from parent */ \
|
||||
if (k == M_C(name, _get_num)(parent)) \
|
||||
k--; \
|
||||
assert(k >= 0 && k < M_C(name, _get_num)(parent)); \
|
||||
/* Merge 'k' & 'k+1' & remove 'k' from parent */ \
|
||||
M_C(name, _merge_node)(parent, k, pass1); \
|
||||
/* Check if we need to backport */ \
|
||||
if (M_C(name, _get_num)(parent) >= N/2) return; \
|
||||
if (pit->num == 0) { \
|
||||
/* We reach the root */ \
|
||||
if (M_C(name, _get_num)(parent) == 0) { \
|
||||
/* Update root (deleted) */ \
|
||||
b->root = parent->kind.node[0]; \
|
||||
M_GET_DEL key_oplist (parent); \
|
||||
} \
|
||||
return; \
|
||||
} \
|
||||
/* Next iteration */ \
|
||||
leaf = parent; \
|
||||
pass1 = false; \
|
||||
} \
|
||||
} \
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2017-2018, Patrick Pelissier
|
||||
* All rights reserved.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* + Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* + Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "m-bptree.h"
|
||||
#include "coverage.h"
|
||||
|
||||
START_COVERAGE
|
||||
BPTREE_DEF2(btree, 3, int, M_DEFAULT_OPLIST, int, M_DEFAULT_OPLIST)
|
||||
END_COVERAGE
|
||||
|
||||
static void test1(void)
|
||||
{
|
||||
btree_t b;
|
||||
btree_init(b);
|
||||
assert (btree_empty_p(b));
|
||||
btree_set_at(b, 12, 34);
|
||||
assert (!btree_empty_p(b));
|
||||
assert (btree_get(b, 12) != NULL);
|
||||
assert (*btree_get(b, 12) == 34);
|
||||
btree_set_at(b, 11, 35);
|
||||
assert (!btree_empty_p(b));
|
||||
assert (btree_get(b, 11) != NULL);
|
||||
assert (*btree_get(b, 11) == 35);
|
||||
btree_set_at(b, 1, 36);
|
||||
assert (!btree_empty_p(b));
|
||||
assert (btree_get(b, 1) != NULL);
|
||||
assert (*btree_get(b, 1) == 36);
|
||||
btree_set_at(b, 25, 37);
|
||||
assert (!btree_empty_p(b));
|
||||
assert (btree_get(b, 25) != NULL);
|
||||
assert (*btree_get(b, 25) == 37);
|
||||
btree_set_at(b, -25, 38);
|
||||
assert (btree_get(b, -25) != NULL);
|
||||
assert (*btree_get(b, -25) == 38);
|
||||
btree_set_at(b, 5, 39);
|
||||
assert (*btree_get(b, -25) == 38);
|
||||
btree_set_at(b, 5, 40);
|
||||
assert (*btree_get(b, 5) == 40);
|
||||
btree_set_at(b, -5, 41);
|
||||
assert (*btree_get(b, -5) == 41);
|
||||
for(int i = -1000; i < 1000; i+=3) {
|
||||
btree_set_at(b, i, 2*i);
|
||||
assert (btree_get(b, i) != NULL);
|
||||
assert (*btree_get(b, i) == 2*i);
|
||||
}
|
||||
for(int i = -1000+1; i < 1000; i+=3) {
|
||||
btree_set_at(b, i, 2*i);
|
||||
assert (btree_get(b, i) != NULL);
|
||||
assert (*btree_get(b, i) == 2*i);
|
||||
}
|
||||
for(int i = -1000+2; i < 1000; i+=3) {
|
||||
btree_set_at(b, i, 2*i);
|
||||
assert (btree_get(b, i) != NULL);
|
||||
assert (*btree_get(b, i) == 2*i);
|
||||
}
|
||||
for(int i = -1000; i < 1000; i++) {
|
||||
assert (btree_get(b, i) != NULL);
|
||||
assert (*btree_get(b, i) == 2*i);
|
||||
}
|
||||
btree_remove(b, 0);
|
||||
assert (btree_get(b, 0) == NULL);
|
||||
btree_remove(b, 1);
|
||||
assert (btree_get(b, 1) == NULL);
|
||||
btree_remove(b, 2);
|
||||
assert (btree_get(b, 2) == NULL);
|
||||
btree_remove(b, 0);
|
||||
assert (btree_get(b, 0) == NULL);
|
||||
btree_remove(b, -1);
|
||||
assert (btree_get(b, -1) == NULL);
|
||||
btree_remove(b, -2);
|
||||
assert (btree_get(b, -2) == NULL);
|
||||
btree_remove(b, 3);
|
||||
assert (btree_get(b, 3) == NULL);
|
||||
btree_remove(b, 4);
|
||||
assert (btree_get(b, 4) == NULL);
|
||||
btree_remove(b, -3);
|
||||
assert (btree_get(b, -3) == NULL);
|
||||
btree_remove(b, 5);
|
||||
assert (btree_get(b, 5) == NULL);
|
||||
btree_remove(b, -5);
|
||||
assert (btree_get(b, -5) == NULL);
|
||||
btree_remove(b, -4);
|
||||
assert (btree_get(b, -4) == NULL);
|
||||
btree_remove(b, 6);
|
||||
assert (btree_get(b, 6) == NULL);
|
||||
btree_remove(b, 7);
|
||||
assert (btree_get(b, 7) == NULL);
|
||||
for(int i = -1000; i < 0; i++) {
|
||||
btree_remove(b, i);
|
||||
assert (btree_get(b, i) == NULL);
|
||||
}
|
||||
for(int i = 0; i < 1000; i++) {
|
||||
btree_remove(b, i);
|
||||
assert (btree_get(b, i) == NULL);
|
||||
}
|
||||
btree_clear(b);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test1();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
Reference in new issue
Block a user