it is used to control the inline semantics. By combining it with optimize option "-fno-inline" for the internal M*LIB function (using _Pragma optimize) we can control easily if we want to use an inline version or call of an external function. Define new user API: * M_USE_EXTERN_DECL : define it before including any MLIB header to requests to call the M*LIB functions and don't inline then. * M_USE_EXTERN_DEF : define it once in a translation unit to define the M*LIB functions. This translation unit shall contain all templated macro too. If you use one option, you need to use the other one too for another translation unit.
1133 lines
84 KiB
C
1133 lines
84 KiB
C
/*
|
|
* M*LIB - dynamic ARRAY module
|
|
*
|
|
* Copyright (c) 2017-2023, Patrick Pelissier
|
|
* All rights reserved.
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* + Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* + Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#ifndef MSTARLIB_ARRAY_H
|
|
#define MSTARLIB_ARRAY_H
|
|
|
|
#include "m-core.h"
|
|
|
|
/* Define a dynamic array of the given type and its associated functions.
|
|
USAGE: ARRAY_DEF(name, type [, oplist_of_the_type]) */
|
|
#define M_ARRAY_DEF(name, ...) \
|
|
M_ARRAY_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__)
|
|
|
|
|
|
/* Define a dynamic array of the given type and its associated functions
|
|
as the provided type name_t with the iterator named it_t
|
|
USAGE: ARRAY_DEF_AS(name, name_t, it_t, type [, oplist_of_the_type]) */
|
|
#define M_ARRAY_DEF_AS(name, name_t, it_t, ...) \
|
|
M_BEGIN_PROTECTED_CODE \
|
|
M_ARRA4_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 dynamic array given its name and its oplist.
|
|
If no oplist is given it is assumed to be M_BASIC_OPLIST
|
|
USAGE: ARRAY_OPLIST(name[, oplist of the type]) */
|
|
#define M_ARRAY_OPLIST(...) \
|
|
M_ARRA4_OPLIST_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \
|
|
((__VA_ARGS__, M_BASIC_OPLIST), \
|
|
(__VA_ARGS__ )))
|
|
|
|
|
|
/* Define an init value to init global variables of type array.
|
|
USAGE:
|
|
array_t global_variable = ARRAY_INIT_VALUE();
|
|
*/
|
|
#define M_ARRAY_INIT_VALUE() \
|
|
{ { 0, 0, NULL } }
|
|
|
|
|
|
/*****************************************************************************/
|
|
/********************************** INTERNAL *********************************/
|
|
/*****************************************************************************/
|
|
|
|
/* Deferred evaluation for the oplist definition,
|
|
so that all arguments are evaluated before further expansion */
|
|
#define M_ARRA4_OPLIST_P1(arg) M_ARRA4_OPLIST_P2 arg
|
|
|
|
/* Validation of the given oplist */
|
|
#define M_ARRA4_OPLIST_P2(name, oplist) \
|
|
M_IF_OPLIST(oplist)(M_ARRA4_OPLIST_P3, M_ARRA4_OPLIST_FAILURE)(name, oplist)
|
|
|
|
/* Prepare a clean compilation failure */
|
|
#define M_ARRA4_OPLIST_FAILURE(name, oplist) \
|
|
((M_LIB_ERROR(ARGUMENT_OF_ARRAY_OPLIST_IS_NOT_AN_OPLIST, name, oplist)))
|
|
|
|
/* OPLIST definition of a dynamic array */
|
|
/* FIXME: Do we want to export some methods as they are slow and
|
|
are not fit to be used for building other methods (like _it_remove)? */
|
|
#define M_ARRA4_OPLIST_P3(name, oplist) \
|
|
(INIT(M_F(name, _init)) \
|
|
,M_IF_METHOD2(INIT_SET,SET, oplist)(INIT_SET(M_F(name, _init_set)),) \
|
|
,M_IF_METHOD(INIT_SET, oplist)(INIT_WITH(API_1(M_INIT_WITH_VAI)),) \
|
|
,M_IF_METHOD2(INIT_SET,SET, oplist)(SET(M_F(name, _set)), ) \
|
|
,CLEAR(M_F(name, _clear)) \
|
|
,INIT_MOVE(M_F(name, _init_move)) \
|
|
,MOVE(M_F(name, _move)) \
|
|
,SWAP(M_F(name, _swap)) \
|
|
,TYPE(M_F(name,_ct)) \
|
|
,NAME(name) \
|
|
,SUBTYPE(M_F(name, _subtype_ct)) \
|
|
,EMPTY_P(M_F(name,_empty_p)) \
|
|
,IT_TYPE(M_F(name,_it_ct)), \
|
|
,IT_FIRST(M_F(name,_it)) \
|
|
,IT_LAST(M_F(name,_it_last)) \
|
|
,IT_END(M_F(name,_it_end)) \
|
|
,IT_SET(M_F(name,_it_set)) \
|
|
,IT_END_P(M_F(name,_end_p)) \
|
|
,IT_LAST_P(M_F(name,_last_p)) \
|
|
,IT_EQUAL_P(M_F(name,_it_equal_p)) \
|
|
,IT_NEXT(M_F(name,_next)) \
|
|
,IT_PREVIOUS(M_F(name,_previous)) \
|
|
,IT_REF(M_F(name,_ref)) \
|
|
,IT_CREF(M_F(name,_cref)) \
|
|
,M_IF_METHOD(INIT_SET, oplist)(IT_INSERT(M_F(name,_insert)) ,) \
|
|
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(IT_REMOVE(M_F(name,_remove)),) \
|
|
,RESET(M_F(name,_reset)) \
|
|
,KEY_TYPE(size_t) \
|
|
,VALUE_TYPE(M_F(name, _subtype_ct)) \
|
|
,KEY_OPLIST(M_BASIC_OPLIST) \
|
|
,VALUE_OPLIST(oplist) \
|
|
,M_IF_METHOD(SET, oplist)(SET_KEY(M_F(name, _set_at)) ,) \
|
|
,GET_KEY(M_F(name, _get)) \
|
|
,M_IF_METHOD(INIT, oplist)(SAFE_GET_KEY(M_F(name, _safe_get)) ,) \
|
|
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(ERASE_KEY(M_F(name, _erase)),) \
|
|
,GET_SIZE(M_F(name, _size)) \
|
|
,M_IF_METHOD(INIT_SET, oplist)(PUSH(M_F(name,_push_back)) ,) \
|
|
,M_IF_AT_LEAST_METHOD(SET,INIT_MOVE,oplist)(POP(M_F(name,_pop_back)) ,) \
|
|
,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(PUSH_MOVE(M_F(name,_push_move)) ,) \
|
|
,M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist)(POP_MOVE(M_F(name,_pop_move)) ,) \
|
|
,OPLIST(oplist) \
|
|
,M_IF_METHOD(CMP, oplist)(SORT(M_F(name, _special_sort)),) \
|
|
,M_IF_METHOD(GET_STR, oplist)(GET_STR(M_F(name, _get_str)),) \
|
|
,M_IF_METHOD(PARSE_STR, oplist)(PARSE_STR(M_F(name, _parse_str)),) \
|
|
,M_IF_METHOD(OUT_STR, oplist)(OUT_STR(M_F(name, _out_str)),) \
|
|
,M_IF_METHOD(IN_STR, oplist)(IN_STR(M_F(name, _in_str)),) \
|
|
,M_IF_METHOD(OUT_SERIAL, oplist)(OUT_SERIAL(M_F(name, _out_serial)),) \
|
|
,M_IF_METHOD(IN_SERIAL, oplist)(IN_SERIAL(M_F(name, _in_serial)),) \
|
|
,M_IF_METHOD(EQUAL, oplist)(EQUAL(M_F(name, _equal_p)),) \
|
|
,M_IF_METHOD(HASH, oplist)(HASH(M_F(name, _hash)),) \
|
|
)
|
|
|
|
|
|
/********************************** INTERNAL *********************************/
|
|
|
|
/* Define the internal contract of an array */
|
|
#define M_ARRA4_CONTRACT(a) do { \
|
|
M_ASSERT (a != NULL); \
|
|
M_ASSERT (a->size <= a->alloc); \
|
|
M_ASSERT (a->size == 0 || a->ptr != NULL); \
|
|
M_ASSERT (a->alloc == 0 || a->ptr != NULL); \
|
|
} while (0)
|
|
|
|
|
|
/* Deferred evaluation for the array definition,
|
|
so that all arguments are fully evaluated before further expansion
|
|
(ensuring good performance)
|
|
and performed a final step evaluation of the returning expansion
|
|
(ensuring delayed evaluation are still expanded)
|
|
*/
|
|
#define M_ARRA4_DEF_P1(arg) M_ID( M_ARRA4_DEF_P2 arg )
|
|
|
|
/* Validate the oplist before going further */
|
|
#define M_ARRA4_DEF_P2(name, type, oplist, array_t, it_t) \
|
|
M_IF_OPLIST(oplist)(M_ARRA4_DEF_P3, M_ARRA4_DEF_FAILURE)(name, type, oplist, array_t, it_t)
|
|
|
|
/* Stop processing with a compilation failure */
|
|
#define M_ARRA4_DEF_FAILURE(name, type, oplist, array_t, it_t) \
|
|
M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(ARRAY_DEF): the given argument is not a valid oplist: " #oplist)
|
|
|
|
/* Internal definition:
|
|
- name: prefix to be used
|
|
- type: type of the elements of the array
|
|
- oplist: oplist of the type of the elements of the array
|
|
- array_t: alias for the type of the array
|
|
- it_t: alias for the iterator of the array
|
|
*/
|
|
#define M_ARRA4_DEF_P3(name, type, oplist, array_t, it_t) \
|
|
M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t) \
|
|
M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \
|
|
M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t) \
|
|
M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \
|
|
M_EMPLACE_QUEUE_DEF(name, array_t, M_F(name, _emplace_back), oplist, M_ARRA4_EMPLACE_DEF)
|
|
|
|
/* Define the types */
|
|
#define M_ARRA4_DEF_TYPE(name, type, oplist, array_t, it_t) \
|
|
\
|
|
/* Define a dynamic array */ \
|
|
typedef struct M_F(name, _s) { \
|
|
size_t size; /* Number of elements in the array */ \
|
|
size_t alloc; /* Allocated size for the array base */ \
|
|
type *ptr; /* Pointer to the array base */ \
|
|
} array_t[1]; \
|
|
\
|
|
/* Define an iterator over an array */ \
|
|
typedef struct M_F(name, _it_s) { \
|
|
size_t index; /* Index of the element */ \
|
|
const struct M_F(name, _s) *array; /* Reference of the array */ \
|
|
} it_t[1]; \
|
|
\
|
|
/* Definition of the synonyms of the type */ \
|
|
typedef struct M_F(name, _s) *M_F(name, _ptr); \
|
|
typedef const struct M_F(name, _s) *M_F(name, _srcptr); \
|
|
typedef array_t M_F(name, _ct); \
|
|
typedef it_t M_F(name, _it_ct); \
|
|
typedef type M_F(name, _subtype_ct); \
|
|
|
|
/* Define the core functions */
|
|
#define M_ARRA4_DEF_CORE(name, type, oplist, array_t, it_t) \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _init)(array_t v) \
|
|
{ \
|
|
M_ASSERT (v != NULL); \
|
|
/* Initially, the array is empty with nothing allocated */ \
|
|
v->size = 0; \
|
|
v->alloc = 0; \
|
|
v->ptr = NULL; \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _reset)(array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
for(size_t i = 0; i < v->size; i++) \
|
|
M_CALL_CLEAR(oplist, v->ptr[i]); \
|
|
v->size = 0; \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _clear)(array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_F(name, _reset)(v); \
|
|
M_CALL_FREE(oplist, v->ptr); \
|
|
/* This is so reusing the object implies an assertion failure */ \
|
|
v->alloc = 1; \
|
|
v->ptr = NULL; \
|
|
} \
|
|
\
|
|
M_IF_METHOD2(INIT_SET, SET, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _set)(array_t d, const array_t s) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(d); \
|
|
M_ARRA4_CONTRACT(s); \
|
|
if (M_UNLIKELY (d == s)) return; \
|
|
if (s->size > d->alloc) { \
|
|
const size_t alloc = s->size; \
|
|
type *ptr = M_CALL_REALLOC(oplist, type, d->ptr, alloc); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL)) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return ; \
|
|
} \
|
|
d->ptr = ptr; \
|
|
d->alloc = alloc; \
|
|
} \
|
|
size_t i; \
|
|
size_t step1 = M_MIN(s->size, d->size); \
|
|
for(i = 0; i < step1; i++) \
|
|
M_CALL_SET(oplist, d->ptr[i], s->ptr[i]); \
|
|
for( ; i < s->size; i++) \
|
|
M_CALL_INIT_SET(oplist, d->ptr[i], s->ptr[i]); \
|
|
for( ; i < d->size; i++) \
|
|
M_CALL_CLEAR(oplist, d->ptr[i]); \
|
|
d->size = s->size; \
|
|
M_ARRA4_CONTRACT(d); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _init_set)(array_t d, const array_t s) \
|
|
{ \
|
|
M_ASSERT (d != s); \
|
|
M_F(name, _init)(d); \
|
|
M_F(name, _set)(d, s); \
|
|
} \
|
|
, /* No SET & INIT_SET */) \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _init_move)(array_t d, array_t s) \
|
|
{ \
|
|
M_ASSERT (d != s); \
|
|
M_ARRA4_CONTRACT(s); \
|
|
d->size = s->size; \
|
|
d->alloc = s->alloc; \
|
|
d->ptr = s->ptr; \
|
|
/* Robustness */ \
|
|
s->alloc = 1; \
|
|
s->ptr = NULL; \
|
|
M_ARRA4_CONTRACT(d); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _move)(array_t d, array_t s) \
|
|
{ \
|
|
M_ASSERT (d != s); \
|
|
M_F(name, _clear)(d); \
|
|
M_F(name, _init_move)(d, s); \
|
|
} \
|
|
\
|
|
M_IF_METHOD(SET, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _set_at)(array_t v, size_t i, type const x) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT(v->size > 0 && v->ptr != NULL); \
|
|
M_ASSERT_INDEX(i, v->size); \
|
|
M_CALL_SET(oplist, v->ptr[i], x); \
|
|
} \
|
|
, /* No SET */) \
|
|
\
|
|
M_INLINE type * \
|
|
M_F(name, _back)(array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT(v->ptr != NULL); \
|
|
M_ASSERT_INDEX(0, v->size); \
|
|
return &v->ptr[v->size-1]; \
|
|
} \
|
|
\
|
|
M_INLINE type * \
|
|
M_F(name, _push_raw)(array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
if (M_UNLIKELY (v->size >= v->alloc)) { \
|
|
M_ASSERT(v->size == v->alloc); \
|
|
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
|
|
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return NULL; \
|
|
} \
|
|
M_ASSERT (alloc > v->size); \
|
|
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return NULL; \
|
|
} \
|
|
v->ptr = ptr; \
|
|
v->alloc = alloc; \
|
|
} \
|
|
M_ASSERT(v->ptr != NULL); \
|
|
type *ret = &v->ptr[v->size]; \
|
|
v->size++; \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSUME(ret != NULL); \
|
|
return ret; \
|
|
} \
|
|
\
|
|
M_IF_METHOD(INIT_SET, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _push_back)(array_t v, type const x) \
|
|
{ \
|
|
type *data = M_F(name, _push_raw)(v); \
|
|
if (M_UNLIKELY (data == NULL) ) \
|
|
return; \
|
|
M_CALL_INIT_SET(oplist, *data, x); \
|
|
} \
|
|
, /* No INIT_SET */ ) \
|
|
\
|
|
M_IF_METHOD(INIT, oplist)( \
|
|
M_INLINE type * \
|
|
M_F(name, _push_new)(array_t v) \
|
|
{ \
|
|
type *data = M_F(name, _push_raw)(v); \
|
|
if (M_UNLIKELY (data == NULL) ) \
|
|
return NULL; \
|
|
M_CALL_INIT(oplist, *data); \
|
|
return data; \
|
|
} \
|
|
, /* No INIT */ ) \
|
|
\
|
|
M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _push_move)(array_t v, type *x) \
|
|
{ \
|
|
M_ASSERT (x != NULL); \
|
|
type *data = M_F(name, _push_raw)(v); \
|
|
if (M_UNLIKELY (data == NULL) ) \
|
|
return; \
|
|
M_DO_INIT_MOVE (oplist, *data, *x); \
|
|
} \
|
|
, /* INIT_SET | INIT_MOVE */ ) \
|
|
\
|
|
M_IF_METHOD(INIT_SET, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _push_at)(array_t v, size_t key, type const x) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT_INDEX(key, v->size+1); \
|
|
if (M_UNLIKELY (v->size >= v->alloc) ) { \
|
|
M_ASSERT(v->size == v->alloc); \
|
|
size_t alloc = M_CALL_INC_ALLOC(oplist, v->alloc); \
|
|
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return ; \
|
|
} \
|
|
M_ASSERT (alloc > v->size); \
|
|
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return; \
|
|
} \
|
|
v->ptr = ptr; \
|
|
v->alloc = alloc; \
|
|
} \
|
|
M_ASSERT(v->ptr != NULL); \
|
|
memmove(&v->ptr[key+1], &v->ptr[key], (v->size-key)*sizeof(type)); \
|
|
v->size++; \
|
|
M_CALL_INIT_SET(oplist, v->ptr[key], x); \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
, /* No INIT_SET */ ) \
|
|
\
|
|
M_IF_METHOD(INIT, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _resize)(array_t v, size_t size) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
if (v->size > size) { \
|
|
/* Decrease size of array */ \
|
|
for(size_t i = size ; i < v->size; i++) \
|
|
M_CALL_CLEAR(oplist, v->ptr[i]); \
|
|
v->size = size; \
|
|
} else if (v->size < size) { \
|
|
/* Increase size of array */ \
|
|
if (size > v->alloc) { \
|
|
size_t alloc = size ; \
|
|
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return; \
|
|
} \
|
|
v->ptr = ptr; \
|
|
v->alloc = alloc; \
|
|
} \
|
|
for(size_t i = v->size ; i < size; i++) \
|
|
M_CALL_INIT(oplist, v->ptr[i]); \
|
|
v->size = size; \
|
|
} \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
, /* No INIT */ ) \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _reserve)(array_t v, size_t alloc) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
/* NOTE: Reserve below needed size to perform a shrink to fit */ \
|
|
if (v->size > alloc) { \
|
|
alloc = v->size; \
|
|
} \
|
|
if (M_UNLIKELY (alloc == 0)) { \
|
|
M_CALL_FREE(oplist, v->ptr); \
|
|
v->size = v->alloc = 0; \
|
|
v->ptr = NULL; \
|
|
} else { \
|
|
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return; \
|
|
} \
|
|
v->ptr = ptr; \
|
|
v->alloc = alloc; \
|
|
} \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
\
|
|
M_IF_METHOD(INIT, oplist)( \
|
|
M_INLINE type * \
|
|
M_F(name, _safe_get)(array_t v, size_t idx) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
const size_t size = idx + 1; \
|
|
/* resize if needed */ \
|
|
if (v->size <= size) { \
|
|
/* Increase size of array */ \
|
|
if (M_UNLIKELY (size > v->alloc) ) { \
|
|
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
|
|
/* In case of overflow of size_t */ \
|
|
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return NULL; \
|
|
} \
|
|
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return NULL; \
|
|
} \
|
|
v->ptr = ptr; \
|
|
v->alloc = alloc; \
|
|
} \
|
|
for(size_t i = v->size ; i < size; i++) \
|
|
M_CALL_INIT(oplist, v->ptr[i]); \
|
|
v->size = size; \
|
|
} \
|
|
M_ASSERT (idx < v->size); \
|
|
M_ARRA4_CONTRACT(v); \
|
|
return &v->ptr[idx]; \
|
|
} \
|
|
, /* No INIT */) \
|
|
\
|
|
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _pop_back)(type *dest, array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (v->ptr != NULL); \
|
|
M_ASSERT_INDEX(0, v->size); \
|
|
v->size--; \
|
|
if (dest) { \
|
|
M_DO_MOVE (oplist, *dest, v->ptr[v->size]); \
|
|
} else { \
|
|
M_CALL_CLEAR(oplist, v->ptr[v->size]); \
|
|
} \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
, /* SET | INIT_MOVE */ ) \
|
|
\
|
|
M_IF_AT_LEAST_METHOD(INIT_SET, INIT_MOVE, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _pop_move)(type *dest, array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (v->ptr != NULL); \
|
|
M_ASSERT_INDEX(0, v->size); \
|
|
M_ASSERT (dest != NULL); \
|
|
v->size--; \
|
|
M_DO_INIT_MOVE (oplist, *dest, v->ptr[v->size]); \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
, /* INIT_SET | INIT_MOVE */ ) \
|
|
\
|
|
M_IF_METHOD(INIT, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _pop_until)(array_t v, it_t pos) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (v == pos->array); \
|
|
M_ASSERT_INDEX(pos->index, v->size+1); \
|
|
M_F(name, _resize)(v, pos->index); \
|
|
} \
|
|
, /* No INIT */ ) \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _empty_p)(const array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
return v->size == 0; \
|
|
} \
|
|
\
|
|
M_INLINE size_t \
|
|
M_F(name, _size)(const array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
return v->size; \
|
|
} \
|
|
\
|
|
M_INLINE size_t \
|
|
M_F(name, _capacity)(const array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
return v->alloc; \
|
|
} \
|
|
\
|
|
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _pop_at)(type *dest, array_t v, size_t i) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (v->size > 0 && v->ptr != NULL); \
|
|
M_ASSERT_INDEX(i, v->size); \
|
|
if (dest) \
|
|
M_DO_MOVE (oplist, *dest, v->ptr[i]); \
|
|
else \
|
|
M_CALL_CLEAR(oplist, v->ptr[i]); \
|
|
memmove(&v->ptr[i], &v->ptr[i+1], sizeof(type)*(v->size-1-i)); \
|
|
v->size--; \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _erase)(array_t a, size_t i) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(a); \
|
|
if (i >= a->size) return false; \
|
|
M_F(name, _pop_at)(NULL, a, i); \
|
|
return true; \
|
|
} \
|
|
, /* SET | INIT_MOVE */ ) \
|
|
\
|
|
M_IF_METHOD(INIT, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _insert_v)(array_t v, size_t i, size_t num) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT_INDEX(i, v->size+1); \
|
|
size_t size = v->size + num; \
|
|
/* Test for overflow of variable size */ \
|
|
if (M_UNLIKELY_NOMEM (size <= v->size)) { \
|
|
/* Unlikely case of nothing to do */ \
|
|
if (num == 0) return; \
|
|
M_MEMORY_FULL(sizeof (type) * v->size); \
|
|
return ; \
|
|
} \
|
|
/* Test if alloc array is sufficient */ \
|
|
if (size > v->alloc) { \
|
|
size_t alloc = M_CALL_INC_ALLOC(oplist, size) ; \
|
|
if (M_UNLIKELY_NOMEM (alloc <= v->alloc)) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return ; \
|
|
} \
|
|
type *ptr = M_CALL_REALLOC(oplist, type, v->ptr, alloc); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
|
M_MEMORY_FULL(sizeof (type) * alloc); \
|
|
return; \
|
|
} \
|
|
v->ptr = ptr; \
|
|
v->alloc = alloc; \
|
|
} \
|
|
memmove(&v->ptr[i+num], &v->ptr[i], sizeof(type)*(v->size - i) ); \
|
|
for(size_t k = i ; k < i+num; k++) \
|
|
M_CALL_INIT(oplist, v->ptr[k]); \
|
|
v->size = size; \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
, /* No INIT */) \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _remove_v)(array_t v, size_t i, size_t j) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT(i < j && v->ptr != NULL); \
|
|
M_ASSERT_INDEX(i, v->size); \
|
|
M_ASSERT_INDEX(j, v->size+1); \
|
|
for(size_t k = i ; k < j; k++) \
|
|
M_CALL_CLEAR(oplist, v->ptr[k]); \
|
|
memmove(&v->ptr[i], &v->ptr[j], sizeof(type)*(v->size - j) ); \
|
|
v->size -= (j-i); \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _swap)(array_t v1, array_t v2) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v1); \
|
|
M_ARRA4_CONTRACT(v2); \
|
|
M_SWAP(size_t, v1->size, v2->size); \
|
|
M_SWAP(size_t, v1->alloc, v2->alloc); \
|
|
M_SWAP(type *, v1->ptr, v2->ptr); \
|
|
M_ARRA4_CONTRACT(v1); \
|
|
M_ARRA4_CONTRACT(v2); \
|
|
} \
|
|
\
|
|
M_IF_AT_LEAST_METHOD(INIT_SET,INIT_MOVE,oplist) ( \
|
|
M_INLINE void \
|
|
M_F(name, _swap_at)(array_t v, size_t i, size_t j) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT(v->ptr != NULL); \
|
|
M_ASSERT_INDEX(i, v->size); \
|
|
M_ASSERT_INDEX(j, v->size); \
|
|
type tmp; \
|
|
M_DO_INIT_MOVE(oplist, tmp, v->ptr[i]); \
|
|
M_DO_INIT_MOVE(oplist, v->ptr[i], v->ptr[j]); \
|
|
M_DO_INIT_MOVE(oplist, v->ptr[j], tmp); \
|
|
M_ARRA4_CONTRACT(v); \
|
|
} \
|
|
, /* INIT_SET | INIT_MOVE */ ) \
|
|
\
|
|
M_INLINE type * \
|
|
M_F(name, _get)(const array_t v, size_t i) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (v->ptr != NULL); \
|
|
M_ASSERT_INDEX(i, v->size); \
|
|
return &v->ptr[i]; \
|
|
} \
|
|
\
|
|
M_INLINE type const * \
|
|
M_F(name, _cget)(const array_t v, size_t i) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (v->ptr != NULL); \
|
|
M_ASSERT_INDEX(i, v->size); \
|
|
return M_CONST_CAST(type, &v->ptr[i]); \
|
|
} \
|
|
\
|
|
M_INLINE type * \
|
|
M_F(name, _front)(const array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT_INDEX(0, v->size); \
|
|
return M_F(name, _get)(v, 0); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _it)(it_t it, const array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (it != NULL); \
|
|
it->index = 0; \
|
|
it->array = v; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _it_last)(it_t it, const array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (it != NULL); \
|
|
/* If size is 0, index is -1 as unsigned, so it is greater than end */ \
|
|
it->index = v->size - 1; \
|
|
it->array = v; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _it_end)(it_t it, const array_t v) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(v); \
|
|
M_ASSERT (it != NULL); \
|
|
it->index = v->size; \
|
|
it->array = v; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _it_set)(it_t it, const it_t org) \
|
|
{ \
|
|
M_ASSERT (it != NULL && org != NULL); \
|
|
it->index = org->index; \
|
|
it->array = org->array; \
|
|
M_ARRA4_CONTRACT(it->array); \
|
|
} \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _end_p)(const it_t it) \
|
|
{ \
|
|
M_ASSERT(it != NULL && it->array != NULL); \
|
|
return it->index >= it->array->size; \
|
|
} \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _last_p)(const it_t it) \
|
|
{ \
|
|
M_ASSERT(it != NULL && it->array != NULL); \
|
|
/* NOTE: Can not compute 'size-1' due to potential overflow \
|
|
if size is 0 */ \
|
|
return it->index + 1 >= it->array->size; \
|
|
} \
|
|
\
|
|
M_INLINE bool \
|
|
M_F(name, _it_equal_p)(const it_t it1, \
|
|
const it_t it2) \
|
|
{ \
|
|
M_ASSERT(it1 != NULL && it2 != NULL); \
|
|
return it1->array == it2->array && it1->index == it2->index; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _next)(it_t it) \
|
|
{ \
|
|
M_ASSERT(it != NULL && it->array != NULL); \
|
|
it->index ++; \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _previous)(it_t it) \
|
|
{ \
|
|
M_ASSERT(it != NULL && it->array != NULL); \
|
|
/* NOTE: In the case index=0, it will be set to (unsigned) -1 \
|
|
==> it will be greater than size ==> end_p will return true */ \
|
|
it->index --; \
|
|
} \
|
|
\
|
|
M_INLINE type * \
|
|
M_F(name, _ref)(const it_t it) \
|
|
{ \
|
|
M_ASSERT(it != NULL); \
|
|
return M_F(name, _get)(it->array, it->index); \
|
|
} \
|
|
\
|
|
M_INLINE type const * \
|
|
M_F(name, _cref)(const it_t it) \
|
|
{ \
|
|
M_ASSERT(it != NULL); \
|
|
return M_F(name, _cget)(it->array, it->index); \
|
|
} \
|
|
\
|
|
M_IF_METHOD(INIT_SET, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _insert)(array_t a, it_t it, type const x) \
|
|
{ \
|
|
M_ASSERT (it != NULL && a == it->array); \
|
|
size_t index = M_F(name, _end_p)(it) ? 0 : it->index+1; \
|
|
M_F(name, _push_at)(a, index, x); \
|
|
it->index = index; \
|
|
} \
|
|
, /* End of INIT_SET */ ) \
|
|
\
|
|
M_IF_AT_LEAST_METHOD(SET, INIT_MOVE, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _remove)(array_t a, it_t it) \
|
|
{ \
|
|
M_ASSERT (it != NULL && a == it->array); \
|
|
M_F(name, _pop_at)(NULL, a, it->index); \
|
|
/* NOTE: it->index will naturaly point to the next element */ \
|
|
} \
|
|
, /* End of SET | INIT_SET */ ) \
|
|
\
|
|
M_IF_METHOD(CMP, oplist) \
|
|
( \
|
|
M_INLINE void M_F(name, _special_sort)(array_t l, \
|
|
int (*func_type) (type const *a, type const *b)) \
|
|
{ \
|
|
/* Using qsort is more compact but slower than a full templated \
|
|
version which can be twice faster */ \
|
|
int (*func_void)(const void*, const void*); \
|
|
/* There is no way (?) to avoid the cast */ \
|
|
func_void = (int (*)(const void*, const void*))func_type; \
|
|
qsort (l->ptr, l->size, sizeof(type), func_void); \
|
|
} \
|
|
\
|
|
M_IF_METHOD2(SWAP, SET, oplist)( \
|
|
M_INLINE void \
|
|
M_C3(m_arra4_,name,_stable_sort_noalloc)(type tab[], size_t size, type tmp[]) \
|
|
{ \
|
|
size_t th = 4; \
|
|
M_IF_DEBUG(type *org_tab = tab;) \
|
|
M_ASSERT (size > 1); \
|
|
/* Let's select the threshold of the pass 1 to be sure \
|
|
the final result is in tab.*/ \
|
|
if (m_core_clz64(size-1) & 1) \
|
|
th += th; \
|
|
\
|
|
/* Pass 1: Partial insertion sort (stable) up to 'th' size */ \
|
|
for(size_t k = 0 ; k < size; ) { \
|
|
size_t max = size - k < 2*th ? size - k : th; \
|
|
for(size_t i = 1; i < max; i++) { \
|
|
size_t j = i; \
|
|
while (j > 0 && M_CALL_CMP(oplist, tab[k+j-1], tab[k+j]) > 0) { \
|
|
M_CALL_SWAP(oplist, tab[k+j-1], tab[k+j]); \
|
|
j = j - 1; \
|
|
} \
|
|
} \
|
|
k += max; \
|
|
} \
|
|
\
|
|
/* N Pass of merge sort (stable) */ \
|
|
while (th < size) { \
|
|
type *dest = tmp; \
|
|
/* Pass n: Merge 2 sections of 'th' elements */ \
|
|
for(size_t k = 0 ; k < size; ) { \
|
|
type *el1 = &tab[k]; \
|
|
type *el2 = &tab[k+th]; \
|
|
size_t n1 = th; \
|
|
size_t n2 = size-k <= 3*th ? size-k-th : th; \
|
|
M_ASSERT (size-k > th); \
|
|
M_ASSERT (0 < n1 && n1 <= size); \
|
|
M_ASSERT (0 < n2 && n2 <= size); \
|
|
k += n1+n2; \
|
|
for (;;) { \
|
|
if (M_CALL_CMP(oplist, *el1, *el2) <= 0) { \
|
|
M_DO_INIT_MOVE(oplist, *dest, *el1); \
|
|
dest++; \
|
|
el1++; \
|
|
if (M_UNLIKELY(-- n1 == 0)) { \
|
|
if (n2 > 0) { \
|
|
memcpy (dest, el2, n2 * sizeof (type)); \
|
|
dest += n2; \
|
|
} \
|
|
break; \
|
|
} \
|
|
} else { \
|
|
M_DO_INIT_MOVE(oplist, *dest, *el2); \
|
|
dest++; \
|
|
el2++; \
|
|
if (M_UNLIKELY(-- n2 == 0)) { \
|
|
if (n1 > 0) { \
|
|
memcpy (dest, el1, n1 * sizeof (type)); \
|
|
dest += n1; \
|
|
} \
|
|
break; \
|
|
} \
|
|
} \
|
|
} \
|
|
} \
|
|
/* Swap t & tab */ \
|
|
M_SWAP(type *, tab, tmp); \
|
|
/* Increase th for next pass */ \
|
|
th += th; \
|
|
} \
|
|
M_ASSERT (org_tab == tab); \
|
|
} \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _special_stable_sort)(array_t l) \
|
|
{ \
|
|
if (M_UNLIKELY (l->size < 2)) \
|
|
return; \
|
|
/* NOTE: if size is <= 4, no need to perform an allocation */ \
|
|
type *temp = M_CALL_REALLOC(oplist, type, NULL, l->size); \
|
|
if (M_UNLIKELY_NOMEM (temp == NULL)) { \
|
|
M_MEMORY_FULL(sizeof (type) * l->size); \
|
|
return ; \
|
|
} \
|
|
M_C3(m_arra4_,name,_stable_sort_noalloc)(l->ptr, l->size, temp); \
|
|
M_CALL_FREE(oplist, temp); \
|
|
} \
|
|
,) /* IF SWAP & SET methods */ \
|
|
\
|
|
,) /* IF CMP oplist */ \
|
|
\
|
|
M_IF_METHOD(EQUAL, oplist)( \
|
|
M_INLINE bool \
|
|
M_F(name, _equal_p)(const array_t array1, \
|
|
const array_t array2) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(array1); \
|
|
M_ARRA4_CONTRACT(array2); \
|
|
if (array1->size != array2->size) return false; \
|
|
size_t i; \
|
|
for(i = 0; i < array1->size; i++) { \
|
|
type const *item1 = M_F(name, _cget)(array1, i); \
|
|
type const *item2 = M_F(name, _cget)(array2, i); \
|
|
bool b = M_CALL_EQUAL(oplist, *item1, *item2); \
|
|
if (!b) return false; \
|
|
} \
|
|
return i == array1->size; \
|
|
} \
|
|
, /* no EQUAL */ ) \
|
|
\
|
|
M_IF_METHOD(HASH, oplist)( \
|
|
M_INLINE size_t \
|
|
M_F(name, _hash)(const array_t array) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(array); \
|
|
M_HASH_DECL(hash); \
|
|
for(size_t i = 0 ; i < array->size; i++) { \
|
|
size_t hi = M_CALL_HASH(oplist, array->ptr[i]); \
|
|
M_HASH_UP(hash, hi); \
|
|
} \
|
|
return M_HASH_FINAL (hash); \
|
|
} \
|
|
, /* no HASH */ ) \
|
|
\
|
|
M_INLINE void \
|
|
M_F(name, _splice)(array_t a1, array_t a2) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(a1); \
|
|
M_ARRA4_CONTRACT(a2); \
|
|
if (M_LIKELY (a2->size > 0)) { \
|
|
size_t newSize = a1->size + a2->size; \
|
|
/* To overflow newSize, we need to a1 and a2 a little bit above \
|
|
SIZE_MAX/2, which is not possible in the classic memory model as we \
|
|
should have exhausted all memory before reaching such sizes. */ \
|
|
M_ASSERT(newSize > a1->size); \
|
|
if (newSize > a1->alloc) { \
|
|
type *ptr = M_CALL_REALLOC(oplist, type, a1->ptr, newSize); \
|
|
if (M_UNLIKELY_NOMEM (ptr == NULL) ) { \
|
|
M_MEMORY_FULL(sizeof (type) * newSize); \
|
|
} \
|
|
a1->ptr = ptr; \
|
|
a1->alloc = newSize; \
|
|
} \
|
|
M_ASSERT(a1->ptr != NULL); \
|
|
M_ASSERT(a2->ptr != NULL); \
|
|
memcpy(&a1->ptr[a1->size], &a2->ptr[0], a2->size * sizeof (type)); \
|
|
/* a2 is now empty */ \
|
|
a2->size = 0; \
|
|
/* a1 has been expanded with the items of a2 */ \
|
|
a1->size = newSize; \
|
|
} \
|
|
} \
|
|
|
|
/* Define the I/O functions */
|
|
#define M_ARRA4_DEF_IO(name, type, oplist, array_t, it_t) \
|
|
\
|
|
M_IF_METHOD(GET_STR, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _get_str)(m_string_t str, array_t const array, \
|
|
bool append) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(array); \
|
|
(append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \
|
|
it_t it; \
|
|
for (M_F(name, _it)(it, array) ; \
|
|
!M_F(name, _end_p)(it); \
|
|
M_F(name, _next)(it)){ \
|
|
type const *item = M_F(name, _cref)(it); \
|
|
M_CALL_GET_STR(oplist, str, *item, true); \
|
|
if (!M_F(name, _last_p)(it)) \
|
|
m_string_push_back (str, M_GET_SEPARATOR oplist); \
|
|
} \
|
|
m_string_push_back (str, ']'); \
|
|
} \
|
|
, /* no GET_STR */ ) \
|
|
\
|
|
M_IF_METHOD(OUT_STR, oplist)( \
|
|
M_INLINE void \
|
|
M_F(name, _out_str)(FILE *file, const array_t array) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(array); \
|
|
M_ASSERT (file != NULL); \
|
|
fputc ('[', file); \
|
|
for (size_t i = 0; i < array->size; i++) { \
|
|
type const *item = M_F(name, _cget)(array, i); \
|
|
M_CALL_OUT_STR(oplist, file, *item); \
|
|
if (i != array->size-1) \
|
|
fputc (M_GET_SEPARATOR oplist, file); \
|
|
} \
|
|
fputc (']', file); \
|
|
} \
|
|
, /* no OUT_STR */ ) \
|
|
\
|
|
M_IF_METHOD2(PARSE_STR, INIT, oplist)( \
|
|
M_INLINE bool \
|
|
M_F(name, _parse_str)(array_t array, const char str[], const char**endp) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(array); \
|
|
M_ASSERT (str != NULL); \
|
|
M_F(name,_reset)(array); \
|
|
int c = *str++; \
|
|
if (M_UNLIKELY (c != '[')) { c = 0; goto exit; } \
|
|
c = m_core_str_nospace(&str); \
|
|
if (M_UNLIKELY (c == ']')) { goto exit; } \
|
|
if (M_UNLIKELY (c == 0)) { goto exit; } \
|
|
str--; \
|
|
M_QLET(item, type, oplist) { \
|
|
do { \
|
|
bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \
|
|
c = m_core_str_nospace(&str); \
|
|
if (b == false || c == 0) { c = 0; break; } \
|
|
M_F(name, _push_back)(array, item); \
|
|
} while (c == M_GET_SEPARATOR oplist); \
|
|
} \
|
|
exit: \
|
|
M_ARRA4_CONTRACT(array); \
|
|
if (endp) *endp = str; \
|
|
return c == ']'; \
|
|
} \
|
|
, /* no PARSE_STR & INIT */ ) \
|
|
\
|
|
M_IF_METHOD2(IN_STR, INIT, oplist)( \
|
|
M_INLINE bool \
|
|
M_F(name, _in_str)(array_t array, FILE *file) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(array); \
|
|
M_ASSERT (file != NULL); \
|
|
M_F(name,_reset)(array); \
|
|
int c = fgetc(file); \
|
|
if (M_UNLIKELY (c != '[')) return false; \
|
|
c = fgetc(file); \
|
|
if (M_UNLIKELY (c == ']')) return true; \
|
|
if (M_UNLIKELY (c == EOF)) return false; \
|
|
ungetc(c, file); \
|
|
M_QLET(item, type, oplist) { \
|
|
do { \
|
|
bool b = M_CALL_IN_STR(oplist, item, file); \
|
|
c = m_core_fgetc_nospace(file); \
|
|
if (b == false || c == EOF) { c = 0; break; } \
|
|
M_F(name, _push_back)(array, item); \
|
|
} while (c == M_GET_SEPARATOR oplist); \
|
|
} \
|
|
M_ARRA4_CONTRACT(array); \
|
|
return c == ']'; \
|
|
} \
|
|
, /* no IN_STR & INIT */ ) \
|
|
\
|
|
M_IF_METHOD(OUT_SERIAL, oplist)( \
|
|
M_INLINE m_serial_return_code_t \
|
|
M_F(name, _out_serial)(m_serial_write_t f, const array_t array) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(array); \
|
|
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
|
m_serial_return_code_t ret; \
|
|
m_serial_local_t local; \
|
|
ret = f->m_interface->write_array_start(local, f, array->size); \
|
|
for (size_t i = 0; i < array->size; i++) { \
|
|
type const *item = M_F(name, _cget)(array, i); \
|
|
if (i != 0) { \
|
|
ret |= f->m_interface->write_array_next(local, f); \
|
|
} \
|
|
ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \
|
|
} \
|
|
ret |= f->m_interface->write_array_end(local, f); \
|
|
return ret & M_SERIAL_FAIL; \
|
|
} \
|
|
, /* no OUT_SERIAL */ ) \
|
|
\
|
|
M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \
|
|
M_INLINE m_serial_return_code_t \
|
|
M_F(name, _in_serial)(array_t array, m_serial_read_t f) \
|
|
{ \
|
|
M_ARRA4_CONTRACT(array); \
|
|
M_ASSERT (f != NULL && f->m_interface != NULL); \
|
|
m_serial_return_code_t ret; \
|
|
m_serial_local_t local; \
|
|
size_t estimated_size = 0; \
|
|
M_F(name,_reset)(array); \
|
|
ret = f->m_interface->read_array_start(local, f, &estimated_size); \
|
|
if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) { \
|
|
return ret; \
|
|
} \
|
|
M_F(name, _reserve)(array, estimated_size); \
|
|
M_QLET(item, type, oplist) { \
|
|
do { \
|
|
ret = M_CALL_IN_SERIAL(oplist, item, f); \
|
|
if (ret != M_SERIAL_OK_DONE) { break; } \
|
|
M_F(name, _push_back)(array, item); \
|
|
ret = f->m_interface->read_array_next(local, f); \
|
|
} while (ret == M_SERIAL_OK_CONTINUE); \
|
|
} \
|
|
M_ARRA4_CONTRACT(array); \
|
|
return ret; \
|
|
} \
|
|
, /* no IN_SERIAL & INIT */ ) \
|
|
|
|
/* Definition of the emplace_back function for arrays */
|
|
#define M_ARRA4_EMPLACE_DEF(name, name_t, function_name, oplist, init_func, exp_emplace_type) \
|
|
M_INLINE void \
|
|
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)(v); \
|
|
if (M_UNLIKELY (data == NULL) ) \
|
|
return; \
|
|
M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \
|
|
} \
|
|
|
|
/********************************** INTERNAL *********************************/
|
|
|
|
#if M_USE_SMALL_NAME
|
|
#define ARRAY_DEF M_ARRAY_DEF
|
|
#define ARRAY_DEF_AS M_ARRAY_DEF_AS
|
|
#define ARRAY_OPLIST M_ARRAY_OPLIST
|
|
#define ARRAY_INIT_VALUE M_ARRAY_INIT_VALUE
|
|
#endif
|
|
|
|
#endif
|