added cmsis_core, furi, mlib and nanobake implemented basic app structure from furi implemented basic placeholder apps
1187 lines
88 KiB
C
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
|