/* * M*LIB - RED BLACK TREE module * * Copyright (c) 2017-2023, 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 MSTARLIB_RBTREE_H #define MSTARLIB_RBTREE_H #include "m-core.h" /* Define a Red/Black binary tree of a given type. USAGE: RBTREE_DEF(name, type [, oplist_of_the_type]) */ #define M_RBTREE_DEF(name, ...) \ M_RBTREE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) /* Define a Red/Black binary tree of a given type as the name name_t and the iterator it_t. USAGE: RBTREE_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */ #define M_RBTREE_DEF_AS(name, name_t, it_t, ...) \ M_BEGIN_PROTECTED_CODE \ M_RBTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, M_F(name, _node_ct), it_t ), \ (name, __VA_ARGS__, name_t, M_F(name, _node_ct), it_t ))) \ M_END_PROTECTED_CODE /* Define the oplist of a rbtree of type. USAGE: RBTREE_OPLIST(name [, oplist_of_the_type]) */ #define M_RBTREE_OPLIST(...) \ M_RBTR33_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ ((__VA_ARGS__, M_BASIC_OPLIST), \ (__VA_ARGS__ ))) /*****************************************************************************/ /********************************** INTERNAL *********************************/ /*****************************************************************************/ /* Deferred evaluation for the oplist definition, so that all arguments are evaluated before further expansion */ #define M_RBTR33_OPLIST_P1(arg) M_RBTR33_OPLIST_P2 arg /* Validation of the given oplist */ #define M_RBTR33_OPLIST_P2(name, oplist) \ M_IF_OPLIST(oplist)(M_RBTR33_OPLIST_P3, M_RBTR33_OPLIST_FAILURE)(name, oplist) /* Prepare a clean compilation failure */ #define M_RBTR33_OPLIST_FAILURE(name, oplist) \ ((M_LIB_ERROR(ARGUMENT_OF_RBTREE_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) /* OPLIST definition of a rbtree NOTE: IT_REF is not exported so that the container appears as not modifiable by algorithm.*/ #define M_RBTR33_OPLIST_P3(name, oplist) \ (INIT(M_F(name, _init)), \ INIT_SET(M_F(name, _init_set)), \ INIT_WITH(API_1(M_INIT_EMPLACE_VAI)), \ SET(M_F(name, _set)), \ CLEAR(M_F(name, _clear)), \ INIT_MOVE(M_F(name, _init_move)), \ MOVE(M_F(name, _move)), \ SWAP(M_F(name, _swap)), \ NAME(name), \ TYPE(M_F(name,_ct)), \ SUBTYPE(M_F(name, _subtype_ct)), \ EMPTY_P(M_F(name,_empty_p)), \ GET_SIZE(M_F(name, _size)), \ IT_TYPE(M_F(name, _it_ct)), \ IT_FIRST(M_F(name,_it)), \ IT_SET(M_F(name,_it_set)), \ IT_LAST(M_F(name,_it_last)), \ IT_END(M_F(name,_it_end)), \ IT_END_P(M_F(name,_end_p)), \ IT_LAST_P(M_F(name,_last_p)), \ IT_EQUAL_P(M_F(name,_it_equal_p)), \ IT_NEXT(M_F(name,_next)), \ IT_PREVIOUS(M_F(name,_previous)), \ IT_CREF(M_F(name,_cref)), \ IT_REMOVE(M_F(name,_remove)), \ RESET(M_F(name,_reset)), \ PUSH(M_F(name,_push)), \ GET_MIN(M_F(name,_min)), \ GET_MAX(M_F(name,_max)), \ M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),), \ M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),), \ M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),), \ M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),), \ M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),), \ M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),), \ M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),), \ M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \ ) /********************************** INTERNAL *********************************/ /* Max depth of the binary tree It is at worst twice the depth of a perfectly even tree with maximum elements. The maximum number of elements is the max of size_t. A perfectly even tree is of depth log2(max(size_t))=CHAR_BIT*sizeof(size_t) */ #define M_RBTR33_MAX_STACK (2*CHAR_BIT*sizeof (size_t)) /* Encapsulation of the color of the nodes. */ #define M_RBTR33_SET_RED(x) ((x)->color = M_RBTR33_RED) #define M_RBTR33_SET_BLACK(x) ((x)->color = M_RBTR33_BLACK) #define M_RBTR33_IS_RED(x) ((x)->color == M_RBTR33_RED) #define M_RBTR33_IS_BLACK(x) ((x)->color == M_RBTR33_BLACK) #define M_RBTR33_COPY_COLOR(x,y) ((x)->color = (y)->color) #define M_RBTR33_GET_COLOR(x) (true ? (x)->color : (x)->color) #define M_RBTR33_SET_COLOR(x, c) ((x)->color = (c)) #define M_RBTR33_GET_CHILD(x, n) ((x)->child[n]) #define M_RBTR33_SET_CHILD(x, n, y) ((x)->child[n] = (y)) // Color of a node of a Red/Black tree typedef enum { M_RBTR33_BLACK = 0, M_RBTR33_RED } m_rbtr33_color_e; // General contact of a Read/Black tree #define M_RBTR33_CONTRACT(tree) do { \ M_ASSERT ((tree) != NULL); \ M_ASSERT ((tree)->node == NULL || M_RBTR33_IS_BLACK((tree)->node)); \ M_ASSERT ((tree)->size != 0 || (tree)->node == NULL); \ } while (0) // Contract of a node (doesn't check for equal depth in black) #define M_RBTR33_CONTRACT_NODE(node) do { \ M_ASSERT((node) != NULL); \ M_ASSERT(M_RBTR33_IS_BLACK(node) || M_RBTR33_IS_RED(node)); \ M_ASSERT(M_RBTR33_IS_BLACK(node) \ || (((node)->child[0] == NULL || M_RBTR33_IS_BLACK(node->child[0])) \ && ((node)->child[1] == NULL || M_RBTR33_IS_BLACK(node->child[1])))); \ } while (0) /* Deferred evaluation for the rbtree definition, so that all arguments are evaluated before further expansion */ #define M_RBTR33_DEF_P1(arg) M_ID( M_RBTR33_DEF_P2 arg ) /* Validate the oplist before going further */ #define M_RBTR33_DEF_P2(name, type, oplist, tree_t, node_t, it_t) \ M_IF_OPLIST(oplist)(M_RBTR33_DEF_P3, M_RBTR33_DEF_FAILURE)(name, type, oplist, tree_t, node_t, it_t) /* Stop processing with a compilation failure */ #define M_RBTR33_DEF_FAILURE(name, type, oplist, tree_t, note_t, it_t) \ M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(RBTREE_DEF): the given argument is not a valid oplist: " #oplist) /* Internal rbtree definition - name: prefix to be used - type: type of the elements of the rbtree - oplist: oplist of the type of the elements of the container - tree_t: alias for the type of the container - it_t: alias for the iterator of the container - node_t: alias for the node of an element of the container */ #define M_RBTR33_DEF_P3(name, type, oplist, tree_t, node_t, it_t) \ M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \ M_RBTR33_DEF_MEMPOOL(name, type, oplist, tree_t, node_t, it_t) \ M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \ M_RBTR33_DEF_IO(name, type, oplist, tree_t, node_t, it_t) \ M_EMPLACE_QUEUE_DEF(name, tree_t, M_F(name, _emplace), oplist, M_EMPLACE_QUEUE_GENE) /* Define the types associated to a R/B Tree */ #define M_RBTR33_DEF_TYPE(name, type, oplist, tree_t, node_t, it_t) \ \ /* Node of Red/Black tree. \ Each node has up to two child, a color (Red or black) \ and the data stored in it */ \ typedef struct M_F(name, _node_s) { \ struct M_F(name, _node_s) *child[2]; \ type data; \ m_rbtr33_color_e color; \ } node_t; \ \ /* Define the Red/Black tree */ \ typedef struct M_F(name, _s) { \ size_t size; /* Number of elements in the tree */ \ node_t *node; /* Root node of the tree */ \ } tree_t[1]; \ typedef struct M_F(name, _s) *M_F(name, _ptr); \ typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ \ /* Iterator on a tree. The iterator stores the full path to the \ current node through all its parents and its depth */ \ typedef struct M_F(name, _it_s) { \ node_t *stack[M_RBTR33_MAX_STACK]; \ int8_t which[M_RBTR33_MAX_STACK]; \ unsigned int cpt; \ } it_t[1]; \ \ /* Definition of the alias used by the oplists */ \ typedef type M_F(name, _subtype_ct); \ typedef tree_t M_F(name, _ct); \ typedef it_t M_F(name, _it_ct); \ /* Define the mempool encapsulation */ #define M_RBTR33_DEF_MEMPOOL(name, type, oplist, tree_t, node_t, it_t) \ /* Link with fast memory allocator if requested */ \ M_IF_METHOD(MEMPOOL, oplist)( \ /* Definition of the memory pool of this kind of node */ \ MEMPOOL_DEF(M_F(name, _mempool), node_t) \ /* Definition of the global variable used to reference this pool */ \ M_GET_MEMPOOL_LINKAGE oplist M_F(name, _mempool_t) M_GET_MEMPOOL oplist; \ /* Allocator function */ \ M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \ return M_F(name, _mempool_alloc)(M_GET_MEMPOOL oplist); \ } \ /* Deallocator function */ \ M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \ M_F(name, _mempool_free)(M_GET_MEMPOOL oplist, ptr); \ } \ \ , /* No mempool allocation */ \ /* Classic Allocator function (common case) */ \ M_INLINE node_t *M_C3(m_rbtr33_,name,_new)(void) { \ return M_CALL_NEW(oplist, node_t); \ } \ /* Classic deallocator function (common case) */ \ M_INLINE void M_C3(m_rbtr33_,name,_del)(node_t *ptr) { \ M_CALL_DEL(oplist, ptr); \ } ) \ /* Define the core functions */ #define M_RBTR33_DEF_CORE(name, type, oplist, tree_t, node_t, it_t) \ \ M_INLINE void \ M_F(name, _init)(tree_t tree) \ { \ M_ASSERT (tree != NULL); \ tree->size = 0; \ tree->node = NULL; \ M_RBTR33_CONTRACT(tree); \ } \ \ M_INLINE void \ M_F(name, _reset)(tree_t tree) \ { \ M_RBTR33_CONTRACT(tree); \ node_t *stack[M_RBTR33_MAX_STACK]; \ unsigned int cpt = 0; \ /* If nothing (no node) nothing to clean: return */ \ if (tree->node == NULL) return; \ /* Parse all the tree */ \ stack[cpt++] = tree->node; \ while (cpt > 0) { \ node_t *n = stack[cpt-1]; \ /* Go down to the bottom left node that exists */ \ while (true) { \ M_RBTR33_CONTRACT_NODE(n); \ /* If there is a left child, get it */ \ if (n->child[0] != NULL) { \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ stack[cpt++] = n->child[0]; \ n = n->child[0]; \ stack[cpt-2]->child[0] = NULL; \ /* If there is a right child, get it */ \ } else if (n->child[1] != NULL) { \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ stack[cpt++] = n->child[1]; \ n = n->child[1]; \ stack[cpt-2]->child[1] = NULL; \ /* No left nor right child, node can be deleted */ \ } else { \ break; \ } \ } \ M_ASSERT (n == stack[cpt - 1]); \ /* Clear the bottom left node */ \ M_CALL_CLEAR(oplist, n->data); \ M_C3(m_rbtr33_,name,_del) (n); \ M_ASSERT((stack[cpt-1] = NULL) == NULL); \ /* Go up to the parent */ \ cpt--; \ } \ /* Mark the root node as empty */ \ tree->node = NULL; \ tree->size = 0; \ } \ \ M_INLINE void \ M_F(name, _clear)(tree_t tree) \ { \ /* Nothing more than clean the tree as everything is cleared */ \ M_F(name, _reset)(tree); \ } \ \ M_INLINE void \ M_F(name, _push)(tree_t tree, type const data) \ { \ M_RBTR33_CONTRACT(tree); \ node_t *tab[M_RBTR33_MAX_STACK]; \ int8_t which[M_RBTR33_MAX_STACK]; \ unsigned int cpt = 0; \ node_t *n = tree->node; \ /* If there is no root node, create a new node */ \ if (n == NULL) { \ n = M_C3(m_rbtr33_,name,_new)(); \ if (M_UNLIKELY_NOMEM (n == NULL)) { \ M_MEMORY_FULL(sizeof (node_t)); \ return; \ } \ /* Copy the data in the root node */ \ M_CALL_INIT_SET(oplist, n->data, data); \ /* Mark the root node as black */ \ n->child[0] = n->child[1] = NULL; \ M_RBTR33_SET_BLACK (n); \ tree->node = n; \ M_ASSERT(tree->size == 0); \ tree->size = 1; \ M_RBTR33_CONTRACT(tree); \ return; \ } \ /* Search for insertion point in the tree */ \ tab[cpt] = n; \ while (n != NULL) { \ M_RBTR33_CONTRACT_NODE(n); \ int cmp = M_CALL_CMP(oplist, n->data, data); \ if (cmp == 0) { \ /* key found ==> stop analysis */ \ break; \ } else { \ /* go left (if cmp > 0) or right (if cmp < 0) */ \ int s = (cmp < 0); \ which[cpt++] = (int8_t) s; \ n = n->child[s]; \ } \ /* We cannot overflow the max depth of a tree */ \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ tab[cpt] = n; \ } \ /* If found, update the data (default is set) */ \ if (n != NULL) { \ M_CALL_SET(oplist, n->data, data); \ M_RBTR33_CONTRACT (tree); \ return; \ } \ /* Create new node to store the data */ \ n = M_C3(m_rbtr33_,name,_new)(); \ if (M_UNLIKELY_NOMEM (n == NULL) ) { \ M_MEMORY_FULL (sizeof (node_t)); \ return; \ } \ /* Copy the data and mark the node as red */ \ M_CALL_INIT_SET(oplist, n->data, data); \ n->child[0] = n->child[1] = NULL; \ M_RBTR33_SET_RED (n); \ /* Add it in the iterator */ \ M_ASSERT (tab[cpt] == NULL); \ tab[cpt] = n; \ /* Add it in the tree */ \ tree->size ++; \ M_ASSERT(tab[cpt-1]->child[0+which[cpt-1]] == NULL); \ tab[cpt-1]->child[0+which[cpt-1]] = n; \ /* Fix the tree to still respect the red/back properties */ \ while (cpt >= 2 \ && M_RBTR33_IS_RED(tab[cpt-1]) \ && tab[cpt-2]->child[1-which[cpt-2]] != NULL \ && M_RBTR33_IS_RED(tab[cpt-2]->child[1-which[cpt-2]])) { \ M_RBTR33_SET_BLACK(tab[cpt-1]); \ M_RBTR33_SET_BLACK(tab[cpt-2]->child[1-which[cpt-2]]); \ M_RBTR33_SET_RED(tab[cpt-2]); \ cpt-=2; \ } \ /* root is always black */ \ M_RBTR33_SET_BLACK(tab[0]); \ if (cpt <= 1 || M_RBTR33_IS_BLACK(tab[cpt-1])) { \ M_RBTR33_CONTRACT (tree); \ return; \ } \ /* Read the grand-parent, the parent and the element */ \ node_t *pp = tab[cpt-2]; \ node_t *p = tab[cpt-1]; \ node_t *x = tab[cpt]; \ int i = which[cpt-2]; \ int j = 1 - i; \ M_ASSERT (i == 0 || i == 1); \ /* We need to do some rotations */ \ if (i == which[cpt-1]) { \ /* The child is the left child of its parent */ \ /* OR The child is the right child of its parent */ \ /* Right rotation: cpt is the new grand-parent. \ x is its left child, the grand-parent is the right one */ \ pp->child[i] = p->child[j]; \ p->child[i] = x; \ p->child[j] = pp; \ M_RBTR33_SET_BLACK(p); \ M_RBTR33_SET_RED(pp); \ } else { \ M_ASSERT (j == which[cpt-1]); \ /* The child is the right child of its parent */ \ /* OR The child is the left child of its parent */ \ /* Left rotation */ \ pp->child[i] = x->child[j]; \ p->child[j] = x->child[i]; \ x->child[i] = p; \ x->child[j] = pp; \ M_RBTR33_SET_BLACK(x); \ M_RBTR33_SET_RED(p); \ M_RBTR33_SET_RED(pp); \ p = x; \ } \ /* Insert the new grand parent */ \ if (cpt == 2) { \ tree->node = p; \ } else { \ M_ASSERT (cpt >= 3); \ tab[cpt-3]->child[which[cpt-3]] = p; \ } \ /* Done */ \ M_RBTR33_CONTRACT (tree); \ } \ \ M_INLINE size_t \ M_F(name, _size)(const tree_t tree) \ { \ M_RBTR33_CONTRACT (tree); \ return tree->size; \ } \ \ /* Set the iterator to the first (child=0) or last (child=1) element */ \ M_INLINE void \ M_C3(m_rbtr33_,name,_it)(it_t it, const tree_t tree, int child) \ { \ M_RBTR33_CONTRACT (tree); \ M_ASSERT (it != NULL); \ M_ASSERT (child == 0 || child == 1); \ unsigned int cpt = 0; \ if (tree->node != NULL) { \ it->which[cpt] = (int8_t) child; \ node_t *n = it->stack[cpt++] = tree->node; \ /* Go down the tree and fill in the iterator */ \ while (n->child[child] != NULL) { \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ n = n->child[child]; \ it->which[cpt] = (int8_t) child; \ it->stack[cpt++] = n; \ } \ M_ASSERT (n == it->stack[cpt - 1]); \ } \ it->cpt = cpt; \ } \ \ M_INLINE void \ M_F(name, _it)(it_t it, const tree_t tree) \ { \ M_C3(m_rbtr33_,name,_it)(it, tree, 0); \ } \ \ M_INLINE void \ M_F(name, _it_last)(it_t it, const tree_t tree) \ { \ M_C3(m_rbtr33_,name,_it)(it, tree, 1); \ } \ \ M_INLINE void \ M_F(name, _it_end)(it_t it, const tree_t tree) \ { \ M_RBTR33_CONTRACT (tree); \ M_ASSERT (it != NULL); \ (void) tree; /* parameter not used */ \ it->cpt = 0; \ } \ \ M_INLINE void \ M_F(name, _it_set)(it_t it, const it_t ref) \ { \ M_ASSERT (it != NULL && ref != NULL); \ *it = *ref; \ } \ \ M_INLINE bool \ M_F(name, _end_p)(const it_t it) \ { \ M_ASSERT (it != NULL); \ return it->cpt == 0; \ } \ \ /* Go to the next (child = 0)or previous element (child = 1) */ \ M_INLINE void \ M_C3(m_rbtr33_,name,_next)(it_t it, int child) \ { \ M_ASSERT (it != NULL); \ M_ASSERT (child == 0 || child == 1); \ if (it->cpt == 0) return; \ unsigned int cpt = it->cpt - 1; \ node_t *n = it->stack[cpt]; \ /* Get the other child */ \ const int right = 1 ^ child; \ if (n->child[right] != NULL) { \ /* Going right */ \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ n = n->child[right]; \ it->which[cpt++] = (int8_t) right; \ it->stack[cpt] = n; \ it->which[cpt++] = (int8_t) child; \ /* Going left */ \ while (n->child[child] != NULL) { \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ n = n->child[child]; \ it->which[cpt] = (int8_t) child; \ it->stack[cpt++] = n; \ } \ M_ASSERT (n == it->stack[cpt - 1]); \ } else { \ /* Going up */ \ while (cpt > 0 && it->which[cpt-1] == right) cpt--; \ } \ it->cpt = cpt; \ } \ \ M_INLINE void \ M_F(name, _next)(it_t it) \ { \ M_C3(m_rbtr33_,name,_next)(it, 0); \ } \ \ M_INLINE void \ M_F(name, _previous)(it_t it) \ { \ M_C3(m_rbtr33_,name,_next)(it, 1); \ } \ \ M_INLINE type * \ M_F(name, _ref)(const it_t it) \ { \ M_ASSERT(it != NULL); \ /* There shall be at least one element */ \ M_ASSERT_INDEX(it->cpt-1, M_RBTR33_MAX_STACK); \ /* NOTE: partially unsafe if the user modify the order of the el */ \ return &(it->stack[it->cpt-1]->data); \ } \ \ M_INLINE type const * \ M_F(name, _cref)(const it_t it) \ { \ return M_CONST_CAST(type, M_F(name, _ref)(it)); \ } \ \ M_INLINE bool \ M_F(name, _it_equal_p)(const it_t it1, const it_t it2) \ { \ M_ASSERT(it1 != NULL && it2 != NULL); \ /* There can be no element */ \ M_ASSERT_INDEX(it1->cpt, M_RBTR33_MAX_STACK); \ M_ASSERT_INDEX(it2->cpt, M_RBTR33_MAX_STACK); \ return it1->cpt == it2->cpt \ && (it1->cpt == 0 || it1->stack[it1->cpt-1] == it2->stack[it2->cpt-1]); \ } \ \ M_INLINE void \ M_F(name, _it_from)(it_t it, const tree_t tree, type const data) \ { \ M_RBTR33_CONTRACT (tree); \ M_ASSERT (it != NULL); \ unsigned int cpt = 0; \ int cmp = 1; \ node_t *n = tree->node; \ /* Find the lowest element greater or equal than data in the tree */ \ while (n != NULL) { \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ it->which[cpt] = 0; \ it->stack[cpt++] = n; \ cmp = M_CALL_CMP(oplist, n->data, data); \ if (cmp == 0) \ break; \ int child = (cmp < 0); \ it->which[cpt-1] = (int8_t) child; \ n = n->child[child]; \ } \ /* Save the iterator */ \ it->cpt = cpt; \ /* The iterator found may be strictly lower than data. \ In this case, go to the next element */ \ if (cmp < 0) { \ M_F(name, _next)(it); \ } \ } \ \ M_INLINE bool \ M_F(name, _it_until_p)(it_t it, type const data) \ { \ M_ASSERT (it != NULL); \ if (M_UNLIKELY(it->cpt == 0)) return true; \ M_ASSERT (it->cpt > 0 && it->cpt < M_RBTR33_MAX_STACK); \ node_t *n = it->stack[it->cpt-1]; \ int cmp = M_CALL_CMP(oplist, n->data, data); \ return (cmp >= 0); \ } \ \ M_INLINE bool \ M_F(name, _it_while_p)(it_t it, type const data) \ { \ M_ASSERT (it != NULL); \ if (M_UNLIKELY(it->cpt == 0)) return false; \ M_ASSERT (it->cpt > 0 && it->cpt < M_RBTR33_MAX_STACK); \ node_t *n = it->stack[it->cpt-1]; \ int cmp = M_CALL_CMP(oplist, n->data, data); \ return (cmp <= 0); \ } \ \ M_INLINE type * \ M_F(name, _min)(const tree_t tree) \ { \ M_RBTR33_CONTRACT (tree); \ node_t *n = tree->node; \ if (M_UNLIKELY (n == NULL) ) return NULL; \ while (n->child[0] != NULL) { \ M_RBTR33_CONTRACT_NODE (n); \ n = n->child[0]; \ } \ return &n->data; \ } \ \ M_INLINE type * \ M_F(name, _max)(const tree_t tree) \ { \ M_RBTR33_CONTRACT (tree); \ node_t *n = tree->node; \ if (M_UNLIKELY (n == NULL) ) return NULL; \ while (n->child[1] != NULL) { \ M_RBTR33_CONTRACT_NODE (n); \ n = n->child[1]; \ } \ return &n->data; \ } \ \ M_INLINE type const * \ M_F(name, _cmin)(const tree_t tree) \ { \ return M_CONST_CAST(type, M_F(name, _min)(tree)); \ } \ \ M_INLINE type const * \ M_F(name, _cmax)(const tree_t tree) \ { \ return M_CONST_CAST(type, M_F(name, _max)(tree)); \ } \ \ M_INLINE type * \ M_F(name, _get)(const tree_t tree, type const data) \ { \ M_RBTR33_CONTRACT (tree); \ node_t *n = tree->node; \ /* Go down the tree */ \ while (n != NULL) { \ M_RBTR33_CONTRACT_NODE (n); \ int cmp = M_CALL_CMP(oplist, n->data, data); \ if (cmp == 0) { \ return &n->data; \ } else { \ /* Go left (if cmp > 0) or right (if cmp < 0) */ \ n = n->child[cmp < 0]; \ } \ } \ return NULL; \ } \ \ M_INLINE type const * \ M_F(name, _cget)(const tree_t tree, type const data) \ { \ return M_CONST_CAST(type, M_F(name, _get)(tree, data)); \ } \ \ /* Create a copy of the given node (recursively) */ \ M_INLINE node_t * \ M_C3(m_rbtr33_,name,_copy_node)(const node_t *o) \ { \ if (o == NULL) return NULL; \ node_t *n = M_C3(m_rbtr33_,name,_new)(); \ if (M_UNLIKELY_NOMEM (n == NULL) ) { \ M_MEMORY_FULL (sizeof (node_t)); \ return NULL; \ } \ M_CALL_INIT_SET(oplist, n->data, o->data); \ n->child[0] = M_C3(m_rbtr33_,name,_copy_node)(o->child[0]); \ n->child[1] = M_C3(m_rbtr33_,name,_copy_node)(o->child[1]); \ M_RBTR33_COPY_COLOR (n, o); \ return n; \ } \ \ M_INLINE void \ M_F(name, _init_set)(tree_t tree, const tree_t ref) \ { \ M_RBTR33_CONTRACT (ref); \ M_ASSERT (tree != NULL && tree != ref); \ tree->size = ref->size; \ /* Copy the root node recursively */ \ tree->node = M_C3(m_rbtr33_,name,_copy_node)(ref->node); \ M_RBTR33_CONTRACT (tree); \ } \ \ M_INLINE void \ M_F(name, _set)(tree_t tree, const tree_t ref) \ { \ M_RBTR33_CONTRACT (tree); \ M_RBTR33_CONTRACT (ref); \ if (tree == ref) return; \ M_F(name,_clear)(tree); \ M_F(name,_init_set)(tree, ref); \ } \ \ M_INLINE void \ M_F(name, _init_move)(tree_t tree, tree_t ref) \ { \ M_RBTR33_CONTRACT (ref); \ M_ASSERT (tree != NULL && tree != ref); \ tree->size = ref->size; \ tree->node = ref->node; \ ref->node = NULL; \ ref->size = 0; \ M_RBTR33_CONTRACT (tree); \ } \ \ M_INLINE void \ M_F(name, _move)(tree_t tree, tree_t ref) \ { \ M_RBTR33_CONTRACT (tree); \ M_RBTR33_CONTRACT (ref); \ M_ASSERT (tree != ref); \ M_F(name,_clear)(tree); \ M_F(name,_init_move)(tree, ref); \ M_RBTR33_CONTRACT (tree); \ } \ \ M_INLINE void \ M_F(name, _swap)(tree_t tree1, tree_t tree2) \ { \ M_RBTR33_CONTRACT (tree1); \ M_RBTR33_CONTRACT (tree2); \ M_SWAP(size_t, tree1->size, tree2->size); \ M_SWAP(node_t *, tree1->node, tree2->node); \ M_RBTR33_CONTRACT (tree1); \ M_RBTR33_CONTRACT (tree2); \ } \ \ M_INLINE bool \ M_F(name, _empty_p)(const tree_t tree) \ { \ M_RBTR33_CONTRACT (tree); \ return tree->size == 0; \ } \ \ /* Take care of the case n == NULL too */ \ M_INLINE bool \ M_C3(m_rbtr33_,name,_black_p)(const node_t *n) \ { \ return (n == NULL) ? true : M_RBTR33_IS_BLACK(n); \ } \ \ M_INLINE void \ M_C3(m_rbtr33_,name,_set_black)(node_t *n) \ { \ if (n != NULL) M_RBTR33_SET_BLACK(n); \ } \ \ M_INLINE node_t * \ M_C3(m_rbtr33_,name,_rotate)(node_t *pp, node_t *ppp, const bool right) \ { \ M_ASSERT (pp != NULL && ppp != NULL); \ bool left = !right; \ node_t *p = pp->child[right]; \ M_ASSERT (p != NULL); \ pp->child[right] = p->child[left]; \ p->child[left] = pp; \ /* Fix grandparent with new parent */ \ M_ASSERT(ppp->child[0] == pp || ppp->child[1] == pp); \ ppp->child[(ppp->child[0] != pp)] = p; \ return p; \ } \ \ M_IF_DEBUG( \ /* Compute the depth of a node */ \ M_INLINE size_t \ M_C3(m_rbtr33_,name,_compute_depth)(const node_t *n) \ { \ if (n == NULL) return 1; \ return M_RBTR33_IS_BLACK (n) \ + M_C3(m_rbtr33_,name,_compute_depth)(n->child[0]); \ } \ ) \ \ M_INLINE bool \ M_F(name, _pop_at)(type *data_ptr, tree_t tree, type const key) \ { \ M_RBTR33_CONTRACT (tree); \ node_t *tab[M_RBTR33_MAX_STACK]; \ int8_t which[M_RBTR33_MAX_STACK]; \ unsigned int cpt = 0; \ node_t root_dummy; \ node_t *n = tree->node; \ which[0] = 0; \ root_dummy.child[0] = n; \ tab[cpt++] = &root_dummy; \ /* Search for the deletion point */ \ tab[cpt] = n; \ while (n != NULL) { \ M_RBTR33_CONTRACT_NODE (n); \ M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(n->child[0]) \ == M_C3(m_rbtr33_,name,_compute_depth)(n->child[1])); \ int cmp = M_CALL_CMP(oplist, n->data, key); \ if (cmp == 0) { \ break; \ } \ int i = (cmp < 0); \ which[cpt++] = (int8_t) i; \ n = n->child[i]; \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ tab[cpt] = n; \ } \ M_ASSERT (tab[cpt] == n); \ /* If not found, fail */ \ if (n == NULL) { \ return false; \ } \ unsigned int cpt_n = cpt; \ node_t *v = n; /* the replacement node */ \ node_t *u; /* the deleted node */ \ m_rbtr33_color_e v_color = M_RBTR33_GET_COLOR(v); \ /* Classical removal of a node from a binary tree */ \ if (v->child[0] != NULL && v->child[1] != NULL) { \ /* node has 2 child. */ \ /* Get the element right next to the deleted one */ \ v = v->child[1]; \ which[cpt++] = 1; \ tab[cpt] = v; \ while (v != NULL) { \ /* Always left node */ \ M_RBTR33_CONTRACT_NODE (v); \ M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(v->child[0]) \ == M_C3(m_rbtr33_,name,_compute_depth)(v->child[1])); \ which[cpt++] = 0; \ v = v->child[0]; \ M_ASSERT (cpt < M_RBTR33_MAX_STACK); \ tab[cpt] = v; \ } \ /* Pop the last element to get the last non-null element */ \ v = tab[--cpt]; \ M_ASSERT (v != NULL); \ u = v->child[1]; \ /* Replace 'v' by 'u' in the tree */ \ M_ASSERT(cpt >= 1 && tab[cpt-1] != NULL && tab[cpt-1]->child[which[cpt-1]] == v); \ tab[cpt-1]->child[which[cpt-1]] = u; \ /* Replace 'n' by 'v' in the tree */ \ M_ASSERT(cpt_n >= 1 && tab[cpt_n-1] != NULL); \ M_ASSERT(tab[cpt_n-1]->child[which[cpt_n-1]] == n); \ tab[cpt_n-1]->child[which[cpt_n-1]] = v; \ v->child[0] = n->child[0]; \ v->child[1] = n->child[1]; \ v_color = M_RBTR33_GET_COLOR(v); \ M_RBTR33_COPY_COLOR(v, n); \ tab[cpt_n] = v; \ /* For the algorithm, 'u' is now the deleted node */ \ } else { \ /* 1 or no child to the node. Replace the element */ \ v = n; \ u = v->child[(n->child[0] == NULL)]; \ M_ASSERT (cpt_n >= 1 &&tab[cpt_n-1] != NULL && tab[cpt_n-1]->child[which[cpt_n-1]] == n); \ M_ASSERT (n->child[(n->child[0] != NULL)] == NULL); \ tab[cpt_n-1]->child[which[cpt_n-1]] = u; \ /* in all cases, this node shall be set to black */ \ } \ \ /* Rebalance from child to root */ \ if (v_color == M_RBTR33_BLACK \ && M_C3(m_rbtr33_,name,_black_p)(u)) { \ /* tab[0] is NULL, tab[1] is root, u is double black */ \ node_t *p = u, *s; \ while (cpt >= 2) { \ p = tab[--cpt]; \ bool nbChild = which[cpt]; \ M_ASSERT (p != NULL && u == p->child[nbChild]); \ s = p->child[!nbChild]; \ /* if sibling is red, perform a rotation to move sibling up */ \ if (!M_C3(m_rbtr33_,name,_black_p)(s)) { \ p = M_C3(m_rbtr33_,name,_rotate) (p, tab[cpt-1], !nbChild); \ M_RBTR33_SET_BLACK(p); /* was sibling */ \ tab[cpt] = p; \ which[cpt++] = nbChild; \ p = p->child[nbChild]; /* was parent */ \ M_ASSERT (p != NULL); \ M_RBTR33_SET_RED(p); \ s = p->child[!nbChild]; \ M_ASSERT (M_C3(m_rbtr33_,name,_black_p)(s)); \ } \ M_ASSERT (p != NULL && u == p->child[nbChild]); \ /* if both childreen of s are black */ \ /* perform recoloring and recur on parent if black */ \ if (s != NULL \ && M_C3(m_rbtr33_,name,_black_p)(s->child[0]) \ && M_C3(m_rbtr33_,name,_black_p)(s->child[1])) { \ M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(s->child[0]) == M_C3(m_rbtr33_,name,_compute_depth)(s->child[1])); \ M_RBTR33_SET_RED(s); \ if (M_RBTR33_IS_RED(p)) { \ M_RBTR33_SET_BLACK(p); \ M_RBTR33_CONTRACT_NODE(p); \ M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(p->child[0]) == M_C3(m_rbtr33_,name,_compute_depth)(p->child[1])); \ break; \ } \ u = p; \ } else { \ M_ASSERT (s != NULL); \ /* at least one child of 's' is red */ \ /* perform rotation(s) */ \ bool childIsRight = !M_C3(m_rbtr33_,name,_black_p)(s->child[1]); \ m_rbtr33_color_e p_color = M_RBTR33_GET_COLOR (p); \ if (childIsRight != nbChild) { \ /* left-left or right-right case */ \ p = M_C3(m_rbtr33_,name,_rotate) (p, tab[cpt-1], childIsRight); \ } else { \ s = M_C3(m_rbtr33_,name,_rotate) (s, p, childIsRight); \ p = M_C3(m_rbtr33_,name,_rotate) (p, tab[cpt-1], !nbChild); \ } \ M_RBTR33_SET_COLOR(p, p_color); \ M_ASSERT(p->child[0] != NULL && p->child[1] != NULL); \ M_RBTR33_SET_BLACK(p->child[0]); \ M_RBTR33_SET_BLACK(p->child[1]); \ M_RBTR33_CONTRACT_NODE(p); \ M_ASSERT(M_C3(m_rbtr33_,name,_compute_depth)(p->child[0]) == M_C3(m_rbtr33_,name,_compute_depth)(p->child[1])); \ break; \ } \ } /* while */ \ if (cpt == 1 /* root has been reached? */ ) { \ M_C3(m_rbtr33_,name,_set_black)(p); \ M_ASSERT (root_dummy.child[0] == p); \ } \ } else { \ M_C3(m_rbtr33_,name,_set_black)(u); \ } \ tree->node = root_dummy.child[0]; \ M_ASSERT (tree->node == NULL || M_RBTR33_IS_BLACK(tree->node)); \ /* delete it */ \ if (data_ptr != NULL) \ M_DO_MOVE(oplist, *data_ptr, n->data); \ else \ M_CALL_CLEAR(oplist, n->data); \ M_C3(m_rbtr33_,name,_del) (n); \ tree->size --; \ M_RBTR33_CONTRACT (tree); \ return true; \ } \ \ M_INLINE void M_F(name,_remove)(tree_t t, it_t it) \ { \ /* Not optimum: another search in the tree is performed */ \ type data; \ M_CALL_INIT_SET(oplist, data, *M_F(name,_cref)(it)); \ M_F(name,_next)(it); \ M_F(name, _pop_at)(NULL, t, data); \ /* We have changed the tree: the iterator is partialy invalid */ \ if (!M_F(name, _end_p)(it)) { \ M_F(name, _it_from)(it, t, *M_F(name,_cref)(it)); \ } \ M_CALL_CLEAR(oplist, data); \ } \ \ M_IF_METHOD(EQUAL, oplist)( \ M_INLINE bool M_F(name,_equal_p)(const tree_t t1, const tree_t t2) { \ M_RBTR33_CONTRACT(t1); \ M_RBTR33_CONTRACT(t2); \ if (t1->size != t2->size) return false; \ it_t it1; \ it_t it2; \ /* NOTE: We can't compare two tree directly as they can be \ structuraly different but functionnaly equal (you get this by \ constructing the tree in a different way). We have to \ compare the ordered value within the tree. */ \ M_F(name, _it)(it1, t1); \ M_F(name, _it)(it2, t2); \ while (!M_F(name, _end_p)(it1) \ && !M_F(name, _end_p)(it2)) { \ type const *ref1 = M_F(name, _cref)(it1); \ type const *ref2 = M_F(name, _cref)(it2); \ if (M_CALL_EQUAL(oplist, *ref1, *ref2) == false) \ return false; \ M_F(name, _next)(it1); \ M_F(name, _next)(it2); \ } \ return M_F(name, _end_p)(it1) \ && M_F(name, _end_p)(it2); \ } \ , /* NO EQUAL METHOD */ ) \ \ M_IF_METHOD(HASH, oplist)( \ M_INLINE size_t M_F(name,_hash)(const tree_t t1) { \ M_RBTR33_CONTRACT(t1); \ M_HASH_DECL(hash); \ /* NOTE: We can't compute the hash directly for the same reason \ than for EQUAL operator. */ \ it_t it1; \ M_F(name, _it)(it1, t1); \ while (!M_F(name, _end_p)(it1)) { \ type const *ref1 = M_F(name, _cref)(it1); \ M_HASH_UP(hash, M_CALL_HASH(oplist, *ref1)); \ M_F(name, _next)(it1); \ } \ return M_HASH_FINAL (hash); \ } \ , /* NO HASH METHOD */ ) \ /* Define the I/O functions */ #define M_RBTR33_DEF_IO(name, type, oplist, tree_t, node_t, it_t) \ M_IF_METHOD(GET_STR, oplist)( \ M_INLINE void M_F(name, _get_str)(m_string_t str, \ tree_t const t1, bool append) { \ M_RBTR33_CONTRACT(t1); \ M_ASSERT(str != NULL); \ (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \ /* NOTE: The print is really naive, and not really efficient */ \ bool commaToPrint = false; \ it_t it1; \ M_F(name, _it)(it1, t1); \ while (!M_F(name, _end_p)(it1)) { \ if (commaToPrint) \ m_string_push_back (str, M_GET_SEPARATOR oplist); \ commaToPrint = true; \ type const *ref1 = M_F(name, _cref)(it1); \ M_CALL_GET_STR(oplist, str, *ref1, true); \ M_F(name, _next)(it1); \ } \ m_string_push_back (str, ']'); \ } \ , /* NO GET_STR */ ) \ \ M_IF_METHOD(OUT_STR, oplist)( \ M_INLINE void \ M_F(name, _out_str)(FILE *file, tree_t const rbtree) \ { \ M_RBTR33_CONTRACT(rbtree); \ M_ASSERT (file != NULL); \ fputc ('[', file); \ it_t it; \ bool commaToPrint = false; \ for (M_F(name, _it)(it, rbtree) ; \ !M_F(name, _end_p)(it); \ M_F(name, _next)(it)){ \ if (commaToPrint) \ fputc (M_GET_SEPARATOR oplist, file); \ commaToPrint = true; \ type const *item = M_F(name, _cref)(it); \ M_CALL_OUT_STR(oplist, file, *item); \ } \ fputc (']', file); \ } \ , /* no out_str */ ) \ \ M_IF_METHOD(PARSE_STR, oplist)( \ M_INLINE bool \ M_F(name, _parse_str)(tree_t rbtree, const char str[], const char **endp) \ { \ M_RBTR33_CONTRACT(rbtree); \ M_ASSERT (str != NULL); \ M_F(name,_reset)(rbtree); \ bool success = false; \ int c = *str++; \ if (M_UNLIKELY (c != '[')) goto exit; \ c = *str++; \ if (M_UNLIKELY (c == ']')) { success = true; goto exit; } \ if (M_UNLIKELY (c == 0)) goto exit; \ str--; \ type item; \ M_CALL_INIT(oplist, item); \ do { \ bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \ do { c = *str++; } while (isspace(c)); \ if (b == false || c == 0) goto exit_clear; \ M_F(name, _push)(rbtree, item); \ } while (c == M_GET_SEPARATOR oplist); \ success = (c == ']'); \ exit_clear: \ M_CALL_CLEAR(oplist, item); \ exit: \ if (endp) *endp = str; \ return success; \ } \ , /* no parse_str */ ) \ \ M_IF_METHOD(IN_STR, oplist)( \ M_INLINE bool \ M_F(name, _in_str)(tree_t rbtree, FILE *file) \ { \ M_RBTR33_CONTRACT(rbtree); \ M_ASSERT (file != NULL); \ M_F(name,_reset)(rbtree); \ int c = fgetc(file); \ if (M_UNLIKELY (c != '[')) return false; \ c = fgetc(file); \ if (M_UNLIKELY (c == ']')) return true; \ if (M_UNLIKELY (c == EOF)) return false; \ ungetc(c, file); \ type item; \ M_CALL_INIT(oplist, item); \ do { \ bool b = M_CALL_IN_STR(oplist, item, file); \ do { c = fgetc(file); } while (isspace(c)); \ if (b == false || c == EOF) break; \ M_F(name, _push)(rbtree, item); \ } while (c == M_GET_SEPARATOR oplist); \ M_CALL_CLEAR(oplist, item); \ return c == ']'; \ } \ , /* no in_str */ ) \ \ M_IF_METHOD(OUT_SERIAL, oplist)( \ M_INLINE m_serial_return_code_t \ M_F(name, _out_serial)(m_serial_write_t f, tree_t const t1) \ { \ M_RBTR33_CONTRACT(t1); \ M_ASSERT (f != NULL && f->m_interface != NULL); \ m_serial_local_t local; \ m_serial_return_code_t ret; \ M_F(name, _subtype_ct) const *item; \ bool first_done = false; \ it_t it; \ ret = f->m_interface->write_array_start(local, f, t1->size); \ for (M_F(name, _it)(it, t1) ; \ !M_F(name, _end_p)(it); \ M_F(name, _next)(it)){ \ item = M_F(name, _cref)(it); \ if (first_done) \ ret |= f->m_interface->write_array_next(local, f); \ ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \ first_done = true; \ } \ ret |= f->m_interface->write_array_end(local, f); \ return ret & M_SERIAL_FAIL; \ } \ , /* no OUT_SERIAL */ ) \ \ M_IF_METHOD(IN_SERIAL, oplist)( \ M_INLINE m_serial_return_code_t \ M_F(name, _in_serial)(tree_t t1, m_serial_read_t f) \ { \ M_RBTR33_CONTRACT(t1); \ M_ASSERT (f != NULL && f->m_interface != NULL); \ m_serial_local_t local; \ m_serial_return_code_t ret; \ size_t estimated_size = 0; \ type key; \ M_F(name,_reset)(t1); \ ret = f->m_interface->read_array_start(local, f, &estimated_size); \ if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ M_CALL_INIT(oplist, key); \ do { \ ret = M_CALL_IN_SERIAL(oplist, key, f); \ if (ret != M_SERIAL_OK_DONE) { break; } \ M_F(name, _push)(t1, key); \ } while ((ret = f->m_interface->read_array_next(local, f)) == M_SERIAL_OK_CONTINUE); \ M_CALL_CLEAR(oplist, key); \ return ret; \ } \ , /* no in_serial */ ) \ \ /********************************** INTERNAL *********************************/ // TODO: specialized _sort shall do nothing, but shall check the requested order. How ? #if M_USE_SMALL_NAME #define RBTREE_DEF M_RBTREE_DEF #define RBTREE_DEF_AS M_RBTREE_DEF_AS #define RBTREE_OPLIST M_RBTREE_OPLIST #endif #endif