/* * M*LIB - DEQUE 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_DEQUE_H #define MSTARLIB_DEQUE_H #include "m-i-list.h" /* Define a deque of a given type and its associated functions. USAGE: DEQUE_DEF(name, type [, oplist_of_the_type]) */ #define M_DEQUE_DEF(name, ...) \ M_DEQUE_DEF_AS(name, M_F(name,_t), M_F(name,_it_t), __VA_ARGS__) /* Define a deque of a given type and its associated functions. as the provided type name_t with the iterator named it_t. USAGE: DEQUE_DEF(name, name_t, it_t, type [, oplist_of_the_type]) */ #define M_DEQUE_DEF_AS(name, name_t, it_t, ...) \ M_BEGIN_PROTECTED_CODE \ M_D3QU3_DEF_P1(M_IF_NARGS_EQ1(__VA_ARGS__) \ ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__)(), name_t, it_t, M_F(name, _node_ct) ), \ (name, __VA_ARGS__, name_t, it_t, M_F(name, _node_ct)))) \ M_END_PROTECTED_CODE /* Define the oplist of a deque of a type. USAGE: DEQUE_OPLIST(name[, oplist of the type]) */ #define M_DEQUE_OPLIST(...) \ M_D3QU3_OPLIST_P1 (M_IF_NARGS_EQ1(__VA_ARGS__) \ ((__VA_ARGS__, M_BASIC_OPLIST), \ (__VA_ARGS__ ))) /* Default initial size of a bucket of items */ #ifndef M_USE_DEQUE_DEFAULT_SIZE #define M_USE_DEQUE_DEFAULT_SIZE 8 #endif /*****************************************************************************/ /********************************* INTERNAL **********************************/ /*****************************************************************************/ /* Define the internal contract of a deque */ #define M_D3QU3_CONTRACT(d) do { \ M_ASSERT ((d) != NULL); \ M_ASSERT ((d)->default_size >= M_USE_DEQUE_DEFAULT_SIZE); \ M_ASSERT ((d)->front->node != NULL); \ M_ASSERT ((d)->front->index <= (d)->front->node->size); \ M_ASSERT ((d)->back->node != NULL); \ M_ASSERT ((d)->back->index <= (d)->back->node->size); \ M_ASSERT ((d)->front->node != (d)->back->node || \ (d)->front->index <= (d)->back->index); \ M_ASSERT ((d)->front->node != (d)->back->node || \ (d)->back->index - (d)->front->index == (d)->count); \ } while (0) /* Deferred evaluation for the deque definition, so that all arguments are evaluated before further expansion */ #define M_D3QU3_DEF_P1(arg) M_ID( M_D3QU3_DEF_P2 arg ) /* Validate the oplist before going further */ #define M_D3QU3_DEF_P2(name, type, oplist, deque_t, it_t, node_t) \ M_IF_OPLIST(oplist)(M_D3QU3_DEF_P3, M_D3QU3_DEF_FAILURE)(name, type, oplist, deque_t, it_t, node_t) /* Stop processing with a compilation failure */ #define M_D3QU3_DEF_FAILURE(name, type, oplist, deque_t, it_t, node_t) \ M_STATIC_FAILURE(M_LIB_NOT_AN_OPLIST, "(DEQUE_DEF): the given argument is not a valid oplist: " #oplist) /* Internal deque definition - name: prefix to be used - type: type of the elements of the deque - oplist: oplist of the type of the elements of the container - deque_t: alias for M_F(name, _t) [ type of the container ] - it_t: alias for M_F(name, _it_t) [ iterator of the container ] - node_t: alias for node_t [ node ] */ #define M_D3QU3_DEF_P3(name, type, oplist, deque_t, it_t, node_t) \ M_D3QU3_DEF_TYPE(name, type, oplist, deque_t, it_t, node_t) \ M_CHECK_COMPATIBLE_OPLIST(name, 1, type, oplist) \ M_D3QU3_DEF_CORE(name, type, oplist, deque_t, it_t, node_t) \ M_EMPLACE_QUEUE_DEF(name, deque_t, M_F(name, _emplace_back), oplist, M_D3QUE_EMPLACE_BACK_DEF) \ M_EMPLACE_QUEUE_DEF(name, deque_t, M_F(name, _emplace_front), oplist, M_D3QUE_EMPLACE_FRONT_DEF) /* Define the types. It is a linked list of buckets of the types, each new created bucket size grows compared to the previous one resulting in: strict O(1) for push/pop O(ln(n)) for random access. No insert / delete operations are planned. [Could be done in O(n) complexity if needed] Define the bucket (aka node) structure. */ #define M_D3QU3_DEF_TYPE(name, type, oplist, deque_t, it_t, node_t) \ \ typedef struct M_F(name, _node_s) { \ ILIST_INTERFACE(M_F(name, _node_list), struct M_F(name, _node_s)); \ size_t size; \ type data[M_MIN_FLEX_ARRAY_SIZE]; \ } node_t; \ \ /* Each node is allocated with a variable size bucket (so we use \ M_GET_REALLOC for the allocation). But we want to delete the nodes \ automatically with the intrusive list used for storing the nodes: \ so we register as a DEL operator the FREE operator of the oplist. \ The interfaces are compatible. \ */ \ /* FIXME: How can I separate public types and private implementation? */ \ ILIST_DEF(M_F(name, _node_list), node_t, (DEL(M_GET_FREE oplist)) ) \ \ /* Define an internal iterator */ \ typedef struct M_F(name, _it2_s) { \ node_t *node; \ size_t index; \ } M_F(name, _it2_ct)[1]; \ \ /* Define the deque type: \ - 'list' if the list of buckets containing the objects. \ - 'front' is a pseudo-iterator to the first \ - 'back' is a pseudo-iterator to the one after last element \ - 'default_size' is the size used for the creation of a new bucket \ - 'count' is the number of elements in the container. \ */ \ typedef struct M_F(name, _s) { \ M_F(name, _node_list_t) list; \ M_F(name, _it2_ct) front; \ M_F(name, _it2_ct) back; \ size_t default_size; \ size_t count; \ } deque_t[1]; \ \ /* Define pointer alias */ \ typedef struct M_F(name, _s) *M_F(name, _ptr); \ typedef const struct M_F(name, _s) *M_F(name, _srcptr); \ \ /* Define the iterator object */ \ typedef struct M_F(name, _it_s) { \ node_t *node; \ size_t index; \ const struct M_F(name, _s) *deque; \ } it_t[1]; \ \ /* Define internal types for oplist */ \ typedef deque_t M_F(name, _ct); \ typedef type M_F(name, _subtype_ct); \ typedef it_t M_F(name, _it_ct); \ /* Define the core functions */ #define M_D3QU3_DEF_CORE(name, type, oplist, deque_t, it_t, node_t) \ \ /* Allocate a new node for a deque */ \ M_INLINE node_t* \ M_C3(m_d3qu3_,name,_new_node)(deque_t d) \ { \ size_t def = d->default_size; \ /* Test for overflow of the size computation */ \ if (M_UNLIKELY_NOMEM (def > SIZE_MAX / sizeof (type) - sizeof(node_t))) { \ M_MEMORY_FULL(sizeof(node_t)+def * sizeof(type)); \ return NULL; \ } \ /* Alloc a new node with dynamic size */ \ node_t*n = (node_t*) (void*) \ M_CALL_REALLOC(oplist, char, NULL, \ sizeof(node_t) + def * sizeof(type) ); \ if (M_UNLIKELY_NOMEM (n==NULL)) { \ M_MEMORY_FULL(sizeof(node_t)+def * sizeof(type)); \ return NULL; \ } \ /* Initialize the node */ \ n->size = def; \ M_F(name, _node_list_init_field)(n); \ /* Increase the next bucket allocation */ \ /* Do not increase it too much if there are few items */ \ def = M_MIN(def, d->count); \ d->default_size = M_CALL_INC_ALLOC(oplist, def); \ /* FIXME: Check for overflow? */ \ return n; \ } \ \ M_INLINE void \ M_F(name, _init)(deque_t d) \ { \ M_F(name, _node_list_init)(d->list); \ d->default_size = M_USE_DEQUE_DEFAULT_SIZE; \ d->count = 0; \ node_t *n = M_C3(m_d3qu3_,name,_new_node)(d); \ if (n == NULL) return; \ M_F(name, _node_list_push_back)(d->list, n); \ d->front->node = n; \ d->front->index = M_USE_DEQUE_DEFAULT_SIZE/2; \ d->back->node = n; \ d->back->index = M_USE_DEQUE_DEFAULT_SIZE/2; \ M_D3QU3_CONTRACT(d); \ } \ \ M_INLINE void \ M_F(name, _reset)(deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ node_t *min_node = NULL; \ for(node_t *n = d->front->node; \ n != NULL ; \ n = (n == d->back->node) ? NULL : \ M_F(name, _node_list_next_obj)(d->list, n) ){ \ size_t min = n == d->front->node ? d->front->index : 0; \ size_t max = n == d->back->node ? d->back->index : n->size; \ for(size_t i = min; i < max; i++) { \ M_CALL_CLEAR(oplist, n->data[i]); \ } \ min_node = (min_node == NULL || min_node->size > n->size) ? n : min_node; \ } \ M_ASSERT (min_node != NULL); \ d->front->node = min_node; \ d->front->index = min_node->size / 2; \ d->back->node = min_node; \ d->back->index = min_node->size / 2; \ d->count = 0; \ M_D3QU3_CONTRACT(d); \ } \ \ M_INLINE void \ M_F(name, _clear)(deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_F(name, _reset)(d); \ /* We have registered the delete operator to clear all objects */ \ M_F(name, _node_list_clear)(d->list); \ /* It is safer to clean some variables */ \ d->front->node = NULL; \ d->back->node = NULL; \ d->count = 0; \ } \ \ M_INLINE type * \ M_F(name, _push_back_raw)(deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ node_t *n = d->back->node; \ size_t index = d->back->index; \ if (M_UNLIKELY (n->size <= index)) { \ /* try to get an already allocated node */ \ n = M_F(name, _node_list_next_obj)(d->list, n); \ if (n == NULL) { \ /* No node exists, allocate a new one */ \ n = M_C3(m_d3qu3_,name,_new_node)(d); \ if (M_UNLIKELY (n == NULL)) return NULL; \ M_F(name, _node_list_push_back)(d->list, n); \ } \ d->back->node = n; \ index = 0; \ } \ type *ret = &n->data[index]; \ index++; \ d->count ++; \ d->back->index = index; \ M_D3QU3_CONTRACT(d); \ return ret; \ } \ \ /* Internal, for INIT_WITH */ \ M_INLINE type * \ M_F(name, _push_raw)(deque_t d) \ { \ return M_F(name, _push_back_raw)(d); \ } \ \ M_INLINE void \ M_F(name, _push_back)(deque_t d, type const x) \ { \ type *p = M_F(name, _push_back_raw)(d); \ if (M_LIKELY(p != NULL)) { \ M_CALL_INIT_SET(oplist, *p, x); \ } \ } \ \ M_IF_METHOD(INIT, oplist)( \ M_INLINE type * \ M_F(name, _push_back_new)(deque_t d) \ { \ type *p = M_F(name, _push_back_raw)(d); \ if (M_LIKELY(p != NULL)) { \ M_CALL_INIT(oplist, *p); \ } \ return p; \ } \ , ) \ \ M_INLINE void \ M_F(name, _push_back_move)(deque_t d, type *x) \ { \ M_ASSERT (x != NULL); \ type *p = M_F(name, _push_back_raw)(d); \ if (M_UNLIKELY(p == NULL)) \ return; \ M_DO_INIT_MOVE (oplist, *p, *x); \ } \ \ M_INLINE type* \ M_F(name, _push_front_raw)(deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ node_t *n = d->front->node; \ size_t index = d->front->index; \ index --; \ /* If overflow */ \ if (M_UNLIKELY (n->size <= index)) { \ n = M_F(name, _node_list_previous_obj)(d->list, n); \ if (n == NULL) { \ n = M_C3(m_d3qu3_,name,_new_node)(d); \ if (M_UNLIKELY (n == NULL)) return NULL; \ M_F(name, _node_list_push_front)(d->list, n); \ } \ d->front->node = n; \ index = n->size -1; \ } \ type *ret = &n->data[index]; \ d->count ++; \ d->front->index = index; \ M_D3QU3_CONTRACT(d); \ return ret; \ } \ \ M_INLINE void \ M_F(name, _push_front)(deque_t d, type const x) \ { \ type *p = M_F(name, _push_front_raw)(d); \ if (M_LIKELY(p != NULL)) { \ M_CALL_INIT_SET(oplist, *p, x); \ } \ } \ \ M_IF_METHOD(INIT, oplist)( \ M_INLINE type * \ M_F(name, _push_front_new)(deque_t d) \ { \ type *p = M_F(name, _push_front_raw)(d); \ if (M_LIKELY(p != NULL)) { \ M_CALL_INIT(oplist, *p); \ } \ return p; \ } \ ,) \ \ M_INLINE void \ M_F(name, _push_front_move)(deque_t d, type *x) \ { \ M_ASSERT (x != NULL); \ type *p = M_F(name, _push_front_raw)(d); \ if (M_UNLIKELY(p == NULL)) \ return; \ M_DO_INIT_MOVE (oplist, *p, *x); \ } \ \ M_INLINE void \ M_F(name, _pop_back)(type *ptr, deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT(d->count > 0); \ node_t *n = d->back->node; \ size_t index = d->back->index; \ index --; \ if (M_UNLIKELY (n->size <= index)) { \ /* If there is a next node, \ pop the back node and push it back to the front. This \ reduce the used memory if the deque is used as a FIFO queue.*/ \ node_t *next = M_F(name, _node_list_next_obj)(d->list, n); \ if (next != NULL) { \ next = M_F(name, _node_list_pop_back)(d->list); \ M_ASSERT (next != n); \ M_F(name, _node_list_push_front)(d->list, next); \ } \ n = M_F(name, _node_list_previous_obj)(d->list, n); \ M_ASSERT (n != NULL && n->size > 1); \ d->back->node = n; \ index = n->size-1; \ } \ if (ptr != NULL) \ M_IF_METHOD(MOVE, oplist) ( \ M_CALL_MOVE(oplist, *ptr, n->data[index]); else \ , \ M_CALL_SET(oplist, *ptr, n->data[index]); \ ) \ M_CALL_CLEAR(oplist, n->data[index]); \ d->count --; \ d->back->index = index; \ M_D3QU3_CONTRACT(d); \ } \ \ M_IF_METHOD(INIT, oplist)( \ M_INLINE void \ M_F(name, _pop_back_move)(type *ptr, deque_t d) \ { \ M_ASSERT(ptr != NULL); \ /* Note: Lazy implementation. Can be improved if needed */ \ M_CALL_INIT(oplist, *ptr); \ M_F(name, _pop_back)(ptr, d); \ } \ , ) \ \ M_INLINE void \ M_F(name, _pop_front)(type *ptr, deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT(d->count > 0); \ node_t *n = d->front->node; \ size_t index = d->front->index; \ if (ptr != NULL) \ M_IF_METHOD(MOVE, oplist) ( \ M_CALL_MOVE(oplist, *ptr, n->data[index]); else \ , \ M_CALL_SET(oplist, *ptr, n->data[index]); \ ) \ M_CALL_CLEAR(oplist, n->data[index]); \ index++; \ if (M_UNLIKELY (n->size <= index)) { \ /* If there is a previous node, \ pop the front node and push it back to the back. This \ recycles the used memory if the deque is used as a FIFO queue.*/ \ node_t *prev = M_F(name, _node_list_previous_obj)(d->list, n); \ if (prev != NULL) { \ prev = M_F(name, _node_list_pop_front)(d->list); \ M_ASSERT (prev != n); \ M_F(name, _node_list_push_back)(d->list, prev); \ } \ /* Update front iterator to point to the next object */ \ n = M_F(name, _node_list_next_obj)(d->list, n); \ if (M_UNLIKELY(n == NULL)) { \ /* No next obj. \ It is only possible if there was only 1 element */ \ M_ASSERT(d->count == 1); \ /* Reset the deque to the midle of the current node */ \ d->back->node = d->front->node; \ index = d->front->node->size/2; \ d->back->index = d->front->node->size/2; \ } else { \ d->front->node = n; \ index = 0; \ } \ } \ d->count --; \ d->front->index = index; \ M_D3QU3_CONTRACT(d); \ } \ \ M_IF_METHOD(INIT, oplist)( \ M_INLINE void \ M_F(name, _pop_front_move)(type *ptr, deque_t d) \ { \ M_ASSERT(ptr != NULL); \ /* Note: Lazy implementation */ \ M_CALL_INIT(oplist, *ptr); \ M_F(name, _pop_front)(ptr, d); \ } \ ,) \ \ M_INLINE type * \ M_F(name, _back)(const deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT (d->count > 0); \ size_t i = d->back->index; \ node_t *n = d->back->node; \ if (M_UNLIKELY (i == 0)) { \ n = M_F(name, _node_list_previous_obj)(d->list, n); \ M_ASSERT (n != NULL); \ i = n->size; \ } \ return &n->data[i-1]; \ } \ \ M_INLINE type * \ M_F(name, _front)(const deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT (d->count > 0); \ size_t i = d->front->index; \ node_t *n = d->front->node; \ return &n->data[i]; \ } \ \ M_INLINE size_t \ M_F(name, _size)(const deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ return d->count; \ } \ \ M_INLINE size_t \ M_F(name, _capacity_back)(const deque_t v) \ { \ M_D3QU3_CONTRACT(v); \ size_t s = 0; \ for(node_t *n = M_F(name, _node_list_back)(v->list); \ n != NULL ; \ n = (n == v->back->node) ? NULL : \ M_F(name, _node_list_previous_obj)(v->list, n) ){ \ s += (n == v->back->node ? v->back->index : n->size); \ } \ return s; \ } \ \ M_INLINE size_t \ M_F(name, _capacity_front)(const deque_t v) \ { \ M_D3QU3_CONTRACT(v); \ size_t s = 0; \ for(node_t *n = M_F(name, _node_list_front)(v->list); \ n != NULL ; \ n = (n == v->front->node) ? NULL : \ M_F(name, _node_list_next_obj)(v->list, n) ){ \ s += n->size - (n == v->front->node ? v->front->index : 0); \ } \ return s; \ } \ \ M_INLINE size_t \ M_F(name, _capacity)(const deque_t v) \ { \ return M_F(name, _capacity_back)(v)+M_F(name, _capacity_front)(v); \ } \ \ M_INLINE bool \ M_F(name, _empty_p)(const deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ return d->count == 0; \ } \ \ M_INLINE void \ M_F(name, _it)(it_t it, const deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT (it != NULL); \ it->node = d->front->node; \ it->index = d->front->index; \ it->deque = d; \ } \ \ M_INLINE void \ M_F(name, _it_last)(it_t it, const deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT (it != NULL); \ it->node = d->back->node; \ it->index = d->back->index - 1; \ it->deque = d; \ if (M_UNLIKELY (it->index >= it->node->size)) { \ it->node = M_F(name, _node_list_previous_obj)(d->list, it->node); \ M_ASSERT (it->node != NULL && it->node->size > 1); \ it->index = it->node->size-1; \ } \ } \ \ M_INLINE void \ M_F(name, _it_end)(it_t it, const deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT (it != NULL); \ it->node = d->back->node; \ it->index = d->back->index; \ it->deque = d; \ } \ \ M_INLINE void \ M_F(name, _it_set)(it_t it1, const it_t it2) \ { \ M_ASSERT (it1 != NULL); \ M_ASSERT (it2 != NULL); \ it1->node = it2->node; \ it1->index = it2->index; \ it1->deque = it2->deque; \ } \ \ M_INLINE bool \ M_F(name, _end_p)(it_t it) \ { \ M_ASSERT (it != NULL); \ return (it->node == it->deque->back->node \ && it->index >= it->deque->back->index) \ || (it->node == it->deque->front->node \ && it->index < it->deque->front->index); \ } \ \ M_INLINE void \ M_F(name, _next)(it_t it) \ { \ M_ASSERT (it != NULL); \ node_t *n = it->node; \ it->index ++; \ if (M_UNLIKELY (it->index >= n->size)) { \ n = M_F(name, _node_list_next_obj)(it->deque->list, n); \ if (M_UNLIKELY (n == NULL || it->node == it->deque->back->node)) { \ /* Point to 'end' (can't undo it) */ \ it->node = it->deque->back->node; \ it->index = it->deque->back->node->size; \ return; \ } \ it->node = n; \ it->index = 0; \ } \ } \ \ M_INLINE void \ M_F(name, _previous)(it_t it) \ { \ M_ASSERT (it != NULL); \ node_t *n = it->node; \ it->index --; \ if (M_UNLIKELY (it->index >= n->size)) { \ n = M_F(name, _node_list_previous_obj)(it->deque->list, n); \ if (M_UNLIKELY (n == NULL || it->node == it->deque->front->node)) { \ /* Point to 'end' (can't undo it) */ \ it->node = it->deque->back->node; \ it->index = it->deque->back->node->size; \ return; \ } \ it->node = n; \ it->index = n->size - 1; \ } \ } \ \ M_INLINE bool \ M_F(name, _last_p)(it_t it) \ { \ M_ASSERT (it != NULL); \ it_t it2; \ M_F(name, _it_set)(it2, it); \ M_F(name, _next)(it2); \ return M_F(name, _end_p)(it2); \ } \ \ M_INLINE bool \ M_F(name, _it_equal_p)(it_t it1, const it_t it2) \ { \ M_ASSERT (it1 != NULL); \ M_ASSERT (it2 != NULL); \ return it1->deque == it2->deque \ && it1->node == it2->node \ && it1->index == it2->index; \ } \ \ M_INLINE type * \ M_F(name, _ref)(it_t it) \ { \ M_ASSERT (it != NULL); \ M_ASSERT (it->index < it->node->size); \ return &it->node->data[it->index]; \ } \ \ M_INLINE type const * \ M_F(name, _cref)(it_t it) \ { \ M_ASSERT (it != NULL); \ M_ASSERT (it->index < it->node->size); \ return M_CONST_CAST(type, &it->node->data[it->index]); \ } \ \ M_INLINE void \ M_F(name, _remove)(deque_t d, it_t it) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT (it != NULL && it->node != NULL); \ M_ASSERT (d->count >= 1); \ M_ASSERT_INDEX(it->index, it->node->size); \ \ node_t *n = it->node; \ M_CALL_CLEAR(oplist, n->data[it->index]); \ if (n == d->back->node) { \ /* back index points to the element after the end */ \ M_ASSERT (d->back->index != 0); \ M_ASSERT (it->index < d->back->index); \ /* We cannot have a node deletion in this case */ \ memmove(&n->data[it->index], &n->data[it->index+1], \ sizeof(type) * (d->back->index - it->index - 1)); \ d->back->index --; \ /* The iterator points to the next element */ \ } else if (n == d->front->node) { \ /* front index points to the first element */ \ if (M_UNLIKELY (d->front->index == n->size -1)) { \ /* Node 'smart' deletion (single element) */ \ /* Update front iterator to point to the next object */ \ n = M_F(name, _node_list_next_obj)(d->list, n); \ /* We must have a next element, as we have a different back node \ than the front node. */ \ M_ASSERT (n != NULL); \ d->front->node = n; \ d->front->index = 0; \ /* The iterator references this element */ \ it->node = n; \ it->index = 0; \ } else { \ memmove(&n->data[d->front->index+1], &n->data[d->front->index], \ sizeof(type) * (it->index - d->front->index)); \ d->front->index ++; \ /* The iterator shall reference the next element */ \ M_F(name, _next)(it); \ } \ } else { \ /* Nether front or end node */ \ if (M_UNLIKELY(n->size == 1)) { \ /* The iterator shall reference the next element */ \ M_F(name, _next)(it); \ /* Node deletion */ \ M_ASSERT(d->count > 1); \ M_F(name, _node_list_unlink)(n); \ M_CALL_FREE(oplist, n); \ } else { \ memmove(&n->data[it->index], &n->data[it->index+1], \ sizeof(type) * (it->node->size - it->index - 1)); \ /* We lose capacity of the node... */ \ n->size --; \ /* The iterator points to the next element \ except if it was the last one*/ \ if (M_UNLIKELY(it->index >= n->size)) M_F(name, _next)(it); \ } \ } \ d->count--; \ M_D3QU3_CONTRACT(d); \ } \ \ M_INLINE void \ M_F(name, _init_set)(deque_t d, const deque_t src) \ { \ M_D3QU3_CONTRACT(src); \ M_ASSERT (d != NULL); \ M_F(name, _node_list_init)(d->list); \ d->default_size = M_USE_DEQUE_DEFAULT_SIZE + src->count; \ d->count = src->count; \ node_t *n = M_C3(m_d3qu3_,name,_new_node)(d); \ if (n == NULL) return; \ d->default_size /= 2; \ M_F(name, _node_list_push_back)(d->list, n); \ d->front->node = n; \ d->front->index = M_USE_DEQUE_DEFAULT_SIZE/2; \ d->back->node = n; \ d->back->index = M_USE_DEQUE_DEFAULT_SIZE/2 + src->count; \ it_t it; \ size_t i = M_USE_DEQUE_DEFAULT_SIZE/2; \ for(M_F(name, _it)(it, src); !M_F(name, _end_p)(it) ; M_F(name, _next)(it)) { \ type const *obj = M_F(name, _cref)(it); \ M_CALL_INIT_SET(oplist, n->data[i], *obj); \ i++; \ M_ASSERT (i <= d->back->index); \ } \ M_D3QU3_CONTRACT(d); \ } \ \ M_INLINE void \ M_F(name, _set)(deque_t d, deque_t const src) \ { \ if (M_UNLIKELY (src == d)) \ return; \ /* TODO: Reuse memory of d! */ \ M_F(name, _clear)(d); \ M_F(name, _init_set)(d, src); \ } \ \ M_INLINE void \ M_F(name, _init_move)(deque_t d, deque_t src) \ { \ M_D3QU3_CONTRACT(src); \ M_ASSERT (d!= NULL); \ M_F(name,_node_list_init_move)(d->list, src->list); \ d->front->node = src->front->node; \ d->front->index = src->front->index; \ d->back->node = src->back->node; \ d->back->index = src->back->index; \ d->default_size = src->default_size; \ d->count = src->count; \ memset(src, 0, sizeof(deque_t)); \ M_D3QU3_CONTRACT(d); \ } \ \ M_INLINE void \ M_F(name, _move)(deque_t d, deque_t src) \ { \ M_D3QU3_CONTRACT(d); \ M_D3QU3_CONTRACT(src); \ M_F(name, _clear)(d); \ M_F(name, _init_move)(d, src); \ M_D3QU3_CONTRACT(d); \ } \ \ M_INLINE void \ M_F(name, _swap)(deque_t d, deque_t e) \ { \ M_D3QU3_CONTRACT(d); \ M_D3QU3_CONTRACT(e); \ M_F(name, _node_list_swap) (d->list, e->list); \ M_SWAP(node_t *, d->front->node, e->front->node); \ M_SWAP(node_t *, d->back->node, e->back->node); \ M_SWAP(size_t, d->front->index, e->front->index); \ M_SWAP(size_t, d->back->index, e->back->index); \ M_SWAP(size_t, d->default_size, e->default_size); \ M_SWAP(size_t, d->count, e->count); \ M_D3QU3_CONTRACT(d); \ M_D3QU3_CONTRACT(e); \ } \ \ M_INLINE type* \ M_F(name, _get)(deque_t d, size_t key) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT_INDEX (key, d->count); \ const size_t index0 = d->front->index; \ size_t count = 0; \ /* This loop is in log(N) since the size increase exponentially.*/ \ for(node_t *n = d->front->node; true ; \ n = (n == d->back->node) ? NULL : \ M_F(name, _node_list_next_obj)(d->list, n) ){ \ M_ASSERT(n != NULL); \ if (index0 + key < count + n->size) { \ return &n->data[index0 + key - count]; \ } \ count += n->size; \ } \ } \ \ M_INLINE type const * \ M_F(name, _cget)(deque_t d, size_t key) \ { \ return M_CONST_CAST(type, M_F(name, _get)(d, key)); \ } \ \ M_INLINE void \ M_F(name, _set_at)(deque_t d, size_t key, type const x) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT_INDEX (key, d->count); \ type *p = M_F(name, _get)(d, key); \ M_CALL_SET(oplist, *p, x); \ M_D3QU3_CONTRACT(d); \ } \ \ M_IF_METHOD(EQUAL, oplist)( \ M_INLINE bool \ M_F(name, _equal_p)(const deque_t d1, const deque_t d2) \ { \ M_D3QU3_CONTRACT(d1); \ M_D3QU3_CONTRACT(d2); \ if (d1->count != d2->count) \ return false; \ it_t it1; \ it_t it2; \ for(M_F(name, _it)(it1, d1), M_F(name,_it)(it2, d2); \ !M_F(name, _end_p)(it1) ; \ M_F(name, _next)(it1), M_F(name, _next)(it2)) { \ type const *obj1 = M_F(name, _cref)(it1); \ type const *obj2 = M_F(name, _cref)(it2); \ if (M_CALL_EQUAL(oplist, *obj1, *obj2) == false) \ return false; \ } \ M_ASSERT (M_F(name, _end_p)(it2)); \ return true; \ } \ , /* NO EQUAL */) \ \ M_IF_METHOD(HASH, oplist)( \ M_INLINE size_t \ M_F(name, _hash)(const deque_t d) \ { \ M_D3QU3_CONTRACT(d); \ M_HASH_DECL(hash); \ it_t it; \ for(M_F(name, _it)(it, d); !M_F(name, _end_p)(it); M_F(name, _next)(it)) { \ type const *obj = M_F(name, _cref)(it); \ M_HASH_UP (hash, M_CALL_HASH(oplist, *obj)); \ } \ return M_HASH_FINAL(hash); \ } \ , /* NO HASH */) \ \ M_IF_METHOD(SWAP, oplist)( \ M_INLINE void \ M_F(name, _swap_at)(deque_t d, size_t i, size_t j) \ { \ M_D3QU3_CONTRACT(d); \ M_ASSERT_INDEX (i, d->count); \ M_ASSERT_INDEX (j, d->count); \ type *obj1 = M_F(name, _get)(d, i); \ type *obj2 = M_F(name, _get)(d, j); \ M_CALL_SWAP(oplist, *obj1, *obj2); \ M_D3QU3_CONTRACT(d); \ } \ , /* NO SWAP */) \ \ M_IF_METHOD(GET_STR, oplist)( \ M_INLINE void \ M_F(name, _get_str)(m_string_t str, deque_t const deque, bool append) \ { \ M_D3QU3_CONTRACT(deque); \ (append ? m_string_cat_cstr : m_string_set_cstr) (str, "["); \ it_t it; \ for (M_F(name, _it)(it, deque) ; \ !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 deque_t deque) \ { \ M_D3QU3_CONTRACT(deque); \ M_ASSERT (file != NULL); \ fputc ('[', file); \ it_t it; \ for (M_F(name, _it)(it, deque) ; \ !M_F(name, _end_p)(it); \ M_F(name, _next)(it)) { \ type const *item = M_F(name, _cref)(it); \ M_CALL_OUT_STR(oplist, file, *item); \ if (!M_F(name, _last_p)(it)) \ 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)(deque_t deque, const char str[], const char **endp) \ { \ M_D3QU3_CONTRACT(deque); \ M_ASSERT (str != NULL); \ M_F(name,_reset)(deque); \ bool success = false; \ int c = *str++; \ if (M_UNLIKELY (c != '[')) goto exit; \ c = *str++; \ if (M_UNLIKELY (c == ']')) {success = true; goto exit;} \ if (M_UNLIKELY (c == 0)) goto exit; \ str--; \ type item; \ M_CALL_INIT(oplist, item); \ do { \ bool b = M_CALL_PARSE_STR(oplist, item, str, &str); \ do { c = *str++; } while (isspace(c)); \ if (b == false || c == 0) { goto exit_clear; } \ M_F(name, _push_back)(deque, item); \ } while (c == M_GET_SEPARATOR oplist); \ M_D3QU3_CONTRACT(deque); \ success = (c == ']'); \ exit_clear: \ M_CALL_CLEAR(oplist, item); \ exit: \ if (endp) *endp = str; \ return success; \ } \ , /* no PARSE_STR */ ) \ \ M_IF_METHOD2(IN_STR, INIT, oplist)( \ M_INLINE bool \ M_F(name, _in_str)(deque_t deque, FILE *file) \ { \ M_D3QU3_CONTRACT(deque); \ M_ASSERT (file != NULL); \ M_F(name,_reset)(deque); \ int c = fgetc(file); \ if (M_UNLIKELY (c != '[')) return false; \ c = fgetc(file); \ if (M_UNLIKELY (c == ']')) return true; \ if (M_UNLIKELY (c == EOF)) return false; \ ungetc(c, file); \ type item; \ M_CALL_INIT(oplist, item); \ do { \ bool b = M_CALL_IN_STR(oplist, item, file); \ do { c = fgetc(file); } while (isspace(c)); \ if (b == false || c == EOF) { break; } \ M_F(name, _push_back)(deque, item); \ } while (c == M_GET_SEPARATOR oplist); \ M_CALL_CLEAR(oplist, item); \ M_D3QU3_CONTRACT(deque); \ return c == ']'; \ } \ , /* no IN_STR */ ) \ \ M_IF_METHOD(OUT_SERIAL, oplist)( \ M_INLINE m_serial_return_code_t \ M_F(name, _out_serial)(m_serial_write_t f, const deque_t deque) \ { \ M_D3QU3_CONTRACT(deque); \ M_ASSERT (f != NULL && f->m_interface != NULL); \ m_serial_local_t local; \ m_serial_return_code_t ret; \ bool first_done = false; \ ret = f->m_interface->write_array_start(local, f, deque->count); \ M_F(name, _it_ct) it; \ for (M_F(name, _it)(it, deque) ; \ !M_F(name, _end_p)(it); \ M_F(name, _next)(it)){ \ type const *item = M_F(name, _cref)(it); \ if (first_done) \ ret |= f->m_interface->write_array_next(local, f); \ ret |= M_CALL_OUT_SERIAL(oplist, f, *item); \ first_done = true; \ } \ ret |= f->m_interface->write_array_end(local, f); \ return ret & M_SERIAL_FAIL; \ } \ , /* no OUT_SERIAL */ ) \ \ M_IF_METHOD2(IN_SERIAL, INIT, oplist)( \ M_INLINE m_serial_return_code_t \ M_F(name, _in_serial)(deque_t deque, m_serial_read_t f) \ { \ M_D3QU3_CONTRACT(deque); \ M_ASSERT (f != NULL && f->m_interface != NULL); \ m_serial_local_t local; \ m_serial_return_code_t ret; \ size_t estimated_size = 0; \ M_F(name,_reset)(deque); \ ret = f->m_interface->read_array_start(local, f, &estimated_size); \ if (M_UNLIKELY (ret != M_SERIAL_OK_CONTINUE)) return ret; \ type item; \ M_CALL_INIT(oplist, item); \ do { \ ret = M_CALL_IN_SERIAL(oplist, item, f); \ if (ret != M_SERIAL_OK_DONE) { break; } \ M_F(name, _push_back)(deque, item); \ ret = f->m_interface->read_array_next(local, f); \ } while (ret == M_SERIAL_OK_CONTINUE); \ M_CALL_CLEAR(oplist, item); \ return ret; \ } \ , /* no IN_SERIAL & INIT */ ) \ /* Definition of the emplace_back function for deque */ #define M_D3QUE_EMPLACE_BACK_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_back_raw)(v); \ if (M_UNLIKELY (data == NULL) ) \ return; \ M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ } /* Definition of the emplace_front function for deque */ #define M_D3QUE_EMPLACE_FRONT_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_front_raw)(v); \ if (M_UNLIKELY (data == NULL) ) \ return; \ M_EMPLACE_CALL_FUNC(a, init_func, oplist, *data, exp_emplace_type); \ } /********************************* INTERNAL **********************************/ /* Deferred evaluation for the oplist definition, so that all arguments are evaluated before further expansion */ #define M_D3QU3_OPLIST_P1(arg) M_D3QU3_OPLIST_P2 arg /* Validation of the given oplist */ #define M_D3QU3_OPLIST_P2(name, oplist) \ M_IF_OPLIST(oplist)(M_D3QU3_OPLIST_P3, M_D3QU3_OPLIST_FAILURE)(name, oplist) /* Prepare a clean compilation failure */ #define M_D3QU3_OPLIST_FAILURE(name, oplist) \ ((M_LIB_ERROR(ARGUMENT_OF_DEQUE_OPLIST_IS_NOT_AN_OPLIST, name, oplist))) /* OPLIST definition of a deque */ #define M_D3QU3_OPLIST_P3(name, oplist) \ (INIT(M_F(name, _init)) \ ,INIT_SET(M_F(name, _init_set)) \ ,INIT_WITH(API_1(M_INIT_WITH_VAI)) \ ,SET(M_F(name, _set)) \ ,CLEAR(M_F(name, _clear)) \ ,INIT_MOVE(M_F(name, _init_move)) \ ,MOVE(M_F(name, _move)) \ ,SWAP(M_F(name, _swap)) \ ,NAME(name) \ ,TYPE(M_F(name,_ct)) \ ,SUBTYPE(M_F(name, _subtype_ct)) \ ,EMPTY_P(M_F(name,_empty_p)) \ ,IT_TYPE(M_F(name,_it_ct)) \ ,IT_FIRST(M_F(name,_it)) \ ,IT_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)) \ ,IT_REMOVE(M_F(name,_remove)) \ ,RESET(M_F(name,_reset)) \ ,GET_SIZE(M_F(name, _size)) \ ,PUSH(M_F(name,_push_back)) \ ,POP(M_F(name,_pop_back)) \ ,OPLIST(oplist) \ ,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 **********************************/ #if M_USE_SMALL_NAME #define DEQUE_DEF M_DEQUE_DEF #define DEQUE_DEF_AS M_DEQUE_DEF_AS #define DEQUE_OPLIST M_DEQUE_OPLIST #endif #endif