diff --git a/m-list.h b/m-list.h index d084a683..03831a80 100644 --- a/m-list.h +++ b/m-list.h @@ -27,13 +27,20 @@ #include "m-core.h" -/* Define a singly list of a given type. +/* Define a singly linked list of a given type. USAGE: LIST_DEF(name, type [, oplist_of_the_type]) */ #define LIST_DEF(name, ...) \ LISTI_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \ ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__), M_C(name,_t), M_C(name, _it_t) ), \ (name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) ))) +/* Define a dual-push singly linked list of a given type. + USAGE: LIST_DEF(name, type [, oplist_of_the_type]) */ +#define LIST_DUAL_PUSH_DEF(name, ...) \ + LISTI_DUAL_PUSH_DEF(M_IF_NARGS_EQ1(__VA_ARGS__) \ + ((name, __VA_ARGS__, M_GLOBAL_OPLIST_OR_DEF(__VA_ARGS__), M_C(name,_t), M_C(name, _it_t) ), \ + (name, __VA_ARGS__, M_C(name,_t), M_C(name, _it_t) ))) + /* Define the oplist of a list of type. USAGE: LIST_OPLIST(name [, oplist_of_the_type]) */ #define LIST_OPLIST(...) \ @@ -228,7 +235,7 @@ static inline void \ M_C(name, _it)(list_it_t l, const list_t v) \ { \ - assert (v != NULL && l[0].l != NULL); \ + assert (v != NULL && l != NULL); \ l[0].l[0] = *v; \ l[0].previous = NULL; \ } \ @@ -589,5 +596,460 @@ } \ , /* no hash */ ) \ +/* Dual Push singly linked list. + Support Push Back / Push Front / Pop back in O(1). + Doesn't support Pop front. + This is done by keeping a pointer to both back & front + */ +#define LISTI_DUAL_PUSH_DEF(arg) LISTI_DUAL_PUSH_DEF2 arg + +#define LISTI_DUAL_PUSH_DEF2(name, type, oplist, list_t, list_it_t) \ + \ + struct M_C(name, _s) { \ + struct M_C(name, _s) *next; \ + type data; \ + }; \ + \ + typedef struct { \ + struct M_C(name, _s) *front; \ + struct M_C(name, _s) *back; \ + } list_t[1]; \ + \ + typedef type M_C(name, _type_t); \ + \ + typedef struct M_C(name, _it_s) { \ + struct M_C(name, _s) *previous; \ + struct M_C(name, _s) *current; \ + } list_it_t[1]; \ + \ + LISTI_MEMPOOL_DEF(name, type, oplist, list_t, list_it_t) \ + LISTI_DUAL_PUSH_DEF3(name, type, oplist, list_t, list_it_t) \ + LISTI_ITBASE_DEF(name, type, oplist, list_t, list_it_t) + +#define LISTI_DUAL_PUSH_CONTRACT(l) do { \ + assert (l != NULL); \ + assert ( (l->back == NULL && l->front == NULL) \ + || (l->back != NULL && l->front != NULL)); \ + } while (0) + +#define LISTI_DUAL_PUSH_DEF3(name, type, oplist, list_t, list_it_t) \ + \ + static inline void \ + M_C(name, _init)(list_t v) \ + { \ + assert( v != NULL); \ + v->front = NULL; \ + v->back = NULL; \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + } \ + \ + static inline void \ + M_C(name, _clean)(list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + struct M_C(name, _s) *it = v->back; \ + while (it != NULL) { \ + struct M_C(name, _s) *next = it->next; \ + M_GET_CLEAR oplist(it->data); \ + M_C(name,_int_del)(it); \ + it = next; \ + } \ + v->front = NULL; \ + v->back = NULL; \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + } \ + \ + static inline void \ + M_C(name, _clear)(list_t v) \ + { \ + M_C(name, _clean)(v); \ + } \ + \ + static inline const type * \ + M_C(name, _back)(const list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + assert (v->back != NULL); \ + return M_CONST_CAST(type, &(v->back->data)); \ + } \ + \ + static inline type * \ + M_C(name, _push_back_raw)(list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + struct M_C(name, _s) *next = M_C(name, _int_new)(); \ + if (M_UNLIKELY (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_C(name, _s))); \ + return NULL; \ + } \ + type *ret = &next->data; \ + next->next = v->back; \ + v->back = next; \ + /* Update front too if the list was empty */ \ + /* This C code shall generate branchless code */ \ + struct M_C(name, _s) *front = v->front; \ + front = (front == NULL) ? next : front; \ + v->front = front; \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + return ret; \ + } \ + \ + static inline void \ + M_C(name, _push_back)(list_t v, type const x) \ + { \ + type *data = M_C(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return; \ + M_GET_INIT_SET oplist(*data, x); \ + } \ + \ + static inline type * \ + M_C(name, _push_back_new)(list_t v) \ + { \ + type *data = M_C(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return NULL; \ + M_GET_INIT oplist(*data); \ + return data; \ + } \ + \ + static inline void \ + M_C(name, _pop_back)(type *data, list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + assert (v->back != NULL); \ + struct M_C(name, _s) *tofree = v->back; \ + if (data != NULL) { \ + M_GET_SET oplist(*data, tofree->data); \ + } \ + M_GET_CLEAR oplist(tofree->data); \ + v->back = tofree->next; \ + M_C(name,_int_del)(tofree); \ + /* Update front too if the list became empty */ \ + /* This C code shall generate branchless code */ \ + struct M_C(name, _s) *front = v->front; \ + front = (front == tofree) ? NULL : front; \ + v->front = front; \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + } \ + \ + static inline const type * \ + M_C(name, _front)(list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + assert (v->front != NULL); \ + return M_CONST_CAST(type, &(v->front->data)); \ + } \ + \ + static inline type * \ + M_C(name, _push_front_raw)(list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + struct M_C(name, _s) *next = M_C(name, _int_new)(); \ + if (M_UNLIKELY (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_C(name, _s))); \ + return NULL; \ + } \ + type *ret = &next->data; \ + next->next = NULL; \ + if (M_LIKELY(v->front != NULL)) { \ + v->front->next = next; \ + } else { \ + /* Update back too as the list was empty */ \ + v->back = next; \ + } \ + v->front = next; \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + return ret; \ + } \ + \ + static inline void \ + M_C(name, _push_front)(list_t v, type const x) \ + { \ + type *data = M_C(name, _push_front_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return; \ + M_GET_INIT_SET oplist(*data, x); \ + } \ + \ + static inline type * \ + M_C(name, _push_front_new)(list_t v) \ + { \ + type *data = M_C(name, _push_back_raw)(v); \ + if (M_UNLIKELY (data == NULL)) \ + return NULL; \ + M_GET_INIT oplist(*data); \ + return data; \ + } \ + \ + static inline bool \ + M_C(name, _empty_p)(const list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + return v->back == NULL; \ + } \ + \ + static inline void \ + M_C(name, _swap)(list_t l, list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(l); \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + M_SWAP(struct M_C(name, _s) *, l->front, v->front); \ + M_SWAP(struct M_C(name, _s) *, l->back, v->back); \ + } \ + \ + static inline void \ + M_C(name, _it)(list_it_t it, const list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + assert (it != NULL); \ + it->current = v->back; \ + it->previous = NULL; \ + } \ + \ + static inline void \ + M_C(name, _it_set)(list_it_t it1, const list_it_t it2) \ + { \ + assert (it1 != NULL && it2 != NULL); \ + it1->current = it2->current; \ + it1->previous = it2->previous; \ + } \ + \ + static inline void \ + M_C(name, _it_end)(list_it_t it1, const list_t v) \ + { \ + assert (it1 != NULL); \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + (void)v; /* unused */ \ + it1->current = NULL; \ + it1->previous = NULL; \ + } \ + \ + static inline bool \ + M_C(name, _end_p)(const list_it_t it) \ + { \ + assert (it != NULL); \ + return it->current == NULL; \ + } \ + \ + static inline bool \ + M_C(name, _last_p)(const list_it_t it) \ + { \ + assert (it != NULL); \ + return it->current == NULL || it->current->next == NULL; \ + } \ + \ + static inline void \ + M_C(name, _next)(list_it_t it) \ + { \ + assert(it != NULL && it->current->next != NULL); \ + it->previous = it->current; \ + it->current = it->current->next; \ + } \ + \ + static inline bool \ + M_C(name, _it_equal_p)(const list_it_t it1, const list_it_t it2) \ + { \ + assert(it1 != NULL && it2 != NULL); \ + return it1->current == it2->current; \ + } \ + \ + static inline type * \ + M_C(name, _ref)(const list_it_t it) \ + { \ + assert(it != NULL && it->current != NULL); \ + return &(it->current->data); \ + } \ + \ + static inline const type * \ + M_C(name, _cref)(const list_it_t it) \ + { \ + assert(it != NULL && it->current != NULL); \ + return M_CONST_CAST(type, &(it->current->data)); \ + } \ + \ + static inline size_t \ + M_C(name, _size)(const list_t v) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(v); \ + size_t size = 0; \ + struct M_C(name, _s) *it = v->back; \ + while (it != NULL) { \ + size ++; \ + it = it->next; \ + } \ + return size; \ + } \ + \ + static inline void \ + M_C(name, _insert)(list_t list, list_it_t insertion_point, \ + type const x) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(list); \ + assert (insertion_point != NULL); \ + struct M_C(name, _s) *next = M_C(name, _int_new)(); \ + if (M_UNLIKELY (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_C(name, _s))); \ + return; \ + } \ + M_GET_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_C(name, _s) *front = list->front; \ + front = (front == NULL) ? next : front; \ + list->front = front; \ + } else { \ + next->next = insertion_point->current->next; \ + insertion_point->current->next = next; \ + /* update front if current == front */ \ + struct M_C(name, _s) *front = list->front; \ + front = (front == insertion_point->current) ? next : front; \ + list->front = front; \ + } \ + } \ + \ + static inline void \ + M_C(name, _remove)(list_t list, list_it_t removing_point) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(list); \ + assert (removing_point != NULL); \ + assert(removing_point->current != NULL); \ + struct M_C(name, _s) *next = removing_point->current->next; \ + struct M_C(name, _s) *previous = removing_point->previous; \ + if (M_UNLIKELY (previous == NULL)) { \ + list->back = next; \ + } else { \ + previous->next = next; \ + } \ + /* Update front */ \ + struct M_C(name, _s) *front = list->front; \ + front = (next == NULL) ? previous : front; \ + list->front = front; \ + /* Remove node */ \ + M_GET_CLEAR oplist(removing_point->current->data); \ + M_C(name,_int_del) (removing_point->current); \ + removing_point->current = next; \ + } \ + \ + static inline void \ + M_C(name, _set)(list_t list, const list_t org) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(list); \ + LISTI_DUAL_PUSH_CONTRACT(org); \ + struct M_C(name, _s) *next = NULL; \ + struct M_C(name, _s) *it_org; \ + struct M_C(name, _s) **update_list; \ + if (M_UNLIKELY (list == org)) return; \ + M_C(name, _clean)(list); \ + update_list = &list->back; \ + it_org = org->back; \ + while (it_org != NULL) { \ + next = M_C(name, _int_new)(); \ + *update_list = next; \ + if (M_UNLIKELY (next == NULL)) { \ + M_MEMORY_FULL(sizeof (struct M_C(name, _s))); \ + return; \ + } \ + update_list = &next->next; \ + M_GET_INIT_SET oplist(next->data, it_org->data); \ + it_org = it_org->next; \ + } \ + list->front = next; \ + *update_list = NULL; \ + } \ + \ + static inline void \ + M_C(name, _init_set)(list_t list, const list_t org) \ + { \ + assert (list != org); \ + M_C(name, _init)(list); \ + M_C(name, _set)(list, org); \ + } \ + \ + static inline void \ + M_C(name, _init_move)(list_t list, list_t org) \ + { \ + assert (list != org); \ + list->back = org->back; \ + list->front = org->front; \ + org->back = NULL; \ + org->front = NULL; \ + } \ + \ + static inline void \ + M_C(name, _move)(list_t list, list_t org) \ + { \ + M_C(name, _clear)(list); \ + M_C(name, _init_move)(list, org); \ + } \ + \ + static inline void \ + M_C(name, _splice_back)(list_t list1, list_t list2, list_it_t it) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(list1); \ + LISTI_DUAL_PUSH_CONTRACT(list2); \ + assert (list1 != list2); \ + assert(it->current != NULL); \ + /* First remove 'it' from list2 */ \ + struct M_C(name, _s) *current = it->current; \ + struct M_C(name, _s) *next = current->next; \ + if (it->previous == NULL) { \ + list1->back = next; \ + } else { \ + it->previous->next = next; \ + } \ + /* Update front */ \ + struct M_C(name, _s) *front = list2->front; \ + front = (next == NULL) ? it->previous : front; \ + list2->front = front; \ + /* Update it to next element */ \ + it->current = next; \ + /* Move current in nv */ \ + current->next = list1->back; \ + list1->back = current; \ + /* Update front too if the list was empty */ \ + /* This C code shall generate branchless code */ \ + front = list1->front; \ + front = (front == NULL) ? current : front; \ + list1->front = front; \ + } \ + \ + static inline void \ + M_C(name, _splice)(list_t list1, list_t list2) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(list1); \ + LISTI_DUAL_PUSH_CONTRACT(list2); \ + assert (list1 != list2); \ + if (M_LIKELY (list1->front != NULL)) { \ + list1->front->next = list2->back; \ + list1->front = list2->front; \ + } else { \ + /* list1 is empty */ \ + list1->back = list2->back; \ + list1->front = list2->front; \ + } \ + list2->back = NULL; \ + list2->front = NULL; \ + LISTI_DUAL_PUSH_CONTRACT(list1); \ + LISTI_DUAL_PUSH_CONTRACT(list2); \ + } \ + \ + static inline void \ + M_C(name, _reverse)(list_t list) \ + { \ + LISTI_DUAL_PUSH_CONTRACT(list); \ + list->front = list->back; \ + struct M_C(name, _s) *previous = NULL, *it = list->back, *next; \ + while (it != NULL) { \ + next = it->next; \ + it->next = previous; \ + previous = it; \ + it = next; \ + } \ + list->back = previous; \ + LISTI_DUAL_PUSH_CONTRACT(list); \ + } \ #endif diff --git a/tests/test-mlist.c b/tests/test-mlist.c index f3e678d0..9991d6e2 100644 --- a/tests/test-mlist.c +++ b/tests/test-mlist.c @@ -29,6 +29,7 @@ START_COVERAGE LIST_DEF(list_uint, unsigned int) LIST_DEF(list_mpz, mpz_t, MYMPZ_OPLIST) +LIST_DUAL_PUSH_DEF(list2_double, double) END_COVERAGE #define LIST_UINT_OPLIST LIST_OPLIST(list_uint)