Author SHA1 Message Date
Patrick Pelissier 2ef7f5b8f0 Fix issue of M_LET with LIST_BIF containers. 2025-07-26 10:44:21 +02:00
Patrick Pelissier 522f37f66d WIP on BI-F list. Still an issue to fix 2025-07-26 10:38:28 +02:00
Patrick Pelissier 089b8b3c61 Add BI_PUSH container (name TBC) 2025-07-26 10:38:27 +02:00
Patrick Pelissier 40c6a6c891 Add parametrisation of _push_front / _push_back for dual push list 2025-07-26 10:37:39 +02:00
Patrick Pelissier c095769f86 Prepare integration of new dual push lists 2025-07-26 10:36:47 +02:00
Patrick Pelissier 46759b5f2c Rename _push_raw method to _push_back_raw method (Cleaner semantic) 2025-07-26 10:35:48 +02:00
Patrick Pelissier 8c8a1e2822 Fix bug reported by florommel on dict's reserve 2025-07-18 20:11:13 +02:00
Patrick Pelissier ad646f5e04 Update links 2025-07-01 22:17:49 +02:00
Patrick Pelissier 9ecc031fd7 Fix doc 2025-06-10 22:18:24 +02:00
Patrick Pelissier ea3c2c5019 Update doc 2025-06-10 22:15:34 +02:00
Patrick Pelissier a9dc2eef12 Update doc 2025-05-31 00:11:49 +02:00
Patrick Pelissier 39a0a4f672 Fix format 2025-05-29 15:06:36 +02:00
Patrick Pelissier 4ffa4a15ac Rename example 2025-05-29 15:01:21 +02:00
Patrick Pelissier 2d5c9e71ae Add new example 2025-05-29 14:58:25 +02:00
Patrick Pelissier e802e22b7b Port to new API 2025-05-29 14:58:09 +02:00
Patrick Pelissier 3fbe8ec0b6 Update _transform, _reduce and _map_reduce to be more flexible
Rename _map_reduce to _transform_reduce
2025-05-29 10:41:20 +02:00
Patrick Pelissier 34085aa637 WIP to allow recursive variant 2025-05-29 10:40:19 +02:00
Patrick Pelissier 77033e2020 Move to V0.8.1 2025-05-29 10:39:42 +02:00
Patrick Pelissier 5c57ad1f51 Add example on tree 2025-05-24 17:40:29 +02:00
Patrick Pelissier 27621db95d Add method _left, right, _up and _down for tree to get directly an iterator to the requested element 2025-05-24 17:38:57 +02:00
Patrick Pelissier eab3f701a8 Fix displayed size in case of overflow computation 2025-05-24 10:18:15 +02:00
19 changed files with 591 additions and 224 deletions

No files matched your search

+1 -1
View File
@@ -34,7 +34,7 @@ DESTDIR=
# Package name & version.
PACKAGE=m_lib-$(VERSION)
VERSION=0.8.0
VERSION=0.8.1
# 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
+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
the library usage
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.
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.
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
but rather read the examples.
as the code is quite complex and uses a lot of tricks.
You should rather read the examples.
In this documentation,
* _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.
## 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
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.
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
@@ -806,14 +822,15 @@ Example:
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.
This make the code much easier to read and understand.
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
the basic type shall be given instead
the type shall be explictely given instead
(This is due to limitation in the C preprocessing).
### 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 `_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_front_raw(name_t container)`
@@ -1868,7 +1884,7 @@ void name_clear(name_t list)
void name_reset(name_t list)
type *name_back(const name_t list)
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)
void name_push_move(name_t list, type *value)
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_clear(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)
type *name_push_new(name_t array)
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.
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**.
On insertion of a new element, contained objects may moved.
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
(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`
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.
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_down(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
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`
and push the result into the container `d` so that `d = func(c)`
Reset the container `d`, then
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`
the transformed value of the constant object `in`.
Afterwards `out` is pushed moved into `d`.
`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`
This method is defined only if the base type exports an `INIT` method.
This method is defined only if the container exports a `PUSH_MOVE` method.
`c` and `d` cannot be the same containers.
This method is defined only if the base type exports an `INIT` method and if the container exports a `PUSH` method.
`c` and `d` shall not 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`.
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`
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`.
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))`
@@ -9189,7 +9236,7 @@ The following methods are modified to support the memory context parameter (the
* \<array\>_init_set
* \<array\>_move
* \<array\>_set_at
* \<array\>_push_raw
* \<array\>_push_back_raw
* \<array\>_push_back
* \<array\>_push_new
* \<array\>_push_move
@@ -9288,7 +9335,7 @@ The following methods are modified to support the memory context parameter (the
* m-list
* \<list\>_reset
* \<list\>_clear
* \<list\>_push_raw
* \<list\>_push_back_raw
* \<list\>_push_back
* \<list\>_push_new
* \<list\>_pop_back
@@ -9308,7 +9355,6 @@ The following methods are modified to support the memory context parameter (the
* \<list\>_\<emplace_back\>
* \<list\>_\<emplace_front\>
* \<list\>_push_back_raw
* \<list\>_push_raw
* \<list\>_push_back
* \<list\>_push_back_new
* \<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)
* 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){
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);
return true;
}
int main(void)
@@ -31,7 +33,7 @@ int main(void)
}
/* 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);
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, _eq_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) );
@@ -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, _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, _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 * ) \
@@ -756,27 +757,28 @@
} \
\
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 */ \
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_CALL_RESET(cont_oplist, dst); \
type_t tmp; \
M_CALL_INIT(type_oplist, tmp); \
for M_EACH(item, src, cont_oplist) { \
type_t tmp; \
M_CALL_INIT(type_oplist, tmp); \
func(&tmp, *item); \
M_CALL_PUSH_MOVE(cont_oplist, dst, &tmp); \
if (func(&tmp, *item, data)) { \
M_CALL_PUSH(cont_oplist, dst, tmp); \
} \
} \
M_CALL_CLEAR(type_oplist, tmp); \
M_IF_METHOD(REVERSE, cont_oplist)(M_CALL_REVERSE(cont_oplist, dst),); \
} \
, /* END PUSH_MOVE */), /* END INIT */ ) \
\
/* Reduce all elements of the container in dst in function of func */ \
M_IF_METHOD(SET, type_oplist)( \
M_P(void, name, _reduce, type_t *dest, container_t const l, \
M_F(name, _transform_cb_ct) func) \
M_P(bool, name, _reduce, type_t *dest, container_t const l, M_F(name, _reduce_cb_ct) func) \
{ \
M_UNUSED_CONTEXT(); \
bool initDone = false; \
@@ -788,15 +790,15 @@
initDone = true; \
} \
} \
return initDone; \
} \
, /* END SET */) \
\
/* Reduce all transformed elements of the container in dst in function of func */ \
M_IF_METHOD(INIT, type_oplist)( \
M_P(void, name, _map_reduce, type_t *dest, \
const container_t l, \
M_F(name, _transform_cb_ct) redFunc, \
M_F(name, _transform_cb_ct) mapFunc) \
M_P(bool, name, _transform_reduce, type_t *dest, const container_t l, \
M_F(name, _reduce_cb_ct) redFunc, \
M_F(name, _transform_cb_ct) mapFunc, void *data) \
{ \
M_UNUSED_CONTEXT(); \
bool initDone = false; \
@@ -804,14 +806,15 @@
M_CALL_INIT(type_oplist, tmp); \
for M_EACH(item, l, cont_oplist) { \
if (initDone) { \
mapFunc(&tmp, *item); \
redFunc(dest, tmp); \
if (mapFunc(&tmp, *item, data) ) { \
redFunc(dest, tmp); \
} \
} else { \
mapFunc(dest, *item); \
initDone = true; \
initDone = mapFunc(dest, *item, data); \
} \
} \
M_CALL_CLEAR(type_oplist, tmp); \
return initDone; \
} \
, ) \
+5 -5
View File
@@ -367,7 +367,7 @@
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); \
if (M_UNLIKELY (v->size >= v->alloc)) { \
@@ -395,7 +395,7 @@
M_IF_METHOD(INIT_SET, oplist)( \
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) ) \
return; \
M_IF_EXCEPTION( v->size --); \
@@ -407,7 +407,7 @@
M_IF_METHOD(INIT, oplist)( \
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) ) \
return NULL; \
M_IF_EXCEPTION( v->size --); \
@@ -420,7 +420,7 @@
M_P(void, name, _push_move, array_t v, type *x) \
{ \
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) ) \
return; \
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) \
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) ) \
return; \
M_IF_EXCEPTION( v->size --); \
+4 -4
View File
@@ -71,7 +71,7 @@
/* Define M*LIB version */
#define M_CORE_VERSION_MAJOR 0
#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
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
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
argument is between parenthesis, otherwise an INIT_SET is performed.
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)
/* 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 */
#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_CALL_INIT_WITH, M_CALL_INIT_SET) \
(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; \
} \
\
/* 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) \
{ \
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->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) \
@@ -1534,6 +1537,9 @@ enum m_d1ct_oa_element_e {
/* FIXME: Overflow not handled. What to do in case of it? */ \
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) ; \
if (M_UNLIKELY(dict->count <= dict->lower_limit)) { \
dict->lower_limit = 0; \
} \
} \
\
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(value_oplist, data[p].value, data[i].value); \
} 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(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(value_oplist, data[p].value, data[i].value); \
} 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(value_oplist, ptr->value, data[i].value); \
} \
+218 -152
View File
@@ -44,15 +44,15 @@
M_END_PROTECTED_CODE
/* Define a singly linked list of a given type allowing both push.
USAGE: LIST_DUAL_PUSH_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
/* Define a singly linked list of a given type allowing both push and one pop (back)
USAGE: LIST_DUAL_PUSH_DEF(name, type [, oplist_of_the_type]) */
#define M_LIST_DUAL_PUSH_DEF(name, ...) \
M_LIST_DUAL_PUSH_DEF_AS(name, M_F(name,_t), M_F(name, _it_t), __VA_ARGS__)
/* Define a singly linked list of a given type allowing both push.
/* Define a singly linked list of a given type allowing both push and one pop (back)
as the provided type name_t with the iterator named it_t
USAGE: LIST_DUAL_PUSH_DEF(name, type [, oplist_of_the_type]) */
USAGE: LIST_DUAL_PUSH_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
#define M_LIST_DUAL_PUSH_DEF_AS(name, name_t, it_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_L1ST_DUAL_PUSH_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
@@ -61,6 +61,22 @@
M_END_PROTECTED_CODE
/* Define a singly linked list of a given type allowing both push and one pop (front)
USAGE: LIST_BIF_DEF(name, type [, oplist_of_the_type]) */
#define M_LIST_BIF_DEF(name, ...) \
M_LIST_BIF_DEF_AS(name, M_F(name,_t), M_F(name, _it_t), __VA_ARGS__)
/* Define a singly linked list of a given type allowing both push and one pop (front)
as the provided type name_t with the iterator named it_t
USAGE: LIST_BIF_DEF_AS(name, name_t, type [, oplist_of_the_type]) */
#define M_LIST_BIF_DEF_AS(name, name_t, it_t, ...) \
M_BEGIN_PROTECTED_CODE \
M_L1ST_BIF_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t ), \
(name, __VA_ARGS__, name_t, it_t ))) \
M_END_PROTECTED_CODE
/* Define the oplist of a list of the given type.
USAGE: LIST_OPLIST(name [, oplist_of_the_type]) */
#define M_LIST_OPLIST(...) \
@@ -68,6 +84,10 @@
((__VA_ARGS__, M_BASIC_OPLIST ), \
(__VA_ARGS__ )))
/* Define the oplist of a list of the given type.
USAGE: LIST_BIF_OPLIST(name [, oplist_of_the_type]) */
#define M_LIST_BIF_OPLIST(...) M_OPEXTEND(M_LIST_OPLIST(__VA_ARGS__), REVERSE(0))
/* Define an init value to init global variables of type list.
USAGE:
list_t global_variable = LIST_INIT_VALUE();
@@ -128,8 +148,8 @@
IT_INSERT(M_F(name, _insert)), \
IT_REMOVE(M_F(name,_remove)), \
RESET(M_F(name,_reset)), \
PUSH(M_F(name,_push_back)), \
POP(M_F(name,_pop_back)), \
PUSH(M_F(name,_push)), \
POP(M_F(name,_pop)), \
PUSH_MOVE(M_F(name,_push_move)), \
POP_MOVE(M_F(name,_pop_move)) \
,SPLICE_BACK(M_F(name,_splice_back)) \
@@ -172,8 +192,8 @@
IT_INSERT(API_0P(M_F(name, _insert))), \
IT_REMOVE(API_0P(M_F(name,_remove))), \
RESET(API_0P(M_F(name,_reset))), \
PUSH(API_0P(M_F(name,_push_back))), \
POP(API_0P(M_F(name,_pop_back))), \
PUSH(API_0P(M_F(name,_push))), \
POP(API_0P(M_F(name,_pop))), \
PUSH_MOVE(API_0P(M_F(name,_push_move))), \
POP_MOVE(API_0P(M_F(name,_pop_move))) \
,SPLICE_BACK(M_F(name,_splice_back)) \
@@ -293,7 +313,7 @@
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); \
struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
@@ -309,7 +329,7 @@
\
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)) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
@@ -318,10 +338,15 @@
} \
} \
\
M_P(void, name, _push, list_t v, type const x) \
{ \
M_F(name, _push_back)M_R(v, x); \
} \
\
M_IF_METHOD(INIT, oplist)( \
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)) \
return NULL; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
@@ -347,10 +372,15 @@
M_L1ST_CONTRACT(v); \
} \
\
M_P(void, name, _pop, type *data, list_t v) \
{ \
M_F(name, _pop_back)M_R(data, v); \
} \
\
M_P(void, name, _push_move, list_t v, type *x) \
{ \
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)) \
return; \
M_CALL_INIT_MOVE (oplist, *data, *x); \
@@ -892,7 +922,7 @@
#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_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) ) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = *v ); \
@@ -903,30 +933,30 @@
/* Definition of the emplace_back function for dual push list */
#define M_L1ST_EMPLACE_BACK_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
#define M_L1ST_EMPLACE_STRONG_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_F(name, _subtype_ct) *data = M_F(name, _push_back_raw)M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->back); \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_CALL_DEL(oplist, next)) { \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->first); \
M_ON_EXCEPTION( v->first = next->next, v->last = (v->last == next) ? NULL : v->last, M_CALL_DEL(oplist, next)) { \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
} \
}
/* Definition of the emplace_front function for dual push list */
#define M_L1ST_EMPLACE_FRONT_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
#define M_L1ST_EMPLACE_WEAK_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_IF_EXCEPTION(struct M_F(name, _s) *front = v->front, *back = v->back); \
M_IF_EXCEPTION(struct M_F(name, _s) *front = v->last, *back = v->first); \
M_F(name, _subtype_ct) *data = M_F(name, _push_front_raw)M_R(v); \
if (M_UNLIKELY (data == NULL) ) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->front); \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->last); \
M_IF_EXCEPTION(M_ASSERT(tofree != NULL)); \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_ON_EXCEPTION( v->first = back, v->last = front, (front != NULL ? front : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
} \
}
@@ -948,8 +978,8 @@
/* Define the internal contract of an dual-push list */
#define M_L1ST_DUAL_PUSH_CONTRACT(l) do { \
M_ASSERT (l != NULL); \
M_ASSERT ( (l->back == NULL && l->front == NULL) \
|| (l->back != NULL && l->front != NULL)); \
M_ASSERT ( (l->first == NULL && l->last == NULL) \
|| (l->first != NULL && l->last != NULL)); \
} while (0)
@@ -963,9 +993,9 @@
#define M_L1ST_DUAL_PUSH_DEF_P3(name, type, oplist, list_t, it_t) \
M_L1ST_DUAL_PUSH_DEF_TYPE(name, type, oplist, list_t, it_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_L1ST_DUAL_PUSH_DEF_P4(name, type, oplist, list_t, it_t) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_back, oplist, M_L1ST_EMPLACE_BACK_DEF) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_front, oplist, M_L1ST_EMPLACE_FRONT_DEF) \
M_L1ST_DUAL_PUSH_DEF_P4(name, type, oplist, list_t, it_t, _back, _push_back_raw, _push_back, _push_back_move, _push_back_new, _pop_back, _front, _push_front_raw, _push_front, _push_front_move, _push_front_new) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_back, oplist, M_L1ST_EMPLACE_STRONG_DEF) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_front, oplist, M_L1ST_EMPLACE_WEAK_DEF) \
M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t)
@@ -978,13 +1008,12 @@
}; \
\
/* Dual Push singly linked list. \
Support Push Back / Push Front / Pop back in O(1). \
Doesn't support Pop front. \
Support Push Back / Push Front / Pop back or front in O(1). \
This is done by keeping a pointer to both back & front \
*/ \
typedef struct M_F(name, _head_s) { \
struct M_F(name,_s) *front; /* Pointer to the front node or NULL */ \
struct M_F(name,_s) *back; /* Pointer to the back node or NULL */ \
struct M_F(name,_s) *last; /* Pointer to the weak entry node or NULL */ \
struct M_F(name,_s) *first; /* Pointer to the strong entry node or NULL */ \
} list_t[1]; \
\
/* Define the iterator over a dual push singly linked list */ \
@@ -1007,30 +1036,32 @@
- oplist: oplist of the type of the elements of the container
- list_t: alias for type of the container
- it_t: alias for iterator of the container
- strong: name of the strong entry point (back or front)
- weak: name of the weak entry point (front or back)
*/
#define M_L1ST_DUAL_PUSH_DEF_P4(name, type, oplist, list_t, it_t) \
#define M_L1ST_DUAL_PUSH_DEF_P4(name, type, oplist, list_t, it_t, strong, push_strong_raw, push_strong, push_strong_move, push_strong_new, pop_strong, weak, push_weak_raw, push_weak, push_weak_move, push_weak_new) \
\
M_INLINE void \
M_F(name, _init)(list_t v) \
{ \
M_ASSERT( v != NULL); \
v->front = NULL; \
v->back = NULL; \
v->last = NULL; \
v->first = NULL; \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
} \
\
M_P(void, name, _reset, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
struct M_F(name, _s) *it = v->back; \
struct M_F(name, _s) *it = v->first; \
while (it != NULL) { \
struct M_F(name, _s) *next = it->next; \
M_CALL_CLEAR(oplist, it->data); \
M_CALL_DEL(oplist, it); \
it = next; \
} \
v->front = NULL; \
v->back = NULL; \
v->last = NULL; \
v->first = NULL; \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
} \
\
@@ -1040,14 +1071,14 @@
} \
\
M_INLINE type * \
M_F(name, _back)(const list_t v) \
M_F(name, strong)(const list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
M_ASSERT (v->back != NULL); \
return &(v->back->data); \
M_ASSERT (v->first != NULL); \
return &(v->first->data); \
} \
\
M_P(type *, name, _push_back_raw, list_t v) \
M_P(type *, name, push_strong_raw, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
@@ -1055,52 +1086,51 @@
M_MEMORY_FULL(struct M_F(name, _s), 1); \
} \
type *ret = &next->data; \
next->next = v->back; \
v->back = next; \
/* Update front too if the list was empty */ \
next->next = v->first; \
v->first = next; \
/* Update weak entry too if the list was empty */ \
/* This C code should generate branchless code */ \
struct M_F(name, _s) *front = v->front; \
front = (front == NULL) ? next : front; \
v->front = front; \
struct M_F(name, _s) *node = v->last; \
node = (node == NULL) ? next : node; \
v->last = node; \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
return ret; \
} \
\
/* Internal, for INIT_WITH */ \
M_P(type *, name, _push_raw, list_t d) \
M_P(void, name, push_strong, list_t v, type const x) \
{ \
return M_F(name, _push_back_raw)M_R(d); \
} \
\
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_strong_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->back); \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_CALL_DEL(oplist, next)) { \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->first); \
M_ON_EXCEPTION( v->first = next->next, v->last = (v->last == next) ? NULL : v->last, M_CALL_DEL(oplist, next)) { \
M_CALL_INIT_SET(oplist, *data, x); \
} \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_P(type *, name, _push_back_new, list_t v) \
M_P(void, name, _push, list_t v, type const x) \
{ \
type *data = M_F(name, _push_back_raw)M_R(v); \
M_F(name, push_strong)M_R(v, x); \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_P(type *, name, push_strong_new, list_t v) \
{ \
type *data = M_F(name, push_strong_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return NULL; \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->back); \
M_ON_EXCEPTION( v->back = next->next, v->front = (v->front == next) ? NULL : v->front, M_CALL_DEL(oplist, next)) { \
M_IF_EXCEPTION(struct M_F(name, _s) *next = v->first); \
M_ON_EXCEPTION( v->first = next->next, v->last = (v->last == next) ? NULL : v->last, M_CALL_DEL(oplist, next)) { \
M_CALL_INIT(oplist, *data); \
} \
return data; \
} \
, /* No INIT */ ) \
\
M_P(void, name, _push_back_move, list_t v, type *x) \
M_P(void, name, push_strong_move, list_t v, type *x) \
{ \
M_ASSERT (x != NULL); \
type *data = M_F(name, _push_back_raw)M_R(v); \
type *data = M_F(name, push_strong_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_CALL_INIT_MOVE (oplist, *data, *x); \
@@ -1108,55 +1138,60 @@
\
M_P(void, name, _push_move, list_t v, type *x) \
{ \
M_F(name, _push_back_move)M_R(v, x); \
M_F(name, push_strong_move)M_R(v, x); \
} \
\
M_P(void, name, _pop_back, type *data, list_t v) \
M_P(void, name, pop_strong, type *data, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
M_ASSERT (v->back != NULL); \
struct M_F(name, _s) *tofree = v->back; \
M_ASSERT (v->first != NULL); \
struct M_F(name, _s) *tofree = v->first; \
if (data != NULL) { \
M_DO_MOVE(oplist, *data, tofree->data); \
} else { \
M_CALL_CLEAR(oplist, tofree->data); \
} \
v->back = tofree->next; \
v->first = tofree->next; \
M_CALL_DEL(oplist, tofree); \
/* Update front too if the list became empty */ \
/* Update weak entry too if the list became empty */ \
/* This C code shall generate branchless code */ \
struct M_F(name, _s) *front = v->front; \
front = (front == tofree) ? NULL : front; \
v->front = front; \
struct M_F(name, _s) *node = v->last; \
node = (node == tofree) ? NULL : node; \
v->last = node; \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
} \
\
M_P(void, name, _pop, type *data, list_t v) \
{ \
M_F(name, pop_strong)M_R(data, v); \
} \
\
M_P(void, name, _pop_move, type *data, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
M_ASSERT (v->back != NULL); \
M_ASSERT (v->first != NULL); \
M_ASSERT (data != NULL); \
struct M_F(name, _s) *tofree = v->back; \
struct M_F(name, _s) *tofree = v->first; \
M_CALL_INIT_MOVE (oplist, *data, tofree->data); \
v->back = tofree->next; \
v->first = tofree->next; \
M_CALL_DEL(oplist, tofree); \
/* Update front too if the list became empty */ \
/* Update weak entry too if the list became empty */ \
/* This C code shall generate branchless code */ \
struct M_F(name, _s) *front = v->front; \
front = (front == tofree) ? NULL : front; \
v->front = front; \
struct M_F(name, _s) *node = v->last; \
node = (node == tofree) ? NULL : node; \
v->last = node; \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
} \
\
M_INLINE type * \
M_F(name, _front)(list_t v) \
M_F(name, weak)(list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
M_ASSERT (v->front != NULL); \
return &(v->front->data); \
M_ASSERT (v->last != NULL); \
return &(v->last->data); \
} \
\
M_P(type *, name, _push_front_raw, list_t v) \
M_P(type *, name, push_weak_raw, list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
struct M_F(name, _s) *next = M_CALL_NEW(oplist, struct M_F(name, _s)); \
@@ -1165,49 +1200,49 @@
} \
type *ret = &next->data; \
next->next = NULL; \
if (M_LIKELY(v->front != NULL)) { \
v->front->next = next; \
if (M_LIKELY(v->last != NULL)) { \
v->last->next = next; \
} else { \
/* Update back too as the list was empty */ \
v->back = next; \
/* Update strong entry too as the list was empty */ \
v->first = next; \
} \
v->front = next; \
v->last = next; \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
return ret; \
} \
\
M_P(void, name, _push_front, list_t v, type const x) \
M_P(void, name, push_weak, list_t v, type const x) \
{ \
M_IF_EXCEPTION(struct M_F(name, _s) *front = v->front, *back = v->back); \
type *data = M_F(name, _push_front_raw)M_R(v); \
M_IF_EXCEPTION(struct M_F(name, _s) *last = v->last, *first = v->first); \
type *data = M_F(name, push_weak_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->front); \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->last); \
M_IF_EXCEPTION(M_ASSERT(tofree != NULL)); \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_ON_EXCEPTION( v->first = first, v->last = last, (last != NULL ? last : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_CALL_INIT_SET(oplist, *data, x); \
} \
} \
\
M_P(void, name, _push_front_move, list_t v, type *x) \
M_P(void, name, push_weak_move, list_t v, type *x) \
{ \
M_ASSERT (x != NULL); \
type *data = M_F(name, _push_front_raw)M_R(v); \
type *data = M_F(name, push_weak_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return; \
M_CALL_INIT_MOVE (oplist, *data, *x); \
} \
\
M_IF_METHOD(INIT, oplist)( \
M_P(type *, name, _push_front_new, list_t v) \
M_P(type *, name, push_weak_new, list_t v) \
{ \
M_IF_EXCEPTION(struct M_F(name, _s) *front = v->front, *back = v->back); \
type *data = M_F(name, _push_front_raw)M_R(v); \
M_IF_EXCEPTION(struct M_F(name, _s) *last = v->last, *first = v->first); \
type *data = M_F(name, push_weak_raw)M_R(v); \
if (M_UNLIKELY (data == NULL)) \
return NULL; \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->front); \
M_IF_EXCEPTION(struct M_F(name, _s) *m_volatile tofree = v->last); \
M_IF_EXCEPTION(M_ASSERT(tofree != NULL)); \
M_ON_EXCEPTION( v->back = back, v->front = front, (front != NULL ? front : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_ON_EXCEPTION( v->first = first, v->last = last, (last != NULL ? last : tofree)->next = NULL, M_CALL_DEL(oplist, tofree)) { \
M_CALL_INIT(oplist, *data); \
} \
return data; \
@@ -1218,7 +1253,7 @@
M_F(name, _empty_p)(const list_t v) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
return v->back == NULL; \
return v->first == NULL; \
} \
\
M_INLINE void \
@@ -1226,8 +1261,8 @@
{ \
M_L1ST_DUAL_PUSH_CONTRACT(l); \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
M_SWAP(struct M_F(name, _s) *, l->front, v->front); \
M_SWAP(struct M_F(name, _s) *, l->back, v->back); \
M_SWAP(struct M_F(name, _s) *, l->last, v->last); \
M_SWAP(struct M_F(name, _s) *, l->first, v->first); \
} \
\
M_INLINE void \
@@ -1235,7 +1270,7 @@
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
M_ASSERT (it != NULL); \
it->current = v->back; \
it->current = v->first; \
it->previous = NULL; \
} \
\
@@ -1305,7 +1340,7 @@
{ \
M_L1ST_DUAL_PUSH_CONTRACT(v); \
size_t size = 0; \
struct M_F(name, _s) *it = v->back; \
struct M_F(name, _s) *it = v->first; \
while (it != NULL) { \
size ++; \
it = it->next; \
@@ -1324,19 +1359,19 @@
M_ON_EXCEPTION( M_CALL_DEL(oplist, next)) \
M_CALL_INIT_SET(oplist, next->data, x); \
if (M_UNLIKELY (insertion_point->current == NULL)) { \
next->next = list->back; \
list->back = next; \
/* update front if list is empty */ \
struct M_F(name, _s) *front = list->front; \
front = (front == NULL) ? next : front; \
list->front = front; \
next->next = list->first; \
list->first = next; \
/* update weak entry if list is empty */ \
struct M_F(name, _s) *node = list->last; \
node = (node == NULL) ? next : node; \
list->last = node; \
} else { \
next->next = insertion_point->current->next; \
insertion_point->current->next = next; \
/* update front if current == front */ \
struct M_F(name, _s) *front = list->front; \
front = (front == insertion_point->current) ? next : front; \
list->front = front; \
/* update weak entry if current == front */ \
struct M_F(name, _s) *node = list->last; \
node = (node == insertion_point->current) ? next : node; \
list->last = node; \
} \
} \
\
@@ -1348,14 +1383,14 @@
struct M_F(name, _s) *next = removing_point->current->next; \
struct M_F(name, _s) *previous = removing_point->previous; \
if (M_UNLIKELY (previous == NULL)) { \
list->back = next; \
list->first = next; \
} else { \
previous->next = next; \
} \
/* Update front */ \
struct M_F(name, _s) *front = list->front; \
front = (next == NULL) ? previous : front; \
list->front = front; \
/* Update weak entry */ \
struct M_F(name, _s) *node = list->last; \
node = (next == NULL) ? previous : node; \
list->last = node; \
/* Remove node */ \
M_CALL_CLEAR(oplist, removing_point->current->data); \
M_CALL_DEL(oplist, removing_point->current); \
@@ -1371,8 +1406,8 @@
struct M_F(name, _s) **update_list; \
if (M_UNLIKELY (list == org)) return; \
M_F(name, _reset)M_R(list); \
update_list = &list->back; \
it_org = org->back; \
update_list = &list->first; \
it_org = org->first; \
M_ASSERT(*update_list == NULL); \
M_ON_EXCEPTION(M_CALL_DEL(oplist, next) ) \
while (it_org != NULL) { \
@@ -1386,7 +1421,7 @@
update_list = &next->next; \
it_org = it_org->next; \
/* so that even on exception, the object is still coherent */ \
list->front = next; \
list->last = next; \
*update_list = NULL; \
M_IF_EXCEPTION(next = NULL); \
} \
@@ -1405,10 +1440,10 @@
M_F(name, _init_move)(list_t list, list_t org) \
{ \
M_ASSERT (list != org); \
list->back = org->back; \
list->front = org->front; \
org->back = NULL; \
org->front = NULL; \
list->first = org->first; \
list->last = org->last; \
org->first = NULL; \
org->last = NULL; \
} \
\
M_P(void, name, _move, list_t list, list_t org) \
@@ -1420,6 +1455,7 @@
M_INLINE void \
M_F(name, _splice_back)(list_t list1, list_t list2, it_t it) \
{ \
/* FIXME back ==> strong */ \
M_L1ST_DUAL_PUSH_CONTRACT(list1); \
M_L1ST_DUAL_PUSH_CONTRACT(list2); \
M_ASSERT (it->current != NULL); \
@@ -1427,24 +1463,24 @@
struct M_F(name, _s) *current = it->current; \
struct M_F(name, _s) *next = current->next; \
if (it->previous == NULL) { \
list2->back = next; \
list2->first = next; \
} else { \
it->previous->next = next; \
} \
/* Update the front of 'list2' if it was the last element */ \
struct M_F(name, _s) *front = list2->front; \
struct M_F(name, _s) *front = list2->last; \
front = (next == NULL) ? it->previous : front; \
list2->front = front; \
list2->last = front; \
/* Update 'it' to point to the next element */ \
it->current = next; \
/* Move the extracted 'current' into the list 'nv' */ \
current->next = list1->back; \
list1->back = current; \
current->next = list1->first; \
list1->first = current; \
/* Update the front field if the list 'nv' was empty */ \
/* This C code shall generate branchless code */ \
front = list1->front; \
front = list1->last; \
front = (front == NULL) ? current : front; \
list1->front = front; \
list1->last = front; \
M_L1ST_DUAL_PUSH_CONTRACT(list1); \
M_L1ST_DUAL_PUSH_CONTRACT(list2); \
} \
@@ -1462,33 +1498,33 @@
M_ASSERT(current != NULL); \
struct M_F(name, _s) *next = current->next; \
if (opos->previous == NULL) { \
olist->back = next; \
olist->first = next; \
} else { \
opos->previous->next = next; \
} \
/* Update the front of 'olist' if it was the last element */ \
struct M_F(name, _s) *front = olist->front; \
struct M_F(name, _s) *front = olist->last; \
front = (next == NULL) ? opos->previous : front; \
olist->front = front; \
olist->last = front; \
/* Update 'opos' to point to the next element */ \
opos->current = next; \
/* opos->previous is still valid & doesn't need to be updated */ \
/* Insert into 'nlist' */ \
struct M_F(name, _s) *npos_current = npos->current; \
if (M_UNLIKELY (npos_current == NULL)) { \
current->next = nlist->back; \
nlist->back = current; \
current->next = nlist->first; \
nlist->first = current; \
/* update 'front' if the list was empty (branchless) */ \
front = nlist->front; \
front = nlist->last; \
front = (front == NULL) ? current : front; \
nlist->front = front; \
nlist->last = front; \
} else { \
current->next = npos_current->next; \
npos_current->next = current; \
/* update front if current == front (branchless) */ \
front = nlist->front; \
front = nlist->last; \
front = (front == npos_current) ? current : front; \
nlist->front = front; \
nlist->last = front; \
} \
/* Update 'npos' to point to 'current'. */ \
npos->previous = npos_current; \
@@ -1503,16 +1539,16 @@
M_L1ST_DUAL_PUSH_CONTRACT(list1); \
M_L1ST_DUAL_PUSH_CONTRACT(list2); \
M_ASSERT (list1 != list2); \
if (M_LIKELY (list1->front != NULL)) { \
list1->front->next = list2->back; \
list1->front = list2->front; \
if (M_LIKELY (list1->last != NULL)) { \
list1->last->next = list2->first; \
list1->last = list2->last; \
} else { \
/* list1 is empty */ \
list1->back = list2->back; \
list1->front = list2->front; \
list1->first = list2->first; \
list1->last = list2->last; \
} \
list2->back = NULL; \
list2->front = NULL; \
list2->first = NULL; \
list2->last = NULL; \
M_L1ST_DUAL_PUSH_CONTRACT(list1); \
M_L1ST_DUAL_PUSH_CONTRACT(list2); \
} \
@@ -1521,25 +1557,55 @@
M_F(name, _reverse)(list_t list) \
{ \
M_L1ST_DUAL_PUSH_CONTRACT(list); \
list->front = list->back; \
struct M_F(name, _s) *previous = NULL, *it = list->back, *next; \
list->last = list->first; \
struct M_F(name, _s) *previous = NULL, *it = list->first, *next; \
while (it != NULL) { \
next = it->next; \
it->next = previous; \
previous = it; \
it = next; \
} \
list->back = previous; \
list->first = previous; \
M_L1ST_DUAL_PUSH_CONTRACT(list); \
} \
/* Deferred evaluation for the bi-push list definition,
so that all arguments are evaluated before further expansion */
#define M_L1ST_BIF_DEF_P1(arg) M_ID( M_L1ST_BIF_DEF_P2 arg )
/* Validate the oplist before going further */
#define M_L1ST_BIF_DEF_P2(name, type, oplist, list_t, it_t) \
M_IF_OPLIST(oplist)(M_L1ST_BIF_DEF_P3, M_L1ST_BIF_DEF_FAILURE)(name, type, oplist, list_t, it_t)
/* Stop processing with a compilation failure */
#define M_L1ST_BIF_DEF_FAILURE(name, type, oplist, list_t, it_t) \
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(LIST_BIF_DEF): the given argument is not a valid oplist: " #oplist)
/* Internal bi-push list definition (like dual-push but inverts front & back)
- name: prefix to be used
- type: type of the elements of the array
- oplist: oplist of the type of the elements of the container
- list_t: alias for M_F(name, _t) [ type of the container ]
- it_t: alias for M_F(name, _it_t) [ iterator of the container ]
*/
#define M_L1ST_BIF_DEF_P3(name, type, oplist, list_t, it_t) \
M_L1ST_DUAL_PUSH_DEF_TYPE(name, type, oplist, list_t, it_t) \
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
M_L1ST_DUAL_PUSH_DEF_P4(name, type, oplist, list_t, it_t, _front, _push_front_raw, _push_front, _push_front_move, _push_front_new, _pop_front, _back, _push_back_raw, _push_back, _push_back_move, _push_back_new) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_front, oplist, M_L1ST_EMPLACE_STRONG_DEF) \
M_EMPLACE_QUEUE_DEF(name, list_t, _emplace_back, oplist, M_L1ST_EMPLACE_WEAK_DEF) \
M_L1ST_ITBASE_DEF(name, type, oplist, list_t, it_t)
#if M_USE_SMALL_NAME
#define LIST_DEF M_LIST_DEF
#define LIST_DEF_AS M_LIST_DEF_AS
#define LIST_DUAL_PUSH_DEF M_LIST_DUAL_PUSH_DEF
#define LIST_DUAL_PUSH_DEF_AS M_LIST_DUAL_PUSH_DEF_AS
#define LIST_BIF_DEF M_LIST_BIF_DEF
#define LIST_BIF_DEF_AS M_LIST_BIF_DEF_AS
#define LIST_OPLIST M_LIST_OPLIST
#define LIST_BIF_OPLIST M_LIST_BIF_OPLIST
#define LIST_INIT_VALUE M_LIST_INIT_VALUE
#define LIST_DUAL_PUSH_INIT_VALUE M_LIST_DUAL_PUSH_INIT_VALUE
#endif
+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);
if (M_UNLIKELY_NOMEM (alloc <= size_alloc)) {
/* 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;
//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; \
} \
\
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_F(name, _root_p)(const it_t it) { \
M_TR33_IT_CONTRACT(it, true); \
+2 -2
View File
@@ -176,6 +176,8 @@
M_F(name, _MAX_TYPE) = M_NARGS(__VA_ARGS__) \
}; \
/* 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) { \
enum M_F(name, _enum) type; \
union { \
@@ -183,8 +185,6 @@
} value; \
} 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 */ \
typedef name_t M_F(name, _ct); \
/* Save alias for the types of arguments */ \
+39 -5
View File
@@ -82,11 +82,21 @@ static void g_f(int n)
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);
g_count++;
*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)
@@ -156,13 +166,17 @@ static void test_list(void)
g_min = 0;
g_max = 99;
g_count = 0;
algo_list_transform(tmp, l, func_map);
algo_list_transform(tmp, l, func_map, NULL);
assert(g_count == 101);
assert(list_int_size(tmp) == 101);
for(int i = 0; i < 100; i++) {
assert(*list_int_get(tmp, (size_t) i) == i * i);
}
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);
@@ -305,19 +319,35 @@ static void test_array(void)
assert(g_count == 101);
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);
g_min = 0;
g_max = 99;
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(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) {
g_count = 0;
algo_array_transform(tmp, l, func_map);
algo_array_transform(tmp, l, func_map, NULL);
assert(g_count == 101);
assert(array_int_size(tmp) == 101);
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(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);
#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)
{
test1();
@@ -905,6 +927,7 @@ int main(void)
test_it_oa();
test_oa_str1();
test_oa_str2();
test_reserve_bug();
testobj_final_check();
test_coverage();
exit(0);
+42
View File
@@ -47,6 +47,8 @@ LIST_DEF_AS(list_double, ListDouble, ListDoubleIt, double)
LIST_DUAL_PUSH_DEF_AS(list_doubleDP, ListDoubleDP, ListDoubleDPIt, double)
#define M_OPL_ListDoubleDP() LIST_OPLIST(list_doubleDP, M_BASIC_OPLIST)
LIST_BIF_DEF(list2f_double, double)
ListDouble g_array1 = LIST_INIT_VALUE();
ListDoubleDP g_array2 = LIST_DUAL_PUSH_INIT_VALUE();
@@ -574,6 +576,45 @@ static void test_dual_push1(void)
list2_double_clear(list);
}
static void test_bif(void)
{
list2f_double_t x;
list2f_double_init(x);
assert(list2f_double_empty_p(x));
list2f_double_push_front(x, 2.0);
list2f_double_push_front(x, 1.0);
list2f_double_push_back(x, 3.0);
assert(list2f_double_size(x) == 3);
assert(*list2f_double_front(x) == 1.0);
assert(*list2f_double_back(x) == 3.0);
list2f_double_it_t it;
double d = 1.0;
for(list2f_double_it(it, x); !list2f_double_end_p(it); list2f_double_next(it)){
assert(d == *list2f_double_cref(it));
d += 1.0;
}
list2f_double_pop_front(&d, x);
assert(d == 1.0);
list2f_double_reverse(x);
list2f_double_pop_front(&d, x);
assert(d == 3.0);
list2f_double_pop_front(&d, x);
assert(d == 2.0);
assert(list2f_double_empty_p(x));
list2f_double_clear(x);
M_LET( (y, (1.0, 2.0, 3.0, 4.0)), LIST_BIF_OPLIST(list2f_double, M_BASIC_OPLIST)) {
assert(list2f_double_size(y) == 4);
assert(*list2f_double_front(y) == 1.0);
assert(*list2f_double_back(y) == 4.0);
d = 1.0;
for(list2f_double_it(it, y); !list2f_double_end_p(it); list2f_double_next(it)){
assert( *list2f_double_cref(it) == d);
d += 1.0;
}
}
}
static void test_dual_it1(void)
{
list2_double_t list;
@@ -899,6 +940,7 @@ int main(void)
test_mpz();
test_dual_push1();
test_dual_it1();
test_bif();
test_out_default_oplist();
test_double();
test_let_string();
+11
View File
@@ -113,6 +113,17 @@ static void test_basic(void)
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( *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_up_ref(it7) == 4);
assert( *tree_left_ref(it7) == 9);