mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-02-18 19:03:16 +00:00
added cmsis_core, furi, mlib and nanobake implemented basic app structure from furi implemented basic placeholder apps
1500 lines
111 KiB
C
1500 lines
111 KiB
C
/*
|
|
* M*LIB - B+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_BPTREE_H
|
|
#define MSTARLIB_BPTREE_H
|
|
|
|
#include "m-core.h"
|
|
|
|
/* Define a B+tree of size 'N' that maps a 'key' to a 'value'
|
|
with its associated functions.
|
|
USAGE:
|
|
BPTREE_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
|
|
OR
|
|
BPTREE_DEF2(name, N, key_t, value_t)
|
|
*/
|
|
#define M_BPTREE_DEF2(name, N, key_type, ...) \
|
|
M_BPTREE_DEF2_AS(name, M_F(name,_t), M_F(name,_it_t), M_F(name, _itref_t), N, key_type, __VA_ARGS__)
|
|
|
|
|
|
/* Define a B+tree of size 'N' that maps a 'key' to a 'value'
|
|
as the given name name_t with its associated functions.
|
|
USAGE:
|
|
BPTREE_DEF2_AS(name, name_t, it_t, itref_t, N, key_t, key_oplist, value_t, value_oplist)
|
|
OR
|
|
BPTREE_DEF2_AS(name, name_t, it_t, itref_t, N, key_t, value_t)
|
|
*/
|
|
#define M_BPTREE_DEF2_AS(name, name_t, it_t, itref_t, N, key_type, ...) \
|
|
M_BEGIN_PROTECTED_CODE \
|
|
M_BPTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 0, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, itref_t ), \
|
|
(name, N, key_type, __VA_ARGS__, 1, 0, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, itref_t ))) \
|
|
M_END_PROTECTED_CODE
|
|
|
|
|
|
/* Define a B+tree of a given type, of size N.
|
|
with its associated functions
|
|
USAGE: BPTREE_DEF(name, N, type, [, oplist_of_the_type]) */
|
|
#define M_BPTREE_DEF(name, N, ...) \
|
|
M_BPTREE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), N, __VA_ARGS__)
|
|
|
|
|
|
/* Define a B+tree of a given type, of size N.
|
|
as the given name name_t with its associated functions
|
|
USAGE: BPTREE_DEF_AS(name, name_t, it_t, N, type, [, oplist_of_the_type]) */
|
|
#define M_BPTREE_DEF_AS(name, name_t, it_t, N, ...) \
|
|
M_BEGIN_PROTECTED_CODE \
|
|
M_BPTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 0, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, M_F(name, _itref_ct) ), \
|
|
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 0, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, M_F(name, _itref_ct) ))) \
|
|
M_END_PROTECTED_CODE
|
|
|
|
|
|
/* Define a B+tree of size 'N' that maps a 'key' to a 'value',
|
|
allowing multiple equal keys to exist,
|
|
with its associated functions.
|
|
USAGE:
|
|
BPTREE_MULTI_DEF2(name, N, key_t, key_oplist, value_t, value_oplist)
|
|
OR
|
|
BPTREE_MULTI_DEF2(name, N, key_t, value_t)
|
|
*/
|
|
#define M_BPTREE_MULTI_DEF2(name, N, key_type, ...) \
|
|
M_BPTREE_MULTI_DEF2_AS(name, M_F(name,_t), M_F(name,_it_t), M_F(name,_itref_t), N, key_type, __VA_ARGS__)
|
|
|
|
|
|
/* Define a B+tree of size 'N' that maps a 'key' to a 'value',
|
|
allowing multiple equal keys to exist,
|
|
as the given name name_t with its associated functions.
|
|
USAGE:
|
|
BPTREE_MULTI_DEF2_AS(name, name_t, it_t, itref_t, N, key_t, key_oplist, value_t, value_oplist)
|
|
OR
|
|
BPTREE_MULTI_DEF2_AS(name, name_t, it_t, itref_t, N, key_t, value_t)
|
|
*/
|
|
#define M_BPTREE_MULTI_DEF2_AS(name, name_t, it_t, itref_t, N, key_type, ...) \
|
|
M_BEGIN_PROTECTED_CODE \
|
|
M_BPTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
((name, N, key_type, M_GLOBAL_OPLIST_OR_DEF(key_type)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 1, 1, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, itref_t ), \
|
|
(name, N, key_type, __VA_ARGS__, 1, 1, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, itref_t ))) \
|
|
M_END_PROTECTED_CODE
|
|
|
|
|
|
/* Define a B+tree of a given type, of size N.
|
|
allowing multiple equal keys to exist,
|
|
with its associated functions
|
|
USAGE: BPTREE_MULTI_DEF(name, N, type, [, oplist_of_the_type]) */
|
|
#define M_BPTREE_MULTI_DEF(name, N, ...) \
|
|
M_BPTREE_MULTI_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), N, __VA_ARGS__)
|
|
|
|
|
|
/* Define a B+tree of a given type, of size N.
|
|
allowing multiple equal keys to exist,
|
|
as the given name name_t with its associated functions
|
|
USAGE: BPTREE_MULTI_DEF_AS(name, name_t, it_t, N, type, [, oplist_of_the_type]) */
|
|
#define M_BPTREE_MULTI_DEF_AS(name, name_t, it_t, N, ...) \
|
|
M_BEGIN_PROTECTED_CODE \
|
|
M_BPTR33_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
((name, N, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), 0, 1, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, M_F(name, _itref_ct) ), \
|
|
(name, N, __VA_ARGS__, __VA_ARGS__, 0, 1, name_t, M_F(name, _node_ct), M_F(name, _pit_ct), it_t, M_F(name, _itref_ct) ))) \
|
|
M_BEGIN_PROTECTED_CODE
|
|
|
|
|
|
/* Define the oplist of a B+TREE used as a set of type (from BPTREE_DEF).
|
|
USAGE: BPTREE_OPLIST(name [, oplist_of_the_type])
|
|
NOTE: IT_REF is not exported so that the container appears as not modifiable
|
|
by algorithm. */
|
|
#define M_BPTREE_OPLIST(...) \
|
|
M_BPTR33_KEY_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
((__VA_ARGS__, M_BASIC_OPLIST ), \
|
|
(__VA_ARGS__ )))
|
|
|
|
|
|
/* Define the oplist of a B+TREE used as a map of a key type to a value type (from BPTREE_DEF2).
|
|
USAGE: BPTREE_OPLIST2(name[, key_oplist, value_oplist])
|
|
NOTE: IT_REF is not exported so that the container appears as not modifiable
|
|
by algorithm. */
|
|
#define M_BPTREE_OPLIST2(...) \
|
|
M_BPTR33_OPLIST2_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
((__VA_ARGS__, M_BASIC_OPLIST, M_BASIC_OPLIST ), \
|
|
(__VA_ARGS__ )))
|
|
|
|
|
|
/*****************************************************************************/
|
|
/******************************** INTERNAL ***********************************/
|
|
/*****************************************************************************/
|
|
|
|
/* Deferred evaluation for the oplist definition,
|
|
so that all arguments are evaluated before further expansion */
|
|
#define M_BPTR33_KEY_OPLIST_P1(arg) M_BPTR33_KEY_OPLIST_P2 arg
|
|
|
|
/* Validation of the given oplists */
|
|
#define M_BPTR33_KEY_OPLIST_P2(name, oplist) \
|
|
M_IF_OPLIST(oplist)(M_BPTR33_KEY_OPLIST_P3, M_BPTR33_KEY_OPLIST_FAILURE)(name, oplist)
|
|
|
|
/* Prepare a clean compilation failure */
|
|
#define M_BPTR33_KEY_OPLIST_FAILURE(name, oplist) \
|
|
((M_LIB_ERROR(ARGUMENT_OF_BPTREE_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
|
|
|
/* OPLIST definition of a b+tree (global tree) */
|
|
#define M_BPTR33_KEY_OPLIST_P3(name, oplist) \
|
|
(INIT(M_F(name, _init)), \
|
|
INIT_SET(M_F(name, _init_set)), \
|
|
INIT_WITH(API_1(M_INIT_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)), \
|
|
IT_TYPE(M_F(name, _it_ct)), \
|
|
IT_FIRST(M_F(name,_it)), \
|
|
IT_SET(M_F(name,_it_set)), \
|
|
IT_END(M_F(name,_it_end)), \
|
|
IT_END_P(M_F(name,_end_p)), \
|
|
IT_EQUAL_P(M_F(name,_it_equal_p)), \
|
|
IT_NEXT(M_F(name,_next)), \
|
|
IT_CREF(M_F(name,_cref)), \
|
|
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)),) \
|
|
)
|
|
|
|
|
|
/* Deferred evaluation */
|
|
#define M_BPTR33_OPLIST2_P1(arg) M_BPTR33_OPLIST2_P2 arg
|
|
|
|
/* Validation of the given oplists (first the key oplist, then the value oplist) */
|
|
#define M_BPTR33_OPLIST2_P2(name, key_oplist, value_oplist) \
|
|
M_IF_OPLIST(key_oplist)(M_BPTR33_OPLIST2_P3, M_BPTR33_OPLIST2_FAILURE)(name, key_oplist, value_oplist)
|
|
#define M_BPTR33_OPLIST2_P3(name, key_oplist, value_oplist) \
|
|
M_IF_OPLIST(value_oplist)(M_BPTR33_OPLIST2_P4, M_BPTR33_OPLIST2_FAILURE)(name, key_oplist, value_oplist)
|
|
|
|
/* Prepare a clean compilation failure */
|
|
#define M_BPTR33_OPLIST2_FAILURE(name, key_oplist, value_oplist) \
|
|
((M_LIB_ERROR(ARGUMENT_OF_BPTREE_OPLIST_IS_NOT_AN_OPLIST, name, key_oplist, value_oplist)))
|
|
|
|
/* Final definition of the oplist (associative array) */
|
|
#define M_BPTR33_OPLIST2_P4(name, key_oplist, value_oplist) \
|
|
(INIT(M_F(name, _init)), \
|
|
INIT_SET(M_F(name, _init_set)), \
|
|
INIT_WITH(API_1(M_INIT_KEY_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)), \
|
|
IT_TYPE(M_F(name, _it_ct)), \
|
|
IT_FIRST(M_F(name,_it)), \
|
|
IT_SET(M_F(name,_it_set)), \
|
|
IT_END(M_F(name,_it_end)), \
|
|
IT_END_P(M_F(name,_end_p)), \
|
|
IT_EQUAL_P(M_F(name,_it_equal_p)), \
|
|
IT_NEXT(M_F(name,_next)), \
|
|
IT_CREF(M_F(name,_cref)), \
|
|
RESET(M_F(name,_reset)), \
|
|
GET_MIN(M_F(name,_min)), \
|
|
GET_MAX(M_F(name,_max)), \
|
|
KEY_TYPE(M_F(name, _key_ct)), \
|
|
VALUE_TYPE(M_F(name, _value_ct)), \
|
|
SET_KEY(M_F(name, _set_at)), \
|
|
GET_KEY(M_F(name, _get)), \
|
|
SAFE_GET_KEY(M_F(name, _safe_get)) \
|
|
ERASE_KEY(M_F(name, _erase)), \
|
|
KEY_OPLIST(key_oplist), \
|
|
VALUE_OPLIST(value_oplist), \
|
|
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)(PARSE_STR(M_F(name, _parse_str)),), \
|
|
M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)(OUT_STR(M_F(name, _out_str)),), \
|
|
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)(IN_STR(M_F(name, _in_str)),), \
|
|
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)(OUT_SERIAL(M_F(name, _out_serial)),), \
|
|
M_IF_METHOD_BOTH(IN_SERIAL, key_oplist, value_oplist)(IN_SERIAL(M_F(name, _in_serial)),), \
|
|
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)(EQUAL(M_F(name, _equal_p)),), \
|
|
M_IF_METHOD_BOTH(HASH, key_oplist, value_oplist)(HASH(M_F(name, _hash)),) \
|
|
)
|
|
|
|
|
|
/******************************** INTERNAL ***********************************/
|
|
|
|
/* Internal contract of a B+TREE of size 'N' for a node 'node' or root 'root' */
|
|
#ifdef NDEBUG
|
|
# define M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, node, root) do { } while (0)
|
|
#else
|
|
# define M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, node, root) do { \
|
|
M_ASSERT ((node) != NULL); \
|
|
M_ASSERT ((root) != NULL); \
|
|
int num2 = (node)->num; \
|
|
bool is_leaf2 = num2 <= 0; \
|
|
num2 = num2 < 0 ? -num2 : num2; \
|
|
if ((node) == (root)) { \
|
|
/* Contract of the root node. num can be 0 */ \
|
|
M_ASSERT( 0 <= num2 && num2 <= N); \
|
|
if (num2 == 0) M_ASSERT (is_leaf2); \
|
|
} else { \
|
|
/* Contract of a non-root node. num cannot be 0 */ \
|
|
int c2 = N / 2; \
|
|
M_ASSERT (c2 > 0); \
|
|
M_ASSERT (c2 <= num2 && num2 <= N); \
|
|
} \
|
|
/* The node is sorted */ \
|
|
for(int i2 = 1; i2 < num2 ; i2++) { \
|
|
M_ASSERT (M_CALL_CMP(key_oplist, (node)->key[i2-1], (node)->key[i2]) M_IF(isMulti)(<=, <) 0); \
|
|
} \
|
|
/* The chain node is also sorted */ \
|
|
if ((node)->next != NULL) { \
|
|
M_ASSERT (num2 >= 1); \
|
|
M_ASSERT (M_CALL_CMP(key_oplist, (node)->key[num2-1], (node)->next->key[0]) M_IF(isMulti)(<=, <) 0); \
|
|
} \
|
|
} while (0)
|
|
#endif
|
|
|
|
/* Contract for a B+TREE of size N named 'b' */
|
|
#define M_BPTR33_CONTRACT(N, isMulti, key_oplist, b) do { \
|
|
M_ASSERT (N >= 3); /* TBC: 2 instead ? */ \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, (b)->root, (b)->root); \
|
|
M_ASSERT ((b)->root->next == NULL); \
|
|
if ((b)->root->num <= 0) M_ASSERT (-(b)->root->num == (int) (b)->size); \
|
|
} while (0)
|
|
|
|
/* Max depth of any B+tree
|
|
Worst case is when all nodes are only half full.
|
|
Worst case is with the mininum size of a node (2)
|
|
Maximum number of elements: SIZE_MAX = 2 ^ (CHAR_BIT*sizeof (size_t)) - 1
|
|
Height of such a tree if inferior to:
|
|
Ceil(Log2(2 ^ (CHAR_BIT*sizeof (size_t)))) + 1
|
|
"+ 1" due to final line composed of nodes.
|
|
*/
|
|
#define M_BPTR33_MAX_STACK ((int)(1 + CHAR_BIT*sizeof (size_t)))
|
|
|
|
|
|
/* Deferred evaluation for the b+tree definition,
|
|
so that all arguments are evaluated before further expansion */
|
|
#define M_BPTR33_DEF_P1(arg) M_ID( M_BPTR33_DEF_P2 arg )
|
|
|
|
/* Validate the key oplist before going further */
|
|
#define M_BPTR33_DEF_P2(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_IF_OPLIST(key_oplist)(M_BPTR33_DEF_P3, M_BPTR33_DEF_FAILURE) \
|
|
(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t)
|
|
|
|
/* Validate the value oplist before going further */
|
|
#define M_BPTR33_DEF_P3(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_IF_OPLIST(value_oplist)(M_BPTR33_DEF_P4, M_BPTR33_DEF_FAILURE) \
|
|
(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t)
|
|
|
|
/* Stop processing with a compilation failure */
|
|
#define M_BPTR33_DEF_FAILURE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, \
|
|
"(BPTREE*_DEF): one of the given argument is not a valid oplist: " \
|
|
M_AS_STR(key_oplist) " / " M_AS_STR(value_oplist))
|
|
|
|
/* Internal b+tree definition
|
|
- name: prefix to be used
|
|
- N: size of the node
|
|
- key_t: key type of the elements of the container
|
|
- key_oplist: oplist of the key type of the elements of the container
|
|
- value_t: value type of the elements of the container
|
|
- value_oplist: oplist of the value type of the elements of the container
|
|
- isMap: true if map, false if set
|
|
- isMulti: true if multimap/multiset, false otherwise
|
|
- tree_t: alias for the type of the container
|
|
- it_t: alias for the iterator of the container
|
|
- node_t: alias for internal node
|
|
- pit_t: alias for internal parent iterator
|
|
- subtype_t: alias for the type referenced by the iterator
|
|
*/
|
|
#define M_BPTR33_DEF_P4(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_BPTR33_DEF_TYPE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_CHECK_COMPATIBLE_OPLIST(name, 1, key_t, key_oplist) \
|
|
M_CHECK_COMPATIBLE_OPLIST(name, 2, value_t, value_oplist) \
|
|
M_BPTR33_DEF_CORE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_BPTR33_DEF_IT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_BPTR33_DEF_EXT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_EMPLACE_ASS_ARRAY_OR_QUEUE_DEF(M_INV(isMap), name, tree_t, key_oplist, value_oplist)
|
|
|
|
/* Define the types of a B+Tree */
|
|
#define M_BPTR33_DEF_TYPE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
M_IF(isMap)( \
|
|
/* Type returned by the iterator. Due to having key and value \
|
|
separated in their own array in the node, it is pointers to \
|
|
the objects, not a global pointer to both objects. */ \
|
|
typedef struct M_F(name, _pair_s) { \
|
|
key_t *key_ptr; \
|
|
value_t *value_ptr; \
|
|
} subtype_t; \
|
|
, \
|
|
typedef key_t subtype_t; \
|
|
) \
|
|
\
|
|
/* Define a Node of a B+TREE \
|
|
* For a reason of simplicity, it allocates one more element than \
|
|
* needed so that the code can push one more element in the node and \
|
|
* then split the nodes (simplify the code) \
|
|
*/ \
|
|
typedef struct M_F(name, _node_s) { \
|
|
int num; /* Abs=Number of keys. Sign <0 is leaf */ \
|
|
key_t key[N+1]; /* We can temporary push one more key */ \
|
|
struct M_F(name, _node_s) *next; /* next node reference */ \
|
|
union M_F(name, _kind_s) { /* either value or pointer to other nodes */ \
|
|
M_IF(isMap)(value_t value[N+1];,) \
|
|
struct M_F(name, _node_s) *node[N+2]; \
|
|
} kind; \
|
|
} *node_t; \
|
|
\
|
|
/* A B+TREE is just a pointer to the root node */ \
|
|
typedef struct M_F(name, _s) { \
|
|
node_t root; \
|
|
size_t size; \
|
|
} tree_t[1]; \
|
|
typedef struct M_F(name, _s) *M_F(name, _ptr); \
|
|
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
|
|
\
|
|
/* Definition of the alias used by the oplists */ \
|
|
typedef subtype_t M_F(name, _subtype_ct); \
|
|
typedef key_t M_F(name, _key_ct); \
|
|
typedef value_t M_F(name, _value_ct); \
|
|
typedef tree_t M_F(name, _ct); \
|
|
\
|
|
/* Define the Parent Tree Iterator */ \
|
|
typedef struct M_F(name, _parent_it_s) { \
|
|
int num; \
|
|
node_t parent[M_BPTR33_MAX_STACK]; \
|
|
} pit_t[1]; \
|
|
\
|
|
/* Define the Iterator */ \
|
|
typedef struct M_F(name, _it_s) { \
|
|
M_IF(isMap)(struct M_F(name, _pair_s) pair;,) \
|
|
node_t node; \
|
|
int idx; \
|
|
} it_t[1]; \
|
|
typedef it_t M_F(name, _it_ct); \
|
|
|
|
/* Define the core functions of a B+ Tree */
|
|
#define M_BPTR33_DEF_CORE(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
\
|
|
/* Allocate a new node */ \
|
|
/* TODO: Can be specialized to alloc for leaf or for non leaf */ \
|
|
M_INLINE node_t M_F(name, _new_node)(void) \
|
|
{ \
|
|
M_STATIC_ASSERT(N >= 2, M_LIB_ILLEGAL_PARAM, \
|
|
"Number of items per node shall be >= 2."); \
|
|
node_t n = M_CALL_NEW(key_oplist, struct M_F(name, _node_s)); \
|
|
if (M_UNLIKELY_NOMEM (n == NULL)) { \
|
|
M_MEMORY_FULL(sizeof (node_t)); \
|
|
M_ASSERT (0); \
|
|
} \
|
|
n->next = NULL; \
|
|
n->num = 0; \
|
|
return n; \
|
|
} \
|
|
\
|
|
M_INLINE void M_F(name, _init)(tree_t b) \
|
|
{ \
|
|
b->root = M_F(name, _new_node)(); \
|
|
b->size = 0; \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
} \
|
|
\
|
|
M_INLINE bool M_F(name, _is_leaf)(const node_t n) \
|
|
{ \
|
|
/* We consider the empty node as a leaf */ \
|
|
/* Only the root node can be empty */ \
|
|
return n->num <= 0; \
|
|
} \
|
|
\
|
|
/* Return the number of keys of the node */ \
|
|
M_INLINE int M_F(name, _get_num)(const node_t n) \
|
|
{ \
|
|
int num = n->num; \
|
|
num = num < 0 ? -num : num; \
|
|
M_ASSERT (num <= N); \
|
|
return num; \
|
|
} \
|
|
\
|
|
M_INLINE void M_F(name, _reset)(tree_t b) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
node_t next, n = b->root; \
|
|
pit_t pit; \
|
|
/* np is the heigh of the tree */ \
|
|
int np = 0; \
|
|
/* Scan down the nodes to the left down node */ \
|
|
while (!M_F(name, _is_leaf)(n)) { \
|
|
pit->parent[np++] = n; \
|
|
M_ASSERT (np <= M_BPTR33_MAX_STACK); \
|
|
n = n->kind.node[0]; \
|
|
} \
|
|
pit->parent[np++] = n; \
|
|
M_ASSERT (np <= M_BPTR33_MAX_STACK); \
|
|
/* Clean & free non root */ \
|
|
for(int i = 0; i < np; i++) { \
|
|
n = pit->parent[i]; \
|
|
while (n != NULL) { \
|
|
/* Clear key (& value for leaf) */ \
|
|
int num = M_F(name, _get_num)(n); \
|
|
M_IF(isMap)(bool is_leaf = M_F(name, _is_leaf)(n);,) \
|
|
for(int j = 0; j < num; j++) { \
|
|
M_CALL_CLEAR(key_oplist, n->key[j]); \
|
|
M_IF(isMap)(if (is_leaf) { \
|
|
M_CALL_CLEAR(value_oplist, n->kind.value[j]); \
|
|
},) \
|
|
} \
|
|
/* Next node of the same height */ \
|
|
next = n->next; \
|
|
if (i != 0) { \
|
|
/* Free the node if non root */ \
|
|
M_CALL_DEL(key_oplist, n); \
|
|
} \
|
|
n = next; \
|
|
} \
|
|
} \
|
|
/* Clean root */ \
|
|
b->root->num = 0; \
|
|
b->size = 0; \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
} \
|
|
\
|
|
M_INLINE void M_F(name, _clear)(tree_t b) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
M_F(name, _reset)(b); \
|
|
/* Once the tree is clean, only the root remains */ \
|
|
M_CALL_DEL(key_oplist, b->root); \
|
|
b->root = NULL; \
|
|
} \
|
|
\
|
|
/* Copy recursively the node 'o' of root node 'root' */ \
|
|
M_INLINE node_t M_F(name, _copy_node)(const node_t o, const node_t root) \
|
|
{ \
|
|
node_t n = M_F(name, _new_node)(); \
|
|
/* Set default number of keys and type to copy */ \
|
|
n->num = o->num; \
|
|
/* By default it is not linked to its brother. \
|
|
Only the parent of this node can do it. It is fixed by it */ \
|
|
n->next = NULL; \
|
|
/* Get number of keys in the node and copy them */ \
|
|
int num = M_F(name, _get_num)(o); \
|
|
for(int i = 0; i < num; i++) { \
|
|
M_CALL_INIT_SET(key_oplist, n->key[i], o->key[i]); \
|
|
} \
|
|
if (M_F(name, _is_leaf)(o)) { \
|
|
/* Copy the associated values if it is a leaf and a MAP */ \
|
|
M_IF(isMap)( \
|
|
for(int i = 0; i < num; i++) { \
|
|
M_CALL_INIT_SET(value_oplist, n->kind.value[i], o->kind.value[i]); \
|
|
} \
|
|
, /* End of isMap */) \
|
|
} else { \
|
|
/* Copy recursively the associated nodes if it is not a leaf */ \
|
|
for(int i = 0; i <= num; i++) { \
|
|
M_ASSERT(o->kind.node[i] != root); \
|
|
n->kind.node[i] = M_F(name, _copy_node)(o->kind.node[i], root); \
|
|
} \
|
|
/* The copied nodes don't have their next field correct */ \
|
|
/* Fix the next field for the copied nodes */ \
|
|
for(int i = 0; i < num; i++) { \
|
|
node_t current = n->kind.node[i]; \
|
|
node_t next = n->kind.node[i+1]; \
|
|
current->next = next; \
|
|
/* Go down the tree up to the leaf \
|
|
and fix the final 'next' link with the copied node */ \
|
|
while (!M_F(name, _is_leaf)(current)) { \
|
|
M_ASSERT(!M_F(name, _is_leaf)(next)); \
|
|
current = current->kind.node[current->num]; \
|
|
next = next->kind.node[0]; \
|
|
current->next = next; \
|
|
} \
|
|
} \
|
|
} \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, (o==root) ? n : root); \
|
|
return n; \
|
|
} \
|
|
\
|
|
M_INLINE void M_F(name, _init_set)(tree_t b, const tree_t o) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, o); \
|
|
M_ASSERT (b != NULL); \
|
|
/* Just copy recursively the root node */ \
|
|
b->root = M_F(name, _copy_node)(o->root, o->root); \
|
|
b->size = o->size; \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
} \
|
|
\
|
|
M_INLINE void M_F(name, _set)(tree_t b, const tree_t o) \
|
|
{ \
|
|
/* NOTE: We could reuse the already allocated nodes of 'b'. \
|
|
Not sure if it worth the effort */ \
|
|
M_F(name, _clear)(b); \
|
|
M_F(name, _init_set)(b, o); \
|
|
} \
|
|
\
|
|
M_INLINE bool M_F(name, _empty_p)(const tree_t b) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
/* root shall be an empty leaf */ \
|
|
return b->size == 0; \
|
|
} \
|
|
\
|
|
M_INLINE size_t M_F(name, _size)(const tree_t b) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
return b->size; \
|
|
} \
|
|
\
|
|
M_INLINE node_t M_F(name, _search_for_leaf)(pit_t pit, const tree_t b, key_t const key) \
|
|
{ \
|
|
node_t n = b->root; \
|
|
int np = 0; \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
|
/* Go down the tree while searching for key */ \
|
|
while (!M_F(name, _is_leaf)(n)) { \
|
|
M_ASSERT (np <= M_BPTR33_MAX_STACK); \
|
|
int i, hi = n->num; \
|
|
M_ASSERT (hi > 0); \
|
|
/* Linear search is usually faster than binary search for \
|
|
B+TREE (due to cache effect). If a binary tree is faster for \
|
|
the choosen type and size , it probably means that the \
|
|
size of B+TREE is too big and should be reduced. */ \
|
|
for(i = 0; i < hi; i++) { \
|
|
if (M_CALL_CMP(key_oplist, key, n->key[i]) <= 0) \
|
|
break; \
|
|
} \
|
|
/* Update the Parent iterator */ \
|
|
pit->parent[np++] = n; \
|
|
/* Select the new node to go down to */ \
|
|
n = n->kind.node[i]; \
|
|
M_ASSERT (n != NULL); \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
|
} \
|
|
pit->num = np; \
|
|
return n; \
|
|
} \
|
|
\
|
|
M_INLINE value_t *M_F(name, _get)(const tree_t b, key_t const key) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
pit_t pit; \
|
|
/* Get the leaf node where the key can be */ \
|
|
node_t n = M_F(name, _search_for_leaf)(pit, b, key); \
|
|
int cmp = 0; \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
|
/* Search in the leaf for key */ \
|
|
for(int i = 0; cmp >= 0 && i < -n->num; i++) { \
|
|
cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
|
if (cmp == 0) { \
|
|
/* Return the value if MAP mode or the key if SET mode */ \
|
|
return M_IF(isMap)(&n->kind.value[i],&n->key[i]); \
|
|
} \
|
|
} \
|
|
/* Key not found */ \
|
|
return NULL; \
|
|
} \
|
|
\
|
|
M_INLINE value_t const *M_F(name, _cget)(const tree_t b, key_t const key) \
|
|
{ \
|
|
return M_CONST_CAST(value_t, M_F(name, _get)(b, key)); \
|
|
} \
|
|
\
|
|
M_INLINE int \
|
|
M_F(name, _search_and_insert_in_leaf)(node_t n, key_t const key \
|
|
M_IF(isMap)( M_DEFERRED_COMMA value_t const value,) ) \
|
|
{ \
|
|
M_ASSERT (M_F(name, _is_leaf)(n)); \
|
|
int i, num = M_F(name, _get_num)(n); \
|
|
M_ASSERT (num <= N); \
|
|
/* Search for the key in the node n (a leaf) for insertion */ \
|
|
for(i = 0; i < num; i++) { \
|
|
int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
|
if (cmp <= 0) { \
|
|
M_IF(isMulti)( /* Nothing to do : fallthrough */, \
|
|
/* Update value if keys are equal */ \
|
|
if (M_UNLIKELY (cmp == 0)) { \
|
|
M_IF(isMap)(M_CALL_SET(value_oplist, n->kind.value[i], value);,) \
|
|
return -1; \
|
|
} \
|
|
) \
|
|
/* Move tables to make space for insertion */ \
|
|
memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(unsigned int)(num-i)); \
|
|
M_IF(isMap)(memmove(&n->kind.value[i+1], &n->kind.value[i], sizeof(value_t)*(unsigned int)(num-i));,) \
|
|
break; \
|
|
} \
|
|
} \
|
|
/* Insert key & value if MAP mode */ \
|
|
M_CALL_INIT_SET(key_oplist, n->key[i], key); \
|
|
M_IF(isMap)(M_CALL_INIT_SET(value_oplist, n->kind.value[i], value);,) \
|
|
/* Increase the number of key in the node */ \
|
|
n->num += -1; /* Increase num as num<0 for leaf */ \
|
|
return i; \
|
|
} \
|
|
\
|
|
M_INLINE int \
|
|
M_F(name, _search_and_insert_in_node)(node_t n, node_t l, key_t key) \
|
|
{ \
|
|
M_ASSERT (!M_F(name, _is_leaf)(n)); \
|
|
int i, num = M_F(name, _get_num)(n); \
|
|
M_ASSERT (num <= N); \
|
|
/* Search for the key in the node n (not a leaf) for insertion */ \
|
|
for(i = 0; i < num; i++) { \
|
|
if (n->kind.node[i] == l) { \
|
|
/* Move tables to make space for insertion */ \
|
|
memmove(&n->key[i+1], &n->key[i], sizeof(key_t)*(unsigned int)(num-i)); \
|
|
memmove(&n->kind.node[i+1], &n->kind.node[i], sizeof(node_t)*(unsigned int)(num-i+1)); \
|
|
break; \
|
|
} \
|
|
} \
|
|
/* Insert key in node */ \
|
|
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
|
|
M_CALL_INIT_SET(key_oplist, n->key[i], key); \
|
|
/* Increase the number of key in the node */ \
|
|
n->num += 1; \
|
|
return i; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_IF(isMap)(M_F(name, _set_at),M_F(name,_push))(tree_t b, key_t const key \
|
|
M_IF(isMap)(M_DEFERRED_COMMA value_t const value,)) \
|
|
{ \
|
|
pit_t pit; \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
node_t leaf = M_F(name, _search_for_leaf)(pit, b, key); \
|
|
/* Insert key into the leaf.*/ \
|
|
/* NOTE: Even if there is N elements, we can still add one more.*/ \
|
|
int i = M_F(name, _search_and_insert_in_leaf)(leaf, key M_IF (isMap)(M_DEFERRED_COMMA value,)); \
|
|
if (i < 0) { \
|
|
/* Nothing to do anymore. key already exists in the tree. \
|
|
value has been updated if needed */ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
return; \
|
|
} \
|
|
b->size ++; \
|
|
/* Most likely case: leaf can accept key */ \
|
|
int num = -leaf->num; \
|
|
M_ASSERT (num > 0); \
|
|
if (M_LIKELY (num <= N)) { \
|
|
/* nothing more to do */ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
return; \
|
|
} \
|
|
M_ASSERT (num == N+1); \
|
|
\
|
|
/* Needs to rebalance the B+TREE */ \
|
|
/* leaf is full: need to slip the leaf in two */ \
|
|
int nnum = (N + 1) / 2; \
|
|
num = N + 1 - nnum; \
|
|
node_t nleaf = M_F(name, _new_node)(); \
|
|
/* Move half objects to the new node */ \
|
|
memmove(&nleaf->key[0], &leaf->key[num], sizeof(key_t)*(unsigned int)nnum); \
|
|
M_IF(isMap)(memmove(&nleaf->kind.value[0], &leaf->kind.value[num], sizeof(value_t)*(unsigned int)nnum);,) \
|
|
leaf->num = -num; \
|
|
nleaf->num = -nnum; \
|
|
nleaf->next = leaf->next; \
|
|
leaf->next = nleaf; \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, leaf, b->root); \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, nleaf, b->root); \
|
|
/* Update parent to inject *key_ptr that splits between (leaf, nleaf) */ \
|
|
key_t *key_ptr = &leaf->key[num-1]; \
|
|
while (true) { \
|
|
if (pit->num == 0) { \
|
|
/* We reach root ==> Need to increase the height of the tree.*/ \
|
|
node_t parent = M_F(name, _new_node)(); \
|
|
parent->num = 1; \
|
|
/* TBC: DO_INIT_MOVE instead ? If key was in a node !*/ \
|
|
M_CALL_INIT_SET(key_oplist, parent->key[0], *key_ptr); \
|
|
parent->kind.node[0] = leaf; \
|
|
parent->kind.node[1] = nleaf; \
|
|
b->root = parent; \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
return; \
|
|
} \
|
|
/* Non root node. Get the parent node */ \
|
|
node_t parent = pit->parent[--pit->num]; \
|
|
/* Insert into parent (It is big enough to receive temporary one more) */ \
|
|
i = M_F(name, _search_and_insert_in_node)(parent, leaf, *key_ptr); \
|
|
parent->kind.node[i] = leaf; \
|
|
parent->kind.node[i+1] = nleaf; \
|
|
/* Test if parent node is full? */ \
|
|
if (M_LIKELY (parent->num <= N)) { \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
return; /* No need to split parent.*/ \
|
|
} \
|
|
M_ASSERT (parent->num == N+1); \
|
|
/* Need to split parent in {np} {med} {nnp} */ \
|
|
int nnp = N / 2; \
|
|
int np = N - nnp; \
|
|
M_ASSERT (nnp > 0 && np > 0 && nnp+np+1 == N+1); \
|
|
node_t nparent = M_F(name, _new_node)(); \
|
|
/* Move half items to new node (Like a classic B-TREE) \
|
|
and the median key to the grand-parent*/ \
|
|
memmove(&nparent->key[0], &parent->key[np+1], sizeof(key_t)*(unsigned int)nnp); \
|
|
memmove(&nparent->kind.node[0], &parent->kind.node[np+1], sizeof(node_t)*(unsigned int)(nnp+1)); \
|
|
parent->num = np; \
|
|
nparent->num = nnp; \
|
|
nparent->next = parent->next; \
|
|
parent->next = nparent; \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, parent, b->root); \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, nparent, b->root); \
|
|
/* Prepare for the next step */ \
|
|
key_ptr = &parent->key[np]; \
|
|
leaf = parent; \
|
|
nleaf = nparent; \
|
|
} \
|
|
} \
|
|
\
|
|
M_INLINE value_t *M_F(name, _safe_get)(tree_t b, key_t const key) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
/* Not optimized implementation */ \
|
|
value_t *ret = M_F(name, _get)(b, key); \
|
|
if (ret == NULL) { \
|
|
M_IF(isMap)( \
|
|
value_t v; \
|
|
M_CALL_INIT(value_oplist, v); \
|
|
M_F(name, _set_at)(b, key, v); \
|
|
M_CALL_CLEAR(value_oplist, v); \
|
|
, \
|
|
M_F(name, _push)(b, key); \
|
|
) \
|
|
ret = M_F(name, _get)(b, key); \
|
|
} \
|
|
return ret; \
|
|
} \
|
|
\
|
|
M_INLINE int \
|
|
M_F(name, _search_and_remove_in_leaf)(node_t n, key_t const key) \
|
|
{ \
|
|
M_ASSERT(M_F(name, _is_leaf)(n)); \
|
|
const int num = M_F(name, _get_num)(n); \
|
|
for(int i = 0; i < num; i++) { \
|
|
const int cmp = M_CALL_CMP(key_oplist, key, n->key[i]); \
|
|
if (cmp == 0) { \
|
|
/* found key ==> delete it */ \
|
|
M_CALL_CLEAR(key_oplist, n->key[i]); \
|
|
M_IF(isMap)(M_CALL_CLEAR(value_oplist, n->kind.value[i]);,) \
|
|
memmove(&n->key[i], &n->key[i+1], sizeof(key_t)*(unsigned int)(num-1-i)); \
|
|
M_IF(isMap)(memmove(&n->kind.value[i], &n->kind.value[i+1], sizeof(value_t)*(unsigned int)(num-1-i));,) \
|
|
n->num -= -1; /* decrease number as num is < 0 */ \
|
|
return i; \
|
|
} \
|
|
} \
|
|
return -1; /* Not found */ \
|
|
} \
|
|
\
|
|
M_INLINE void M_F(name, _left_shift)(node_t parent, int k) \
|
|
{ \
|
|
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
|
|
M_ASSERT (0 <= k && k < M_F(name, _get_num)(parent)); \
|
|
node_t left = parent->kind.node[k]; \
|
|
node_t right = parent->kind.node[k+1]; \
|
|
M_ASSERT (left != NULL && right != NULL); \
|
|
int num_left = M_F(name, _get_num)(left); \
|
|
int num_right = M_F(name, _get_num)(right); \
|
|
M_ASSERT (num_left > N/2); \
|
|
M_ASSERT (num_right < N/2); \
|
|
\
|
|
/* Move one item from the left node to the right node */ \
|
|
memmove(&right->key[1], &right->key[0], sizeof(key_t)*(unsigned int)num_right); \
|
|
if (M_F(name, _is_leaf)(left)) { \
|
|
M_IF(isMap)(memmove (&right->kind.value[1], &right->kind.value[0], sizeof(value_t)*(unsigned int)num_right);,) \
|
|
memmove (&right->key[0], &left->key[num_left-1], sizeof (key_t)); \
|
|
M_IF(isMap)(memmove (&right->kind.value[0], &left->kind.value[num_left-1], sizeof (value_t));,) \
|
|
right->num = -num_right - 1; \
|
|
left->num = -num_left + 1; \
|
|
M_CALL_SET(key_oplist, parent->key[k], left->key[num_left-2]); \
|
|
} else { \
|
|
memmove(&right->kind.node[1], &right->kind.node[0], sizeof(node_t)*(unsigned int)(num_right+1)); \
|
|
/* parent[k] is moved to right[0] (clear). parent[k] is therefore clear */ \
|
|
memmove(&right->key[0], &parent->key[k], sizeof(key_t)); \
|
|
right->kind.node[0] = left->kind.node[num_left]; \
|
|
right->num = num_right + 1; \
|
|
left->num = num_left - 1; \
|
|
/* left[n-1] is move to parent[k] (clear). left[n-1] is therefore clear */ \
|
|
memmove(&parent->key[k], &left->key[num_left-1], sizeof (key_t)); \
|
|
} \
|
|
M_ASSERT (right->num != 0); \
|
|
M_ASSERT (left->num != 0); \
|
|
} \
|
|
\
|
|
M_INLINE void M_F(name, _right_shift)(node_t parent, int k) \
|
|
{ \
|
|
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
|
|
M_ASSERT (0 <= k && k < M_F(name, _get_num)(parent)); \
|
|
node_t left = parent->kind.node[k]; \
|
|
node_t right = parent->kind.node[k+1]; \
|
|
M_ASSERT (left != NULL && right != NULL); \
|
|
int num_left = M_F(name, _get_num)(left); \
|
|
int num_right = M_F(name, _get_num)(right); \
|
|
M_ASSERT (num_left < N/2); \
|
|
M_ASSERT (num_right > N/2); \
|
|
\
|
|
/* Move one item from the right node to the left node. */ \
|
|
if (M_F(name, _is_leaf)(right)) { \
|
|
memmove (&left->key[num_left], &right->key[0], sizeof(key_t)); \
|
|
memmove (&right->key[0], &right->key[1], sizeof(key_t)*(unsigned int)(num_right-1)); \
|
|
M_IF(isMap)(memmove (&left->kind.value[num_left], &right->kind.value[0], sizeof (value_t));,) \
|
|
M_IF(isMap)(memmove (&right->kind.value[0], &right->kind.value[1], sizeof(value_t)*(unsigned int)(num_right-1));,) \
|
|
right->num = -num_right + 1; \
|
|
left->num = -num_left - 1; \
|
|
M_CALL_SET(key_oplist, parent->key[k], left->key[num_left]); \
|
|
} else { \
|
|
memmove (&left->key[num_left], &parent->key[k], sizeof (key_t)); \
|
|
memmove (&parent->key[k], &right->key[0], sizeof (key_t)); \
|
|
memmove (&right->key[0], &right->key[1], sizeof(key_t)*(unsigned int)(num_right-1)); \
|
|
left->kind.node[num_left+1] = right->kind.node[0]; \
|
|
memmove (&right->kind.node[0], &right->kind.node[1], sizeof(node_t)*(unsigned int)num_right); \
|
|
right->num = num_right - 1; \
|
|
left->num = num_left + 1; \
|
|
} \
|
|
M_ASSERT (right->num != 0); \
|
|
M_ASSERT (left->num != 0); \
|
|
} \
|
|
\
|
|
M_INLINE void M_F(name, _merge_node)(node_t parent, int k, bool leaf) \
|
|
{ \
|
|
M_ASSERT (parent != NULL && !M_F(name, _is_leaf)(parent)); \
|
|
M_ASSERT (0 <= k && k < M_F(name, _get_num(parent))); \
|
|
node_t left = parent->kind.node[k]; \
|
|
node_t right = parent->kind.node[k+1]; \
|
|
M_ASSERT (left != NULL && right != NULL); \
|
|
int num_parent = M_F(name, _get_num)(parent); \
|
|
int num_left = M_F(name, _get_num)(left); \
|
|
int num_right = M_F(name, _get_num)(right); \
|
|
\
|
|
/* Merge node 'k' and 'k+1' into a single one */ \
|
|
if (leaf) { \
|
|
M_ASSERT (num_left + num_right <= N); \
|
|
memmove(&left->key[num_left], &right->key[0], sizeof(key_t)*(unsigned int)num_right); \
|
|
M_IF(isMap)(memmove(&left->kind.value[num_left], &right->kind.value[0], sizeof(value_t)*(unsigned int)num_right);,) \
|
|
left->num = -num_left - num_right; \
|
|
} else { \
|
|
M_ASSERT (num_left + num_right <= N -1); \
|
|
memmove(&left->key[num_left+1], &right->key[0], sizeof(key_t)*(unsigned int)num_right); \
|
|
memmove(&left->kind.node[num_left+1], &right->kind.node[0], sizeof(node_t)*(unsigned int)(num_right+1)); \
|
|
M_CALL_INIT_SET(key_oplist, left->key[num_left], parent->key[k]); \
|
|
left->num = num_left + 1 + num_right; \
|
|
} \
|
|
left->next = right->next; \
|
|
M_CALL_DEL(key_oplist, right); \
|
|
/* remove k'th key from the parent */ \
|
|
M_CALL_CLEAR(key_oplist, parent->key[k]); \
|
|
memmove(&parent->key[k], &parent->key[k+1], sizeof(key_t)*(unsigned int)(num_parent - k - 1)); \
|
|
memmove(&parent->kind.node[k+1], &parent->kind.node[k+2], sizeof(node_t)*(unsigned int)(num_parent - k -1)); \
|
|
parent->num --; \
|
|
} \
|
|
\
|
|
/* We can also cache the index when we descend the tree. \
|
|
TODO: Bench if this is worth the effort.*/ \
|
|
M_INLINE int \
|
|
M_F(name, _search_for_node)(node_t parent, node_t child) \
|
|
{ \
|
|
M_ASSERT (!M_F(name, _is_leaf)(parent)); \
|
|
int i = 0; \
|
|
while (true) { \
|
|
M_ASSERT(i <= M_F(name, _get_num)(parent)); \
|
|
if (parent->kind.node[i] == child) \
|
|
return i; \
|
|
i++; \
|
|
} \
|
|
} \
|
|
\
|
|
M_INLINE bool M_F(name, _erase)(tree_t b, key_t const key) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
pit_t pit; \
|
|
node_t leaf = M_F(name, _search_for_leaf)(pit, b, key); \
|
|
int k = M_F(name, _search_and_remove_in_leaf)(leaf, key); \
|
|
/* If key is not found ==> erase failed */ \
|
|
if (k < 0) return false; \
|
|
/* Remove one item from the B+TREE */ \
|
|
b->size --; \
|
|
/* If number of keys greater than N>2 or root ==> Nothing more to do */ \
|
|
if (M_LIKELY (M_F(name, _get_num)(leaf) >= N/2) || pit->num == 0) \
|
|
return true; \
|
|
/* Leaf is too small. Needs rebalancing */ \
|
|
M_ASSERT (M_F(name, _get_num)(leaf) == N/2-1); \
|
|
bool pass1 = true; \
|
|
while (true) { \
|
|
M_ASSERT (pit->num > 0); \
|
|
/* Search for node 'leaf' in parent */ \
|
|
node_t parent = pit->parent[--pit->num]; \
|
|
M_ASSERT (parent != NULL); \
|
|
k = M_F(name, _search_for_node)(parent, leaf); \
|
|
/* Look for the neighboor of the removed key. */ \
|
|
/* if we can steal one key from them to keep our node balanced */ \
|
|
if (k > 0 && M_F(name, _get_num)(parent->kind.node[k-1]) > N/2) { \
|
|
M_F(name, _left_shift)(parent, k-1); \
|
|
return true; \
|
|
} else if (k < M_F(name, _get_num)(parent) \
|
|
&& M_F(name, _get_num)(parent->kind.node[k+1]) > N/2) { \
|
|
M_F(name, _right_shift)(parent, k); \
|
|
return true; \
|
|
} \
|
|
/* Merge both nodes, removing 'k' from parent */ \
|
|
if (k == M_F(name, _get_num)(parent)) \
|
|
k--; \
|
|
M_ASSERT(k >= 0 && k < M_F(name, _get_num)(parent)); \
|
|
/* Merge 'k' & 'k+1' & remove 'k' from parent */ \
|
|
M_F(name, _merge_node)(parent, k, pass1); \
|
|
/* Check if we need to continue */ \
|
|
if (M_F(name, _get_num)(parent) >= N/2) \
|
|
return true; \
|
|
if (pit->num == 0) { \
|
|
/* We reach the root */ \
|
|
if (M_F(name, _get_num)(parent) == 0) { \
|
|
/* Update root (deleted) */ \
|
|
b->root = parent->kind.node[0]; \
|
|
M_CALL_DEL(key_oplist, parent); \
|
|
} \
|
|
return true; \
|
|
} \
|
|
/* Next iteration */ \
|
|
leaf = parent; \
|
|
pass1 = false; \
|
|
} \
|
|
} \
|
|
\
|
|
M_INLINE bool M_F(name, _pop_at)(value_t *ptr, tree_t b, key_t const key) \
|
|
{ \
|
|
if (ptr != NULL) { \
|
|
value_t *ref = M_F(name, _get)(b, key); \
|
|
if (ref == NULL) { \
|
|
return false; \
|
|
} \
|
|
M_CALL_SET(value_oplist, *ptr, *ref); \
|
|
} \
|
|
return M_F(name, _erase)(b, key); \
|
|
} \
|
|
\
|
|
M_INLINE value_t * \
|
|
M_F(name, _min)(const tree_t b) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
if (M_UNLIKELY (b->size == 0)) return NULL; \
|
|
node_t n = b->root; \
|
|
/* Scan down the nodes */ \
|
|
while (!M_F(name, _is_leaf)(n)) { \
|
|
n = n->kind.node[0]; \
|
|
} \
|
|
return &n->M_IF(isMap)(kind.value, key)[0]; \
|
|
} \
|
|
\
|
|
M_INLINE value_t * \
|
|
M_F(name, _max)(const tree_t b) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
if (M_UNLIKELY (b->size == 0)) return NULL; \
|
|
node_t n = b->root; \
|
|
/* Scan down the nodes */ \
|
|
while (!M_F(name, _is_leaf)(n)) { \
|
|
n = n->kind.node[n->num]; \
|
|
} \
|
|
return &n->M_IF(isMap)(kind.value, key)[-n->num-1]; \
|
|
} \
|
|
\
|
|
M_INLINE value_t const * \
|
|
M_F(name, _cmin)(const tree_t tree) \
|
|
{ \
|
|
return M_CONST_CAST(value_t, M_F(name, _min)(tree)); \
|
|
} \
|
|
\
|
|
M_INLINE value_t const * \
|
|
M_F(name, _cmax)(const tree_t tree) \
|
|
{ \
|
|
return M_CONST_CAST(value_t, M_F(name, _max)(tree)); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _init_move)(tree_t b, tree_t ref) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, ref); \
|
|
M_ASSERT (b != NULL && b != ref); \
|
|
b->size = ref->size; \
|
|
b->root = ref->root; \
|
|
ref->root = NULL; \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _move)(tree_t b, tree_t ref) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, ref); \
|
|
M_ASSERT (b != ref); \
|
|
M_F(name,_clear)(b); \
|
|
M_F(name,_init_move)(b, ref); \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _swap)(tree_t tree1, tree_t tree2) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree1); \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree2); \
|
|
M_SWAP(size_t, tree1->size, tree2->size); \
|
|
M_SWAP(node_t, tree1->root, tree2->root); \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree1); \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, tree2); \
|
|
} \
|
|
|
|
/* Define iterator functions. */
|
|
#define M_BPTR33_DEF_IT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _it)(it_t it, const tree_t b) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
M_ASSERT (it != NULL); \
|
|
node_t n = b->root; \
|
|
/* Scan down the nodes */ \
|
|
while (!M_F(name, _is_leaf)(n)) { \
|
|
n = n->kind.node[0]; \
|
|
} \
|
|
it->node = n; \
|
|
it->idx = 0; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _it_end)(it_t it, const tree_t b) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
M_ASSERT (it != NULL); \
|
|
node_t n = b->root; \
|
|
/* Scan down the nodes */ \
|
|
while (!M_F(name, _is_leaf)(n)) { \
|
|
n = n->kind.node[n->num]; \
|
|
} \
|
|
it->node = n; \
|
|
it->idx = -n->num; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _it_set)(it_t itd, const it_t its) \
|
|
{ \
|
|
M_ASSERT (itd != NULL && its != NULL); \
|
|
itd->node = its->node; \
|
|
itd->idx = its->idx; \
|
|
} \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _end_p)(it_t it) \
|
|
{ \
|
|
M_ASSERT (it != NULL && it->node != NULL); \
|
|
M_ASSERT (M_F(name, _is_leaf)(it->node)); \
|
|
return it->node->next ==NULL && it->idx >= -it->node->num; \
|
|
} \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _it_equal_p)(const it_t it1, const it_t it2) \
|
|
{ \
|
|
return it1->node == it2->node && it1->idx == it2->idx; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _next)(it_t it) \
|
|
{ \
|
|
M_ASSERT (it != NULL && it->node != NULL); \
|
|
M_ASSERT (M_F(name, _is_leaf)(it->node)); \
|
|
it->idx ++; \
|
|
if (it->idx >= -it->node->num && it->node->next != NULL) { \
|
|
it->node = it->node->next; \
|
|
it->idx = 0; \
|
|
} \
|
|
} \
|
|
\
|
|
M_INLINE subtype_t * \
|
|
M_F(name, _ref)(it_t it) \
|
|
{ \
|
|
M_ASSERT (it != NULL && it->node != NULL); \
|
|
M_ASSERT (M_F(name, _is_leaf)(it->node)); \
|
|
M_ASSERT (it->idx <= -it->node->num); \
|
|
M_IF(isMap)( \
|
|
it->pair.key_ptr = &it->node->key[it->idx]; \
|
|
it->pair.value_ptr = &it->node->kind.value[it->idx]; \
|
|
return &it->pair \
|
|
, \
|
|
return &it->node->key[it->idx] \
|
|
); \
|
|
} \
|
|
\
|
|
M_INLINE subtype_t const * \
|
|
M_F(name, _cref)(it_t it) \
|
|
{ \
|
|
return M_CONST_CAST(subtype_t, M_F(name, _ref)(it)); \
|
|
} \
|
|
\
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _it_from)(it_t it, const tree_t b, key_t const key) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, b); \
|
|
M_ASSERT (it != NULL); \
|
|
pit_t pit; \
|
|
node_t n = M_F(name, _search_for_leaf)(pit, b, key); \
|
|
it->node = n; \
|
|
int i; \
|
|
M_BPTR33_NODE_CONTRACT(N, isMulti, key_oplist, n, b->root); \
|
|
for(i = 0; i < -n->num; i++) { \
|
|
if (M_CALL_CMP(key_oplist, key, n->key[i]) <= 0) \
|
|
break; \
|
|
} \
|
|
if (i == -n->num && n->next != NULL) { \
|
|
it->node = n->next; \
|
|
i = 0; \
|
|
} \
|
|
it->idx = i; \
|
|
} \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _it_until_p)(it_t it, key_t const key) \
|
|
{ \
|
|
M_ASSERT (it != NULL); \
|
|
node_t n = it->node; \
|
|
if (it->idx >= -n->num) return true; \
|
|
int cmp = M_CALL_CMP(key_oplist, n->key[it->idx], key); \
|
|
return (cmp >= 0); \
|
|
} \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _it_while_p)(it_t it, key_t const key) \
|
|
{ \
|
|
M_ASSERT (it != NULL); \
|
|
node_t n = it->node; \
|
|
if (it->idx >= -n->num) return false; \
|
|
int cmp = M_CALL_CMP(key_oplist, n->key[it->idx], key); \
|
|
return (cmp <= 0); \
|
|
} \
|
|
|
|
/* Define additional functions.
|
|
Do not used any fields but the already defined methods */
|
|
#define M_BPTR33_DEF_EXT(name, N, key_t, key_oplist, value_t, value_oplist, isMap, isMulti, tree_t, node_t, pit_t, it_t, subtype_t) \
|
|
\
|
|
M_IF_METHOD_BOTH(EQUAL, key_oplist, value_oplist)( \
|
|
M_INLINE bool M_F(name,_equal_p)(const tree_t t1, const tree_t t2) { \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t2); \
|
|
if (t1->size != t2->size) return false; \
|
|
if (t1->size == 0) return true; \
|
|
/* Slow comparaison */ \
|
|
it_t it1; \
|
|
it_t it2; \
|
|
/* NOTE: We can't compare two trees 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)) { \
|
|
const subtype_t *ref1 = M_F(name, _cref)(it1); \
|
|
const subtype_t *ref2 = M_F(name, _cref)(it2); \
|
|
M_IF(isMap)( \
|
|
if (!M_CALL_EQUAL(key_oplist, *ref1->key_ptr, *ref2->key_ptr)) \
|
|
return false; \
|
|
if (!M_CALL_EQUAL(value_oplist, *ref1->value_ptr, *ref2->value_ptr)) \
|
|
return false; \
|
|
, \
|
|
if (!M_CALL_EQUAL(key_oplist, *ref1, *ref2)) \
|
|
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_BOTH(HASH, key_oplist, value_oplist)( \
|
|
M_INLINE size_t M_F(name,_hash)(const tree_t t1) { \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, 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)) { \
|
|
subtype_t const *ref1 = M_F(name, _cref)(it1); \
|
|
M_IF(isMap)( \
|
|
M_HASH_UP(hash, M_CALL_HASH(key_oplist, *ref1->key_ptr)); \
|
|
M_HASH_UP(hash, M_CALL_HASH(value_oplist, *ref1->value_ptr)); \
|
|
, \
|
|
M_HASH_UP(hash, M_CALL_HASH(key_oplist, *ref1)); \
|
|
) \
|
|
M_F(name, _next)(it1); \
|
|
} \
|
|
return M_HASH_FINAL (hash); \
|
|
} \
|
|
, /* NO HASH METHOD */ ) \
|
|
\
|
|
M_IF_METHOD_BOTH(GET_STR, key_oplist, value_oplist)( \
|
|
M_INLINE void M_F(name, _get_str)(m_string_t str, \
|
|
const tree_t t1, bool append) { \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, 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 it; \
|
|
for (M_F(name, _it)(it, t1) ; \
|
|
!M_F(name, _end_p)(it); \
|
|
M_F(name, _next)(it)) { \
|
|
if (commaToPrint) \
|
|
m_string_push_back (str, M_GET_SEPARATOR key_oplist); \
|
|
commaToPrint = true; \
|
|
subtype_t const *ref1 = M_F(name, _cref)(it); \
|
|
M_IF(isMap)( \
|
|
M_CALL_GET_STR(key_oplist, str, *ref1->key_ptr, true); \
|
|
m_string_cat_cstr(str, ":"); \
|
|
M_CALL_GET_STR(value_oplist,str, *ref1->value_ptr, true) \
|
|
, \
|
|
M_CALL_GET_STR(key_oplist, str, *ref1, true); \
|
|
); \
|
|
} \
|
|
m_string_push_back (str, ']'); \
|
|
} \
|
|
, /* NO GET_STR */ ) \
|
|
\
|
|
M_IF_METHOD_BOTH(OUT_STR, key_oplist, value_oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _out_str)(FILE *file, tree_t const t1) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
|
M_ASSERT (file != NULL); \
|
|
fputc ('[', file); \
|
|
bool commaToPrint = false; \
|
|
it_t it; \
|
|
for (M_F(name, _it)(it, t1) ; \
|
|
!M_F(name, _end_p)(it); \
|
|
M_F(name, _next)(it)){ \
|
|
if (commaToPrint) \
|
|
fputc (M_GET_SEPARATOR key_oplist, file); \
|
|
commaToPrint = true; \
|
|
subtype_t const *ref1 = M_F(name, _cref)(it); \
|
|
M_IF(isMap)( \
|
|
M_CALL_OUT_STR(key_oplist, file, *ref1->key_ptr); \
|
|
fputc (':', file); \
|
|
M_CALL_OUT_STR(value_oplist, file, *ref1->value_ptr) \
|
|
, \
|
|
M_CALL_OUT_STR(key_oplist, file, *ref1); \
|
|
); \
|
|
} \
|
|
fputc (']', file); \
|
|
} \
|
|
, /* no out_str */ ) \
|
|
\
|
|
M_IF_METHOD_BOTH(PARSE_STR, key_oplist, value_oplist)( \
|
|
M_INLINE bool \
|
|
M_F(name, _parse_str)(tree_t t1, const char str[], const char **endp) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
|
M_ASSERT (str != NULL); \
|
|
M_F(name,_reset)(t1); \
|
|
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--; \
|
|
key_t key; \
|
|
M_CALL_INIT(key_oplist, key); \
|
|
M_IF(isMap)(value_t value; \
|
|
M_CALL_INIT(value_oplist, value); \
|
|
, /* No isMap */) \
|
|
do { \
|
|
bool b = M_CALL_PARSE_STR(key_oplist, key, str, &str); \
|
|
do { c = *str++; } while (isspace(c)); \
|
|
if (b == false) goto exit_clear; \
|
|
M_IF(isMap)(if (c != ':') goto exit_clear; \
|
|
b = M_CALL_PARSE_STR(value_oplist, value, str, &str); \
|
|
do { c = *str++; } while (isspace(c)); \
|
|
if (b == false || c == 0) goto exit_clear; \
|
|
M_F(name, _set_at)(t1, key, value); \
|
|
, \
|
|
M_F(name, _push)(t1, key); \
|
|
) \
|
|
} while (c == M_GET_SEPARATOR key_oplist); \
|
|
success = (c == ']'); \
|
|
exit_clear: \
|
|
M_CALL_CLEAR(key_oplist, key); \
|
|
M_IF(isMap)(M_CALL_CLEAR(value_oplist, value); , /* No isMap */ ) \
|
|
exit: \
|
|
if (endp) *endp = str; \
|
|
return success; \
|
|
} \
|
|
, /* no parse_str */ ) \
|
|
\
|
|
M_IF_METHOD_BOTH(IN_STR, key_oplist, value_oplist)( \
|
|
M_INLINE bool \
|
|
M_F(name, _in_str)(tree_t t1, FILE *file) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
|
M_ASSERT (file != NULL); \
|
|
M_F(name,_reset)(t1); \
|
|
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); \
|
|
key_t key; \
|
|
M_CALL_INIT (key_oplist, key); \
|
|
M_IF(isMap)(value_t value; \
|
|
M_CALL_INIT (value_oplist, value); \
|
|
,) \
|
|
do { \
|
|
bool b = M_CALL_IN_STR(key_oplist, key, file); \
|
|
do { c = fgetc(file); } while (isspace(c)); \
|
|
if (b == false) break; \
|
|
M_IF(isMap)(if (c!=':') break; \
|
|
b = M_CALL_IN_STR(value_oplist,value, file); \
|
|
do { c = fgetc(file); } while (isspace(c)); \
|
|
if (b == false || c == EOF) break; \
|
|
M_F(name, _set_at)(t1, key, value) \
|
|
, \
|
|
M_F(name, _push)(t1, key) \
|
|
); \
|
|
} while (c == M_GET_SEPARATOR key_oplist); \
|
|
M_CALL_CLEAR(key_oplist, key); \
|
|
M_IF(isMap)(M_CALL_CLEAR(value_oplist, value); \
|
|
,) \
|
|
return c == ']'; \
|
|
} \
|
|
, /* no in_str */ ) \
|
|
\
|
|
M_IF_METHOD_BOTH(OUT_SERIAL, key_oplist, value_oplist)( \
|
|
M_INLINE m_serial_return_code_t \
|
|
M_F(name, _out_serial)(m_serial_write_t f, tree_t const t1) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, t1); \
|
|
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
|
m_serial_return_code_t ret; \
|
|
m_serial_local_t local; \
|
|
subtype_t const *item; \
|
|
bool first_done = false; \
|
|
it_t it; \
|
|
/* Format is different between associative container \
|
|
& set container */ \
|
|
M_IF(isMap)( \
|
|
ret = f->m_interface->write_map_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_map_next(local, f); \
|
|
ret |= M_CALL_OUT_SERIAL(key_oplist, f, *item->key_ptr); \
|
|
ret |= f->m_interface->write_map_value(local, f); \
|
|
ret |= M_CALL_OUT_SERIAL(value_oplist, f, *item->value_ptr); \
|
|
first_done = true; \
|
|
} \
|
|
ret |= f->m_interface->write_map_end(local, f); \
|
|
, \
|
|
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(key_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_BOTH(IN_SERIAL, key_oplist, value_oplist)( \
|
|
M_INLINE m_serial_return_code_t \
|
|
M_F(name, _in_serial)(tree_t t1, m_serial_read_t f) \
|
|
{ \
|
|
M_BPTR33_CONTRACT(N, isMulti, key_oplist, 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; \
|
|
key_t key; \
|
|
M_F(name,_reset)(t1); \
|
|
M_IF(isMap)( \
|
|
value_t value; \
|
|
ret = f->m_interface->read_map_start(local, f, &estimated_size); \
|
|
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
|
M_CALL_INIT(key_oplist, key); \
|
|
M_CALL_INIT (value_oplist, value); \
|
|
do { \
|
|
ret = M_CALL_IN_SERIAL(key_oplist, key, f); \
|
|
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
|
|
ret = f->m_interface->read_map_value(local, f); \
|
|
if (ret != M_SERIAL_OK_CONTINUE) return M_SERIAL_FAIL; \
|
|
ret = M_CALL_IN_SERIAL(value_oplist, value, f); \
|
|
if (ret != M_SERIAL_OK_DONE) return M_SERIAL_FAIL; \
|
|
M_F(name, _set_at)(t1, key, value); \
|
|
} while ((ret = f->m_interface->read_map_next(local, f)) == M_SERIAL_OK_CONTINUE); \
|
|
M_CALL_CLEAR(key_oplist, key); \
|
|
M_CALL_CLEAR(value_oplist, value); \
|
|
, \
|
|
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
|
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \
|
|
M_CALL_INIT(key_oplist, key); \
|
|
do { \
|
|
ret = M_CALL_IN_SERIAL(key_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(key_oplist, key); \
|
|
) /* End of IF isMap */ \
|
|
return ret; \
|
|
} \
|
|
, /* no in_serial */ ) \
|
|
|
|
|
|
/******************************** INTERNAL ***********************************/
|
|
|
|
#if M_USE_SMALL_NAME
|
|
#define BPTREE_DEF2 M_BPTREE_DEF2
|
|
#define BPTREE_DEF2_AS M_BPTREE_DEF2_AS
|
|
#define BPTREE_DEF M_BPTREE_DEF
|
|
#define BPTREE_DEF_AS M_BPTREE_DEF_AS
|
|
#define BPTREE_MULTI_DEF2 M_BPTREE_MULTI_DEF2
|
|
#define BPTREE_MULTI_DEF2_AS M_BPTREE_MULTI_DEF2_AS
|
|
#define BPTREE_MULTI_DEF M_BPTREE_MULTI_DEF
|
|
#define BPTREE_MULTI_DEF_AS M_BPTREE_MULTI_DEF_AS
|
|
#define BPTREE_OPLIST M_BPTREE_OPLIST
|
|
#define BPTREE_OPLIST2 M_BPTREE_OPLIST2
|
|
#endif
|
|
|
|
#endif
|