16 Commits
18 changed files with 336 additions and 83 deletions

No files matched your search

+1 -1
View File
@@ -34,7 +34,7 @@ DESTDIR=
# Package name & version. # Package name & version.
PACKAGE=m_lib-$(VERSION) PACKAGE=m_lib-$(VERSION)
VERSION=0.8.0 VERSION=0.8.1
# Define the contain of the distribution tarball # Define the contain of the distribution tarball
HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.h m-genint.h m-i-list.h m-list.h m-thread.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h m-bstring.h m-shared-ptr.h m-queue.h HEADER=m-algo.h m-array.h m-atomic.h m-bitset.h m-bptree.h m-buffer.h m-core.h m-deque.h m-dict.h m-funcobj.h m-generic.h m-genint.h m-i-list.h m-list.h m-thread.h m-prioqueue.h m-rbtree.h m-serial-bin.h m-serial-json.h m-snapshot.h m-string.h m-tree.h m-try.h m-tuple.h m-variant.h m-worker.h m-bstring.h m-shared-ptr.h m-queue.h
+72 -26
View File
@@ -119,7 +119,7 @@ there is no overhead in using this library rather than using
direct C low-level access as the compiler is able to **fully** optimize direct C low-level access as the compiler is able to **fully** optimize
the library usage the library usage
and the library is carefully designed. and the library is carefully designed.
In [fact](https://github.com/P-p-H-d/mlib/wiki/performance), M\*LIB In [fact](https://github.com/P-p-H-d/c-stl-comparison/wiki/Benchmark-result), M\*LIB
is one of the fastest generic C/C++ library you can find. is one of the fastest generic C/C++ library you can find.
M\*LIB uses internally the `malloc`, `realloc` and `free` functions to handle M\*LIB uses internally the `malloc`, `realloc` and `free` functions to handle
@@ -139,8 +139,8 @@ You can read or write your full and complex data structure into [JSON](https://e
M\*LIB is distributed under BSD-2 simplified license. M\*LIB is distributed under BSD-2 simplified license.
It is strongly advised not to read the source to know how to use the library It is strongly advised not to read the source to know how to use the library
as the code is quite complex and uses a lot of tricks as the code is quite complex and uses a lot of tricks.
but rather read the examples. You should rather read the examples.
In this documentation, In this documentation,
* _shall_ will be used to indicate a user constraint that is * _shall_ will be used to indicate a user constraint that is
@@ -149,6 +149,22 @@ mandatory to follow under penalty of undefined behavior.
All pointers expected by the functions of the library shall expect non-null argument except if indicated. All pointers expected by the functions of the library shall expect non-null argument except if indicated.
## Key points
* Genericity Mechanism through code generation by macro-base approaches while keeping code size reasonable,
* Minimum supported C Standard is C99,
* Header only library (easier to integrate),
* Performance: M\*LIB is highly optimized with Minimal Runtime Overhead in Release mode,
* Type Safety: Compiler catches type mismatches.
* Runtime errors: Assertion are enabled in Debug mode and check for contract violiation and runtime errors are enabled for robustness in debug mode,
* High focus on homogeneous API design and ease of use,
* Near 100% coverage in instruction and decision by the test suite,
* Actively maintained library,
* Can be used fully without any macro usage outside of its code generation section,
* Provide all the standard containers (vectors, lists, hash maps, sets, queues, etc.) and algorithms (sort, search) and even more,
* Fully customisable memory allocator,
* Can use _Generic keyword for uniform usage of the operators on any registered type.
## Components ## Components
The following headers define containers that don't require the user structure to be modified: The following headers define containers that don't require the user structure to be modified:
@@ -530,7 +546,7 @@ Each graph is presented first in linear scale and then in logarithmic scale to b
M\*LIB is on par with the STL or even faster. M\*LIB is on par with the STL or even faster.
All used bench codes are available in this [repository](https://github.com/P-p-H-d/c-stl-comparison) All used bench codes are available in this [repository](https://github.com/P-p-H-d/c-stl-comparison)
The results for several different libraries are also available [in a separate page](https://github.com/P-p-H-d/mlib/wiki/performance). The results for several different libraries are also available [in a separate page](https://github.com/P-p-H-d/c-stl-comparison/wiki/Benchmark-result).
#### Singly List #### Singly List
@@ -806,14 +822,15 @@ Example:
INIT_SET(mpz_init_set),CLEAR(mpz_clear), INIT_MOVE(M_COPY_A1_DEFAULT),TYPE(mpz_t)) INIT_SET(mpz_init_set),CLEAR(mpz_clear), INIT_MOVE(M_COPY_A1_DEFAULT),TYPE(mpz_t))
``` ```
This can simplify a lot `OPLIST` usage and it is recommended. > [!NOTE]
> Registering globaly the oplist when defining its type simplify a lot the `OPLIST` usage and it is **highly** recommended.
Then each times a macro expects an oplist, you can give instead its type. Then each times a macro expects an oplist, you can give instead its type.
This make the code much easier to read and understand. This make the code much easier to read and understand.
There is one exception however: the macros that are used to build oplist There is one exception however: the macros that are used to build oplist
(like `ARRAY_OPLIST`) don't perform this simplification and the oplist of (like `ARRAY_OPLIST`) don't perform this simplification and the oplist of
the basic type shall be given instead the type shall be explictely given instead
(This is due to limitation in the C preprocessing). (This is due to limitation in the C preprocessing).
### API Interface Adaptation ### API Interface Adaptation
@@ -1398,7 +1415,6 @@ This method is created only if the `INIT_SET` operator is provided.
The `_back` suffixed method will push it in the back of the container. The `_back` suffixed method will push it in the back of the container.
The `_front` suffixed method will push it in the front of the container. The `_front` suffixed method will push it in the front of the container.
##### `type_t *name_push_raw(name_t container)`
##### `type_t *name_push_back_raw(name_t container)` ##### `type_t *name_push_back_raw(name_t container)`
##### `type_t *name_push_front_raw(name_t container)` ##### `type_t *name_push_front_raw(name_t container)`
@@ -1868,7 +1884,7 @@ void name_clear(name_t list)
void name_reset(name_t list) void name_reset(name_t list)
type *name_back(const name_t list) type *name_back(const name_t list)
void name_push_back(name_t list, const type value) void name_push_back(name_t list, const type value)
type *name_push_raw(name_t list) type *name_push_back_raw(name_t list)
type *name_push_new(name_t list) type *name_push_new(name_t list)
void name_push_move(name_t list, type *value) void name_push_move(name_t list, type *value)
void name_emplace_back[suffix](name_t list, args...) void name_emplace_back[suffix](name_t list, args...)
@@ -2190,7 +2206,7 @@ void name_init_move(name_t array, name_t ref)
void name_move(name_t array, name_t ref) void name_move(name_t array, name_t ref)
void name_clear(name_t array) void name_clear(name_t array)
void name_reset(name_t array) void name_reset(name_t array)
type *name_push_raw(name_t array) type *name_push_back_raw(name_t array)
void name_push_back(name_t array, const type value) void name_push_back(name_t array, const type value)
type *name_push_new(name_t array) type *name_push_new(name_t array)
void name_push_move(name_t array, type *val) void name_push_move(name_t array, type *val)
@@ -2453,6 +2469,13 @@ Both oplist shall have at least the following operators (`INIT_SET`, `SET` and `
otherwise it won't generate compilable code. otherwise it won't generate compilable code.
The `key_oplist` shall also define the additional operators (`HASH` and `EQUAL`). The `key_oplist` shall also define the additional operators (`HASH` and `EQUAL`).
> [!NOTE]
> You can either call this macro with only `key_type` and `value_type`
> or with their type+oplists `key_type`, `key_oplist`, `value_type` and `value_oplist`.
> You cannot call it with an oplist for a type and not for the other
> (Otheriwse it won't be able to identify reliabely what is the third argument - a `value_type` or a `key_oplist` ?)
> Failure to respect this rule so will result in a too few argument error for `M_D1CT_DEF2_P2` internal macro.
Elements in the dictionary are **unordered**. Elements in the dictionary are **unordered**.
On insertion of a new element, contained objects may moved. On insertion of a new element, contained objects may moved.
Maximum number of elements of this dictionary is 3'006'477'107 elements. Maximum number of elements of this dictionary is 3'006'477'107 elements.
@@ -2520,6 +2543,13 @@ The elements may move when inserting / deleting other elements (and not just the
This implementation is in general faster for small types of keys This implementation is in general faster for small types of keys
(like integer or float) but are slower for larger types. (like integer or float) but are slower for larger types.
> [!NOTE]
> You can either call this macro with only `key_type` and `value_type`
> or with their type+oplists `key_type`, `key_oplist`, `value_type` and `value_oplist`.
> You cannot call it with an oplist for a type and not for the other
> (Otheriwse it won't be able to identify reliabely what is the third argument - a `value_type` or a `key_oplist` ?)
> Failure to respect this rule so will result in a too few argument error for `M_D1CT_DEF2_P2` internal macro.
`DICT_OA_DEF2_AS` is the same as `DICT_OA_DEF2` `DICT_OA_DEF2_AS` is the same as `DICT_OA_DEF2`
except the name of the types `name_t`, `name_it_t`, `name_itref_t` are provided. except the name of the types `name_t`, `name_it_t`, `name_itref_t` are provided.
@@ -3561,6 +3591,14 @@ Return a pointer to the type of the node which is
respectively. respectively.
It returns NULL if there is no such node. It returns NULL if there is no such node.
##### `it_t name_up(it_t it)`
##### `it_t name_down(it_t it)`
##### `it_t name_left(it_t it)`
##### `it_t name_right(it_t it)`
Return an iterator to point to the node which is up (resp. down, left and right) the given iterator.
Return a valid iterator in case of success, an invalid one otherwse (see `name_end_p`)
##### `bool name_it_up(it_t *it)` ##### `bool name_it_up(it_t *it)`
##### `bool name_it_down(it_t *it)` ##### `bool name_it_down(it_t *it)`
##### `bool name_it_left(it_t *it)` ##### `bool name_it_left(it_t *it)`
@@ -8192,31 +8230,40 @@ Apply the function `func` to each element of the container `c`.
The function may transform the element provided it doesn't reallocate the The function may transform the element provided it doesn't reallocate the
object and if the container supports iterating over modifiable elements. object and if the container supports iterating over modifiable elements.
##### `void name_transform(container_t d, container_t c, void (*func)(type_t *out, const type_t in))` ##### `void name_transform(container_t d, container_t c, bool (*func)(type_t *out, const type_t in, void *data), void *data)`
Apply the function `func` to each element of the container `c` Reset the container `d`, then
and push the result into the container `d` so that `d = func(c)` apply the function `func` to each element of the container `c`
and push its output `*out` into the container `d` if its return value is true.
`func` shall output in the initialized object `out` `func` shall output in the initialized object `*out`
the transformed value of the constant object `in`. the transformed value of the constant object `in` if it decides this value is kept (its return value is true in this case).
Afterwards `out` is pushed moved into `d`. Otherwise its return value is false.
`data` is a user context data pointer free to be used and passed to `func`
This method is defined only if the base type exports an `INIT` method. This method is defined only if the base type exports an `INIT` method and if the container exports a `PUSH` method.
This method is defined only if the container exports a `PUSH_MOVE` method. `c` and `d` shall not be the same containers.
`c` and `d` cannot be the same containers.
##### `void name_reduce(type_t *dest, const container_t c, void (*func)(type_t *, type_t const))` ##### `bool name_reduce(type_t *dest, const container_t c, void (*func)(type_t *, type_t const))`
Perform a reduction using the function `func` to the elements of the container `c`. Perform a reduction using the function `func` to the elements of the container `c`.
The final result is stored in `*dest`. The final result is stored in `*dest`.
If there is no element, `*dest` is let unmodified. If there is no element, `*dest` is let unmodified and it returns `false`.
Otherwise it returns `true` and `*dest` is modified.
##### `void name_map_reduce(type_t *dest, const container_t c, void (*redFunc)(type_t *, type_t const), void *(mapFunc)(type_t *, type_t const))` ##### `bool name_transform_reduce(type_t *dest, const container_t c, void (*redFunc)(type_t *, type_t const), bool *(mapFunc)(type_t *, type_t const, void *data), void *data)`
Perform a reduction using the function `redFunc` Perform a reduction using the function `redFunc`
to the transformed elements of the container `c` using `mapFunc`. to the transformed and kept elements of the container `c` using `mapFunc`.
The final result is stored in `*dest`. The final result is stored in `*dest`.
If there is no element, `*dest` is let unmodified.
`func` shall output in the initialized object `*out`
the transformed value of the constant object `in` if it decides this value is kept (its return value is true in this case).
Otherwise its return value is false.
`data` is a user context data pointer free to be used and passed to `func`
If there is no element, `*dest` is let unmodified and it returns `false`.
Otherwise it returns `true` and `*dest` is modified.
##### `bool name_any_of_p(const container_t c, void *(func)(const type_t))` ##### `bool name_any_of_p(const container_t c, void *(func)(const type_t))`
@@ -9189,7 +9236,7 @@ The following methods are modified to support the memory context parameter (the
* \<array\>_init_set * \<array\>_init_set
* \<array\>_move * \<array\>_move
* \<array\>_set_at * \<array\>_set_at
* \<array\>_push_raw * \<array\>_push_back_raw
* \<array\>_push_back * \<array\>_push_back
* \<array\>_push_new * \<array\>_push_new
* \<array\>_push_move * \<array\>_push_move
@@ -9288,7 +9335,7 @@ The following methods are modified to support the memory context parameter (the
* m-list * m-list
* \<list\>_reset * \<list\>_reset
* \<list\>_clear * \<list\>_clear
* \<list\>_push_raw * \<list\>_push_back_raw
* \<list\>_push_back * \<list\>_push_back
* \<list\>_push_new * \<list\>_push_new
* \<list\>_pop_back * \<list\>_pop_back
@@ -9308,7 +9355,6 @@ The following methods are modified to support the memory context parameter (the
* \<list\>_\<emplace_back\> * \<list\>_\<emplace_back\>
* \<list\>_\<emplace_front\> * \<list\>_\<emplace_front\>
* \<list\>_push_back_raw * \<list\>_push_back_raw
* \<list\>_push_raw
* \<list\>_push_back * \<list\>_push_back
* \<list\>_push_back_new * \<list\>_push_back_new
* \<list\>_push_back_move * \<list\>_push_back_move
+4
View File
@@ -108,3 +108,7 @@ V0.8.0 : * Memory interface massive compatibility break to allow more efficient
(It's because supporting emulation layer with new memory context parameter was complicated) (It's because supporting emulation layer with new memory context parameter was complicated)
* Remove obsole headers: m-c-mempool, m-mempool, m-shared, m-i-shared, -mconcurrent * Remove obsole headers: m-c-mempool, m-mempool, m-shared, m-i-shared, -mconcurrent
V0.8.1 : * m-algo: Update interface of _transform to be more flexible: adding user context pointer, and filtering of the input elements.
* m-algo: Update interface of _reduce to be more user friendly: return value indicates if the reduction is empty or not.
* m-algo: Rename _map_reduce into _transform_reduce and update its interface to be more flexible: adding user context pointer, and filtering of the input elements and return value indicates if the reduction is empty or not.
* Rename _push_raw method by _push_back_raw for all containers (clearer on the semantic)
+4 -2
View File
@@ -13,8 +13,10 @@ ALGO_DEF(array_mpz, ARRAY_OPLIST(array_mpz, M_CLASSIC_OPLIST(mpz)))
static inline void my_mpz_inc(mpz_t *d, const mpz_t a){ static inline void my_mpz_inc(mpz_t *d, const mpz_t a){
mpz_add(*d, *d, a); mpz_add(*d, *d, a);
} }
static inline void my_mpz_sqr(mpz_t *d, const mpz_t a){ static inline bool my_mpz_sqr(mpz_t *d, const mpz_t a, void *data){
(void) data;
mpz_mul(*d, a, a); mpz_mul(*d, a, a);
return true;
} }
int main(void) int main(void)
@@ -31,7 +33,7 @@ int main(void)
} }
/* Compute z = sum (a[i]^2) */ /* Compute z = sum (a[i]^2) */
array_mpz_map_reduce (&z, a, my_mpz_inc, my_mpz_sqr); array_mpz_transform_reduce (&z, a, my_mpz_inc, my_mpz_sqr, NULL);
gmp_printf ("Z=%Zd\n", z); gmp_printf ("Z=%Zd\n", z);
array_mpz_clear(a); array_mpz_clear(a);
+39
View File
@@ -0,0 +1,39 @@
#include <stdio.h>
#include "m-tree.h"
TREE_DEF(tree, int)
// Compute sum of the tree using iterator and a pre-order walk
static int tree_sum_iter(tree_t t)
{
int s = 0;
for(tree_it_t it = tree_it(t); !tree_end_p(it); tree_next(&it)) {
s += *tree_cref(it);
}
return s;
}
// Compute sum of the tree using recursive call
static int tree_sum_recur(tree_it_t it)
{
if (tree_end_p(it))
return 0;
if (tree_leaf_p(it))
return *tree_cref(it);
assert( tree_degree(it) == 2);
return *tree_cref(it) + tree_sum_recur(tree_down(it)) + tree_sum_recur(tree_right(tree_down(it)));
}
int main(void)
{
tree_t t;
tree_init(t);
tree_it_t r = tree_set_root(t, 1);
tree_it_t l = tree_insert_child(r, 2);
tree_insert_child(r, 5);
tree_insert_child(l, 3);
tree_insert_child(l, 4);
printf("sum = %d vs %d\n", tree_sum_iter(t), tree_sum_recur(tree_it(t)));
tree_clear(t);
return 0;
}
+69
View File
@@ -0,0 +1,69 @@
#include <stdio.h>
#include "m-array.h"
#include "m-algo.h"
#include "m-string.h"
ARRAY_DEF(array_int, int)
#define M_OPL_array_int_t() ARRAY_OPLIST(array_int, M_BASIC_OPLIST)
ALGO_DEF(array_int, array_int_t)
static bool callback1(int *out, int in, void *data)
{
int *cpt = data;
bool ret = true;
if (*cpt == -1 && in == 11)
*cpt = 0; // Start when first reach 11
ret &= (*cpt >= 0); // Skip initial numbers unequal 11
ret &= (in % 2 != 0); // Collect odd numbers
(*cpt) += ret;
ret &= (*cpt <= 5); // Only take five numbers
*out = in * in; // Square each number
return ret;
}
static void callback_add_int(int *out, int in)
{
*out += in;
}
static void demo(void)
{
M_LET( (t, M_SEQ(0, 30)), vector, array_int_t) {
printf("t="); array_int_out_str(stdout, t);
int b = -1;
array_int_transform(vector, t, callback1, &b);
printf("\nvector="); array_int_out_str(stdout, vector); printf("\n");
array_int_reduce(&b, vector, callback_add_int);
printf("Sum=%d\n", b);
}
}
ARRAY_DEF(array_str, string_t)
#define M_OPL_array_str_t() ARRAY_OPLIST(array_str, M_STRING_OPLIST)
ALGO_DEF(array_str, array_str_t)
static bool callback2(string_t *out, const string_t in, void *data)
{
char *cp = data, c = *cp;
if (string_search_char(in, c) == M_STRING_FAILURE)
return false;
string_set(*out, in);
return true;
}
static void demo_split(void)
{
M_LET( (s, ("This is a sentence in C11.")), string_t)
M_LET(words, f_words, array_str_t) {
array_str_split(words, s, ' ');
array_str_transform(f_words, words, callback2, &(char){'i'});
printf("f_words="); array_str_out_str(stdout, f_words); printf("\n");
}
}
int main(void)
{
demo();
demo_split();
return 0;
}
+21 -18
View File
@@ -170,7 +170,8 @@
typedef bool (*M_F(name, _test_cb_ct))(type_t const); \ typedef bool (*M_F(name, _test_cb_ct))(type_t const); \
typedef bool (*M_F(name, _eq_cb_ct))(type_t const, type_t const); \ typedef bool (*M_F(name, _eq_cb_ct))(type_t const, type_t const); \
typedef int (*M_F(name, _cmp_cb_ct))(type_t const, type_t const); \ typedef int (*M_F(name, _cmp_cb_ct))(type_t const, type_t const); \
typedef void (*M_F(name, _transform_cb_ct))(type_t *, type_t const); \ typedef bool (*M_F(name, _transform_cb_ct))(type_t *, type_t const, void*); \
typedef void (*M_F(name, _reduce_cb_ct))(type_t *, type_t const); \
typedef void (*M_F(name, _apply_cb_ct))(type_t M_IF_METHOD(IT_REF, cont_oplist)( , const) ); typedef void (*M_F(name, _apply_cb_ct))(type_t M_IF_METHOD(IT_REF, cont_oplist)( , const) );
@@ -181,7 +182,7 @@
M_FUNC_OBJ_ITF_DEF(M_F(name, _test_obj), bool, type_t const) \ M_FUNC_OBJ_ITF_DEF(M_F(name, _test_obj), bool, type_t const) \
M_FUNC_OBJ_ITF_DEF(M_F(name, _eq_obj), bool, type_t const, type_t const ) \ M_FUNC_OBJ_ITF_DEF(M_F(name, _eq_obj), bool, type_t const, type_t const ) \
M_FUNC_OBJ_ITF_DEF(M_F(name, _cmp_obj), int, type_t const, type_t const ) \ M_FUNC_OBJ_ITF_DEF(M_F(name, _cmp_obj), int, type_t const, type_t const ) \
M_FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), void, type_t *, type_t const )\ M_FUNC_OBJ_ITF_DEF(M_F(name, _transform_obj), bool, type_t *, type_t const )\
M_FUNC_OBJ_ITF_DEF(M_F(name, _apply_obj), void, type_t * ) \ M_FUNC_OBJ_ITF_DEF(M_F(name, _apply_obj), void, type_t * ) \
@@ -756,27 +757,28 @@
} \ } \
\ \
M_IF_METHOD(INIT, type_oplist)( \ M_IF_METHOD(INIT, type_oplist)( \
M_IF_METHOD(PUSH_MOVE, cont_oplist)( \ M_IF_METHOD(PUSH, cont_oplist)( \
\ \
/* Apply func for all elements of the container src and push the result in dst */ \ /* Apply func for all elements of the container src and push the result in dst */ \
M_P(void, name, _transform, container_t dst, container_t src, M_F(name, _transform_cb_ct) func) \ M_P(void, name, _transform, container_t dst, container_t src, M_F(name, _transform_cb_ct) func, void *data) \
{ \ { \
M_ASSERT(dst != src); \ M_ASSERT(dst != src); \
M_CALL_RESET(cont_oplist, dst); \ M_CALL_RESET(cont_oplist, dst); \
type_t tmp; \
M_CALL_INIT(type_oplist, tmp); \
for M_EACH(item, src, cont_oplist) { \ for M_EACH(item, src, cont_oplist) { \
type_t tmp; \ if (func(&tmp, *item, data)) { \
M_CALL_INIT(type_oplist, tmp); \ M_CALL_PUSH(cont_oplist, dst, tmp); \
func(&tmp, *item); \ } \
M_CALL_PUSH_MOVE(cont_oplist, dst, &tmp); \
} \ } \
M_CALL_CLEAR(type_oplist, tmp); \
M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \ M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \
} \ } \
, /* END PUSH_MOVE */), /* END INIT */ ) \ , /* END PUSH_MOVE */), /* END INIT */ ) \
\ \
/* Reduce all elements of the container in dst in function of func */ \ /* Reduce all elements of the container in dst in function of func */ \
M_IF_METHOD(SET, type_oplist)( \ M_IF_METHOD(SET, type_oplist)( \
M_P(void, name, _reduce, type_t *dest, container_t const l, \ M_P(bool, name, _reduce, type_t *dest, container_t const l, M_F(name, _reduce_cb_ct) func) \
M_F(name, _transform_cb_ct) func) \
{ \ { \
M_UNUSED_CONTEXT(); \ M_UNUSED_CONTEXT(); \
bool initDone = false; \ bool initDone = false; \
@@ -788,15 +790,15 @@
initDone = true; \ initDone = true; \
} \ } \
} \ } \
return initDone; \
} \ } \
, /* END SET */) \ , /* END SET */) \
\ \
/* Reduce all transformed elements of the container in dst in function of func */ \ /* Reduce all transformed elements of the container in dst in function of func */ \
M_IF_METHOD(INIT, type_oplist)( \ M_IF_METHOD(INIT, type_oplist)( \
M_P(void, name, _map_reduce, type_t *dest, \ M_P(bool, name, _transform_reduce, type_t *dest, const container_t l, \
const container_t l, \ M_F(name, _reduce_cb_ct) redFunc, \
M_F(name, _transform_cb_ct) redFunc, \ M_F(name, _transform_cb_ct) mapFunc, void *data) \
M_F(name, _transform_cb_ct) mapFunc) \
{ \ { \
M_UNUSED_CONTEXT(); \ M_UNUSED_CONTEXT(); \
bool initDone = false; \ bool initDone = false; \
@@ -804,14 +806,15 @@
M_CALL_INIT(type_oplist, tmp); \ M_CALL_INIT(type_oplist, tmp); \
for M_EACH(item, l, cont_oplist) { \ for M_EACH(item, l, cont_oplist) { \
if (initDone) { \ if (initDone) { \
mapFunc(&tmp, *item); \ if (mapFunc(&tmp, *item, data) ) { \
redFunc(dest, tmp); \ redFunc(dest, tmp); \
} \
} else { \ } else { \
mapFunc(dest, *item); \ initDone = mapFunc(dest, *item, data); \
initDone = true; \
} \ } \
} \ } \
M_CALL_CLEAR(type_oplist, tmp); \ M_CALL_CLEAR(type_oplist, tmp); \
return initDone; \
} \ } \
, ) \ , ) \
+5 -5
View File
@@ -367,7 +367,7 @@
return &v->ptr[v->size-1]; \ return &v->ptr[v->size-1]; \
} \ } \
\ \
M_P(type *, name, _push_raw, array_t v) \ M_P(type *, name, _push_back_raw, array_t v) \
{ \ { \
M_ARRA4_CONTRACT(v); \ M_ARRA4_CONTRACT(v); \
if (M_UNLIKELY (v->size >= v->alloc)) { \ if (M_UNLIKELY (v->size >= v->alloc)) { \
@@ -395,7 +395,7 @@
M_IF_METHOD(INIT_SET, oplist)( \ M_IF_METHOD(INIT_SET, oplist)( \
M_P(void, name, _push_back, array_t v, type const x) \ M_P(void, name, _push_back, array_t v, type const x) \
{ \ { \
type *data = M_F(name, _push_raw) M_R(v); \ type *data = M_F(name, _push_back_raw) M_R(v); \
if (M_UNLIKELY (data == NULL) ) \ if (M_UNLIKELY (data == NULL) ) \
return; \ return; \
M_IF_EXCEPTION( v->size --); \ M_IF_EXCEPTION( v->size --); \
@@ -407,7 +407,7 @@
M_IF_METHOD(INIT, oplist)( \ M_IF_METHOD(INIT, oplist)( \
M_P(type *, name, _push_new, array_t v) \ M_P(type *, name, _push_new, array_t v) \
{ \ { \
type *data = M_F(name, _push_raw) M_R(v); \ type *data = M_F(name, _push_back_raw) M_R(v); \
if (M_UNLIKELY (data == NULL) ) \ if (M_UNLIKELY (data == NULL) ) \
return NULL; \ return NULL; \
M_IF_EXCEPTION( v->size --); \ M_IF_EXCEPTION( v->size --); \
@@ -420,7 +420,7 @@
M_P(void, name, _push_move, array_t v, type *x) \ M_P(void, name, _push_move, array_t v, type *x) \
{ \ { \
M_ASSERT (x != NULL); \ M_ASSERT (x != NULL); \
type *data = M_F(name, _push_raw) M_R(v); \ type *data = M_F(name, _push_back_raw) M_R(v); \
if (M_UNLIKELY (data == NULL) ) \ if (M_UNLIKELY (data == NULL) ) \
return; \ return; \
M_CALL_INIT_MOVE (oplist, *data, *x); \ M_CALL_INIT_MOVE (oplist, *data, *x); \
@@ -1136,7 +1136,7 @@
#define M_ARRA4_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ #define M_ARRA4_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \ { \
M_F(name, _subtype_ct) *data = M_F(name, _push_raw) M_R(v); \ M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw) M_R(v); \
if (M_UNLIKELY (data == NULL) ) \ if (M_UNLIKELY (data == NULL) ) \
return; \ return; \
M_IF_EXCEPTION( v->size --); \ M_IF_EXCEPTION( v->size --); \
+4 -4
View File
@@ -71,7 +71,7 @@
/* Define M*LIB version */ /* Define M*LIB version */
#define M_CORE_VERSION_MAJOR 0 #define M_CORE_VERSION_MAJOR 0
#define M_CORE_VERSION_MINOR 8 #define M_CORE_VERSION_MINOR 8
#define M_CORE_VERSION_PATCHLEVEL 0 #define M_CORE_VERSION_PATCHLEVEL 1
/* M_ASSUME is equivalent to M_ASSERT, but gives hints to compiler /* M_ASSUME is equivalent to M_ASSERT, but gives hints to compiler
about how to optimize the code if NDEBUG is defined. about how to optimize the code if NDEBUG is defined.
@@ -4748,7 +4748,7 @@ m_core_parse2_enum (const char str[], const char **endptr)
/* Initialize the container 'dest' as per 'oplist' INIT operator /* Initialize the container 'dest' as per 'oplist' INIT operator
and fill it with the given VA_ARGS arguments with the _push_raw method and fill it with the given VA_ARGS arguments with the _push_back_raw method
allowing INIT_WITH operator for the pushed argument if the given allowing INIT_WITH operator for the pushed argument if the given
argument is between parenthesis, otherwise an INIT_SET is performed. argument is between parenthesis, otherwise an INIT_SET is performed.
NOTE: Parenthesis detection is the best we can do to detect a special NOTE: Parenthesis detection is the best we can do to detect a special
@@ -4766,11 +4766,11 @@ m_core_parse2_enum (const char str[], const char **endptr)
M_INIT_WITH_VAI23_FUNC(M_PAIR_1 pair, M_PAIR_2 pair, a) M_INIT_WITH_VAI23_FUNC(M_PAIR_1 pair, M_PAIR_2 pair, a)
/* We first push a raw new item, and then we get back its pointer using _back. /* We first push a raw new item, and then we get back its pointer using _back.
_back (contrary to _push_raw) has no side effect, and so is safe to be used _back (contrary to _push_back_raw) has no side effect, and so is safe to be used
in a macro */ in a macro */
#define M_INIT_WITH_VAI23_FUNC(d, op, a) \ #define M_INIT_WITH_VAI23_FUNC(d, op, a) \
( \ ( \
(void) M_C(M_GET_NAME op, _push_raw)(d), \ (void) M_C(M_GET_NAME op, _push_back_raw)(d), \
M_IF(M_PARENTHESIS_P(a)) \ M_IF(M_PARENTHESIS_P(a)) \
(M_CALL_INIT_WITH, M_CALL_INIT_SET) \ (M_CALL_INIT_WITH, M_CALL_INIT_SET) \
(M_GET_OPLIST op, *M_C(M_GET_NAME op, _back)(d), M_REMOVE_PARENTHESIS(a)) \ (M_GET_OPLIST op, *M_C(M_GET_NAME op, _back)(d), M_REMOVE_PARENTHESIS(a)) \
-6
View File
@@ -280,12 +280,6 @@
return ret; \ return ret; \
} \ } \
\ \
/* Internal, for generic INIT_WITH */ \
M_P(type *, name, _push_raw, deque_t d) \
{ \
return M_F(name, _push_back_raw)M_R(d); \
} \
\
M_P(void, name, _push_back, deque_t d, type const x) \ M_P(void, name, _push_back, deque_t d, type const x) \
{ \ { \
type *p = M_F(name, _push_back_raw)M_R(d); \ type *p = M_F(name, _push_back_raw)M_R(d); \
+8 -2
View File
@@ -325,6 +325,9 @@ M_ARRAY_DEF(m_array_index, m_indexhash_t, M_POD_OPLIST)
{ \ { \
map->upper_limit = (m_index_t) ((double) size * M_D1CT_OA_UPPER_BOUND) - 1; \ map->upper_limit = (m_index_t) ((double) size * M_D1CT_OA_UPPER_BOUND) - 1; \
map->lower_limit = (size <= M_D1CT_INITIAL_SIZE) ? 0 : (m_index_t) ((double) size * M_D1CT_OA_LOWER_BOUND) ; \ map->lower_limit = (size <= M_D1CT_INITIAL_SIZE) ? 0 : (m_index_t) ((double) size * M_D1CT_OA_LOWER_BOUND) ; \
if (M_UNLIKELY(map->count <= map->lower_limit)) { \
map->lower_limit = 0; \
} \
} \ } \
\ \
M_P(void, name, _init, dict_t map) \ M_P(void, name, _init, dict_t map) \
@@ -1534,6 +1537,9 @@ enum m_d1ct_oa_element_e {
/* FIXME: Overflow not handled. What to do in case of it? */ \ /* FIXME: Overflow not handled. What to do in case of it? */ \
dict->upper_limit = (size_t) ((double) size * coeff_up) - 1; \ dict->upper_limit = (size_t) ((double) size * coeff_up) - 1; \
dict->lower_limit = (size <= M_D1CT_INITIAL_SIZE) ? 0 : (size_t) ((double) size * coeff_down) ; \ dict->lower_limit = (size <= M_D1CT_INITIAL_SIZE) ? 0 : (size_t) ((double) size * coeff_down) ; \
if (M_UNLIKELY(dict->count <= dict->lower_limit)) { \
dict->lower_limit = 0; \
} \
} \ } \
\ \
M_P(void, name, _init, dict_t dict) \ M_P(void, name, _init, dict_t dict) \
@@ -1662,7 +1668,7 @@ enum m_d1ct_oa_element_e {
M_CALL_INIT_MOVE(key_oplist, data[p].key, data[i].key); \ M_CALL_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
M_CALL_INIT_MOVE(value_oplist, data[p].value, data[i].value); \ M_CALL_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
} else { \ } else { \
M_F(name, _pair_ct) *ptr = M_F(name, _array_pair_push_raw)M_R(tmp); \ M_F(name, _pair_ct) *ptr = M_F(name, _array_pair_push_back_raw)M_R(tmp); \
M_CALL_INIT_MOVE(key_oplist, ptr->key, data[i].key); \ M_CALL_INIT_MOVE(key_oplist, ptr->key, data[i].key); \
M_CALL_INIT_MOVE(value_oplist, ptr->value, data[i].value); \ M_CALL_INIT_MOVE(value_oplist, ptr->value, data[i].value); \
} \ } \
@@ -1836,7 +1842,7 @@ enum m_d1ct_oa_element_e {
M_CALL_INIT_MOVE(key_oplist, data[p].key, data[i].key); \ M_CALL_INIT_MOVE(key_oplist, data[p].key, data[i].key); \
M_CALL_INIT_MOVE(value_oplist, data[p].value, data[i].value); \ M_CALL_INIT_MOVE(value_oplist, data[p].value, data[i].value); \
} else { \ } else { \
M_F(name, _pair_ct) *ptr = M_F(name, _array_pair_push_raw)M_R(tmp); \ M_F(name, _pair_ct) *ptr = M_F(name, _array_pair_push_back_raw)M_R(tmp); \
M_CALL_INIT_MOVE(key_oplist, ptr->key, data[i].key); \ M_CALL_INIT_MOVE(key_oplist, ptr->key, data[i].key); \
M_CALL_INIT_MOVE(value_oplist, ptr->value, data[i].value); \ M_CALL_INIT_MOVE(value_oplist, ptr->value, data[i].value); \
} \ } \
+5 -11
View File
@@ -293,7 +293,7 @@
return &((*v)->data); \ return &((*v)->data); \
} \ } \
\ \
M_P(type *, name, _push_raw, list_t v) \ M_P(type *, name, _push_back_raw, list_t v) \
{ \ { \
M_L1ST_CONTRACT(v); \ M_L1ST_CONTRACT(v); \
struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \ struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
@@ -309,7 +309,7 @@
\ \
M_P(void, name, _push_back, list_t v, type const x) \ M_P(void, name, _push_back, list_t v, type const x) \
{ \ { \
type *data = M_F(name, _push_raw)M_R(v); \ type *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \ if (M_UNLIKELY (data == NULL)) \
return; \ return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \ M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
@@ -321,7 +321,7 @@
M_IF_METHOD(INIT, oplist)( \ M_IF_METHOD(INIT, oplist)( \
M_P(type *, name, _push_new, list_t v) \ M_P(type *, name, _push_new, list_t v) \
{ \ { \
type *data = M_F(name, _push_raw)M_R(v); \ type *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \ if (M_UNLIKELY (data == NULL)) \
return NULL; \ return NULL; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \ M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
@@ -350,7 +350,7 @@
M_P(void, name, _push_move, list_t v, type *x) \ M_P(void, name, _push_move, list_t v, type *x) \
{ \ { \
M_ASSERT (x != NULL); \ M_ASSERT (x != NULL); \
type *data = M_F(name, _push_raw)M_R(v); \ type *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \ if (M_UNLIKELY (data == NULL)) \
return; \ return; \
M_CALL_INIT_MOVE (oplist, *data, *x); \ M_CALL_INIT_MOVE (oplist, *data, *x); \
@@ -892,7 +892,7 @@
#define M_L1ST_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \ #define M_L1ST_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \ M_P(void, name, function_name, name_t v M_EMPLACE_LIST_TYPE_VAR(a, exp_emplace_type) ) \
{ \ { \
M_F(name, _subtype_ct) *data = M_F(name, _push_raw)M_R(v); \ M_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL) ) \ if (M_UNLIKELY (data == NULL) ) \
return; \ return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \ M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
@@ -1066,12 +1066,6 @@
return ret; \ return ret; \
} \ } \
\ \
/* Internal, for INIT_WITH */ \
M_P(type *, name, _push_raw, list_t d) \
{ \
return M_F(name, _push_back_raw)M_R(d); \
} \
\
M_P(void, name, _push_back, list_t v, type const x) \ M_P(void, name, _push_back, list_t v, type const x) \
{ \ { \
type *data = M_F(name, _push_back_raw)M_R(v); \ type *data = M_F(name, _push_back_raw)M_R(v); \
+1 -1
View File
@@ -382,7 +382,7 @@ M_P(char *, m_str1ng, _fit2size, m_string_t v, size_t size_alloc)
alloc = m_str1ng_round_capacity(&m, &e, (m_str1ng_size_t) alloc); alloc = m_str1ng_round_capacity(&m, &e, (m_str1ng_size_t) alloc);
if (M_UNLIKELY_NOMEM (alloc <= size_alloc)) { if (M_UNLIKELY_NOMEM (alloc <= size_alloc)) {
/* Overflow in alloc computation */ /* Overflow in alloc computation */
M_MEMORY_FULL(char, alloc); M_MEMORY_FULL(char, size_alloc);
} }
char *ptr = m_str1ng_embedded_p(v) ? NULL : v->u.heap.ptr; char *ptr = m_str1ng_embedded_p(v) ? NULL : v->u.heap.ptr;
//FIXME: old_alloc may not be NULL if ptr is NULL. //FIXME: old_alloc may not be NULL if ptr is NULL.
+28
View File
@@ -669,6 +669,34 @@ typedef int32_t m_tr33_index_t;
return ret; \ return ret; \
} \ } \
\ \
M_INLINE it_t \
M_F(name, _up)(it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
it_t i = { it.tree, it.tree->tab[it.index].parent }; \
return i; \
} \
\
M_INLINE it_t \
M_F(name, _down)(it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
it_t i = { it.tree, it.tree->tab[it.index].child }; \
return i; \
} \
\
M_INLINE it_t \
M_F(name, _left)(it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
it_t i = { it.tree, it.tree->tab[it.index].left }; \
return i; \
} \
\
M_INLINE it_t \
M_F(name, _right)(it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
it_t i = { it.tree, it.tree->tab[it.index].right }; \
return i; \
} \
\
M_INLINE bool \ M_INLINE bool \
M_F(name, _root_p)(const it_t it) { \ M_F(name, _root_p)(const it_t it) { \
M_TR33_IT_CONTRACT(it, true); \ M_TR33_IT_CONTRACT(it, true); \
+2 -2
View File
@@ -176,6 +176,8 @@
M_F(name, _MAX_TYPE) = M_NARGS(__VA_ARGS__) \ M_F(name, _MAX_TYPE) = M_NARGS(__VA_ARGS__) \
}; \ }; \
/* Define the variant */ \ /* Define the variant */ \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
typedef struct M_F(name, _s) { \ typedef struct M_F(name, _s) { \
enum M_F(name, _enum) type; \ enum M_F(name, _enum) type; \
union { \ union { \
@@ -183,8 +185,6 @@
} value; \ } value; \
} name_t[1]; \ } name_t[1]; \
\ \
typedef struct M_F(name, _s) *M_F(name, _ptr); \
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
/* Define internal type for oplist */ \ /* Define internal type for oplist */ \
typedef name_t M_F(name, _ct); \ typedef name_t M_F(name, _ct); \
/* Save alias for the types of arguments */ \ /* Save alias for the types of arguments */ \
+39 -5
View File
@@ -82,11 +82,21 @@ static void g_f(int n)
g_count++; g_count++;
} }
static void func_map(int *d, int n) static bool func_map(int *d, int n, void *data)
{ {
assert (g_min <= n && n <= g_max); assert (g_min <= n && n <= g_max);
g_count++; g_count++;
*d = n * n; *d = n * n;
return (data == NULL) || (n != 0);
}
static bool func_map_none(int *d, int n, void *data)
{
(void) d;
(void) n;
(void) data;
g_count++;
return false;
} }
static void func_reduce(int *d, int n) static void func_reduce(int *d, int n)
@@ -156,13 +166,17 @@ static void test_list(void)
g_min = 0; g_min = 0;
g_max = 99; g_max = 99;
g_count = 0; g_count = 0;
algo_list_transform(tmp, l, func_map); algo_list_transform(tmp, l, func_map, NULL);
assert(g_count == 101); assert(g_count == 101);
assert(list_int_size(tmp) == 101); assert(list_int_size(tmp) == 101);
for(int i = 0; i < 100; i++) { for(int i = 0; i < 100; i++) {
assert(*list_int_get(tmp, (size_t) i) == i * i); assert(*list_int_get(tmp, (size_t) i) == i * i);
} }
assert(*list_int_get(tmp, 100) == 17 * 17); assert(*list_int_get(tmp, 100) == 17 * 17);
g_count = 0;
algo_list_transform(tmp, l, func_map, (void*)&g_count);
assert(g_count == 101);
assert(list_int_size(tmp) == 100);
} }
int *p = algo_list_min(l); int *p = algo_list_min(l);
@@ -305,19 +319,35 @@ static void test_array(void)
assert(g_count == 101); assert(g_count == 101);
int n; int n;
algo_array_reduce(&n, l, func_reduce); bool b;
b = algo_array_reduce(&n, l, func_reduce);
assert(b);
assert(n == 100*99/2+17); assert(n == 100*99/2+17);
g_min = 0; g_min = 0;
g_max = 99; g_max = 99;
g_count = 0; g_count = 0;
algo_array_map_reduce(&n, l, func_reduce, func_map); b = algo_array_transform_reduce(&n, l, func_reduce, func_map, NULL);
assert(b);
assert(g_count == 101); assert(g_count == 101);
assert(n == (328350 + 17*17)); assert(n == (328350 + 17*17));
g_count = 0;
b = algo_array_transform_reduce(&n, l, func_reduce, func_map, (void*) &g_count);
assert(b);
assert(g_count == 101);
assert(n == (328350 + 17*17));
g_count = 0;
n = -1;
b = algo_array_transform_reduce(&n, l, func_reduce, func_map_none, NULL);
assert(!b);
assert(g_count == 101);
assert(n == -1);
M_LET(tmp, array_int_t) { M_LET(tmp, array_int_t) {
g_count = 0; g_count = 0;
algo_array_transform(tmp, l, func_map); algo_array_transform(tmp, l, func_map, NULL);
assert(g_count == 101); assert(g_count == 101);
assert(array_int_size(tmp) == 101); assert(array_int_size(tmp) == 101);
for(int i = 0; i < 100; i++) { for(int i = 0; i < 100; i++) {
@@ -326,6 +356,10 @@ static void test_array(void)
} }
assert(*array_int_get(l, 100) == 17); assert(*array_int_get(l, 100) == 17);
assert(*array_int_get(tmp, 100) == 17 * 17); assert(*array_int_get(tmp, 100) == 17 * 17);
g_count = 0;
algo_array_transform(tmp, l, func_map, (void*) &g_count);
assert(g_count == 101);
assert(array_int_size(tmp) == 100);
array_int_reset(tmp); array_int_reset(tmp);
#define FT(d,x) ((d) = (x) + 1) #define FT(d,x) ((d) = (x) + 1)
+23
View File
@@ -893,6 +893,28 @@ static void test_coverage(void)
} }
/* Reported by florommel */
static void test_reserve_bug(void)
{
dict_int_t dict;
dict_int_init(dict);
/* NOTE: Remove the following line to make it not fail: */
dict_int_reserve(dict, 32);
int k = 0;
for (int n = 0; n < 100; n++) {
for (int i = 0; i < 3; i++) {
dict_int_set_at(dict, ++k, 42);
}
for (int i = 0; i < 3; i++) {
dict_int_erase(dict, k--);
}
k += 100; /* ensure fresh keys in the next iteration */
}
dict_int_clear(dict);
}
int main(void) int main(void)
{ {
test1(); test1();
@@ -905,6 +927,7 @@ int main(void)
test_it_oa(); test_it_oa();
test_oa_str1(); test_oa_str1();
test_oa_str2(); test_oa_str2();
test_reserve_bug();
testobj_final_check(); testobj_final_check();
test_coverage(); test_coverage();
exit(0); exit(0);
+11
View File
@@ -113,6 +113,17 @@ static void test_basic(void)
tree_get_str(s, t, false); tree_get_str(s, t, false);
assert(string_equal_str_p(s, "[{-1,[{0,[{1,[{3},{5,[{2}]},{4,[{9},{7,[{8}]},{10},{6}]}]}]}]}]")); assert(string_equal_str_p(s, "[{-1,[{0,[{1,[{3},{5,[{2}]},{4,[{9},{7,[{8}]},{10},{6}]}]}]}]}]"));
assert( *tree_cref(it7) == 7 );
assert( *tree_cref(tree_down(it7)) == 8 );
assert( *tree_cref(tree_up(it7)) == 4);
assert( *tree_cref(tree_left(it7)) == 9);
assert( *tree_cref(tree_right(it7)) == 10);
assert( *tree_cref(tree_down(root)) == 0);
assert( tree_end_p(tree_up(root)));
assert( tree_end_p(tree_left(root)));
assert( tree_end_p(tree_right(root)));
assert( *tree_down_ref(it7) == 8); assert( *tree_down_ref(it7) == 8);
assert( *tree_up_ref(it7) == 4); assert( *tree_up_ref(it7) == 4);
assert( *tree_left_ref(it7) == 9); assert( *tree_left_ref(it7) == 9);