Tactiliest/libs/mlib/m-rbtree.h
2024-01-17 21:45:57 +01:00

1187 lines
88 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