it is used to control the inline semantics. By combining it with optimize option "-fno-inline" for the internal M*LIB function (using _Pragma optimize) we can control easily if we want to use an inline version or call of an external function. Define new user API: * M_USE_EXTERN_DECL : define it before including any MLIB header to requests to call the M*LIB functions and don't inline then. * M_USE_EXTERN_DEF : define it once in a translation unit to define the M*LIB functions. This translation unit shall contain all templated macro too. If you use one option, you need to use the other one too for another translation unit.
1187 lines
87 KiB
C
1187 lines
87 KiB
C
/*
|
|
* 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
|