#include "cx/linked_list.h"
#include "cx/compare.h"
#include <string.h>
#include <assert.h>
#if __STDC_VERSION__ <
202311L
#ifndef __alignof_is_defined
#define alignof _Alignof
#define __alignof_is_defined
1
#endif
#endif
#define CX_LL_PTR(cur, off) (*(
void**)(((
char*)(cur))+(off)))
#define ll_prev(node)
CX_LL_PTR(node, loc_prev)
#define ll_next(node)
CX_LL_PTR(node, loc_next)
#define ll_advance(node)
CX_LL_PTR(node, loc_advance)
#define ll_data(node) (((
char*)(node))+loc_data)
void *cx_linked_list_at(
const void *start,
size_t start_index,
ptrdiff_t loc_advance,
size_t index
) {
assert(start !=
NULL);
assert(loc_advance >=
0);
size_t i = start_index;
const void *cur = start;
while (i != index && cur !=
NULL) {
cur = ll_advance(cur);
i < index ? i++ : i--;
}
return (
void *) cur;
}
void *cx_linked_list_find(
const void *start,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
cx_compare_func cmp_func,
const void *elem,
size_t *found_index
) {
assert(start !=
NULL);
assert(loc_advance >=
0);
assert(loc_data >=
0);
assert(cmp_func);
void *node = (
void*) start;
size_t index =
0;
do {
void *current = ll_data(node);
if (cmp_func(current, elem) ==
0) {
if (found_index !=
NULL) {
*found_index = index;
}
return node;
}
node = ll_advance(node);
index++;
}
while (node !=
NULL);
return NULL;
}
void *cx_linked_list_first(
const void *node,
ptrdiff_t loc_prev
) {
return cx_linked_list_last(node, loc_prev);
}
void *cx_linked_list_last(
const void *node,
ptrdiff_t loc_next
) {
assert(node !=
NULL);
assert(loc_next >=
0);
const void *cur = node;
const void *last;
do {
last = cur;
}
while ((cur = ll_next(cur)) !=
NULL);
return (
void *) last;
}
void *cx_linked_list_prev(
const void *begin,
ptrdiff_t loc_next,
const void *node
) {
assert(begin !=
NULL);
assert(node !=
NULL);
assert(loc_next >=
0);
if (begin == node)
return NULL;
const void *cur = begin;
const void *next;
while (
1) {
next = ll_next(cur);
if (next == node)
return (
void *) cur;
cur = next;
}
}
void cx_linked_list_link(
void *left,
void *right,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
assert(loc_next >=
0);
ll_next(left) = right;
if (loc_prev >=
0) {
ll_prev(right) = left;
}
}
void cx_linked_list_unlink(
void *left,
void *right,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
assert (loc_next >=
0);
assert(ll_next(left) == right);
ll_next(left) =
NULL;
if (loc_prev >=
0) {
assert(ll_prev(right) == left);
ll_prev(right) =
NULL;
}
}
void cx_linked_list_add(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *new_node
) {
void *last;
if (end ==
NULL) {
assert(begin !=
NULL);
last = *begin ==
NULL ?
NULL : cx_linked_list_last(*begin, loc_next);
}
else {
last = *end;
}
cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, last, new_node, new_node);
}
void cx_linked_list_prepend(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *new_node
) {
cx_linked_list_insert_chain(begin, end, loc_prev, loc_next,
NULL, new_node, new_node);
}
void cx_linked_list_insert(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *node,
void *new_node
) {
cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, node, new_node, new_node);
}
void cx_linked_list_insert_chain(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *node,
void *insert_begin,
void *insert_end
) {
if (insert_end ==
NULL) {
insert_end = cx_linked_list_last(insert_begin, loc_next);
}
void *successor;
if (node ==
NULL) {
assert(begin !=
NULL || (end !=
NULL && loc_prev >=
0));
if (begin !=
NULL) {
successor = *begin;
*begin = insert_begin;
}
else {
successor = *end ==
NULL ?
NULL : cx_linked_list_first(*end, loc_prev);
}
}
else {
successor = ll_next(node);
cx_linked_list_link(node, insert_begin, loc_prev, loc_next);
}
if (successor ==
NULL) {
if (end !=
NULL) {
*end = insert_end;
}
}
else {
cx_linked_list_link(insert_end, successor, loc_prev, loc_next);
}
}
void cx_linked_list_insert_sorted(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *new_node,
cx_compare_func cmp_func
) {
assert(ll_next(new_node) ==
NULL);
cx_linked_list_insert_sorted_chain(
begin, end, loc_prev, loc_next, new_node, cmp_func);
}
static void *cx_linked_list_insert_sorted_chain_impl(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *insert_begin,
cx_compare_func cmp_func,
bool allow_duplicates
) {
assert(begin !=
NULL);
assert(loc_next >=
0);
assert(insert_begin !=
NULL);
void *source_original = *begin;
void *source_argument = insert_begin;
void *new_begin =
NULL;
void *new_end =
NULL;
void *dup_begin =
NULL;
void *dup_end =
NULL;
{
int d = source_original ==
NULL ?
1 : cmp_func(source_original, source_argument);
if (d <=
0) {
new_begin = new_end = source_original;
source_original = ll_next(source_original);
if (d ==
0) {
if (allow_duplicates) {
cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
new_end = source_argument;
}
else {
dup_begin = dup_end = source_argument;
}
source_argument = ll_next(source_argument);
}
}
else {
new_begin = new_end = source_argument;
source_argument = ll_next(source_argument);
}
}
while (source_original !=
NULL && source_argument !=
NULL) {
int d = cmp_func(source_original, source_argument);
if (d <=
0) {
cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
new_end = source_original;
source_original = ll_next(source_original);
if (d ==
0) {
if (allow_duplicates) {
cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
new_end = source_argument;
}
else {
if (dup_end ==
NULL) {
dup_begin = dup_end = source_argument;
}
else {
cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
dup_end = source_argument;
}
}
source_argument = ll_next(source_argument);
}
}
else {
if (!allow_duplicates && cmp_func(new_end, source_argument) ==
0) {
if (dup_end ==
NULL) {
dup_begin = dup_end = source_argument;
}
else {
cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
dup_end = source_argument;
}
}
else {
cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
new_end = source_argument;
}
source_argument = ll_next(source_argument);
}
}
if (source_original !=
NULL) {
cx_linked_list_link(new_end, source_original, loc_prev, loc_next);
new_end = cx_linked_list_last(source_original, loc_next);
}
else if (source_argument !=
NULL) {
if (allow_duplicates) {
cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
new_end = cx_linked_list_last(source_argument, loc_next);
}
else {
while (source_argument !=
NULL) {
if (cmp_func(new_end, source_argument) ==
0) {
if (dup_end ==
NULL) {
dup_begin = dup_end = source_argument;
}
else {
cx_linked_list_link(dup_end, source_argument, loc_prev, loc_next);
dup_end = source_argument;
}
}
else {
cx_linked_list_link(new_end, source_argument, loc_prev, loc_next);
new_end = source_argument;
}
source_argument = ll_next(source_argument);
}
}
}
ll_next(new_end) =
NULL;
if (dup_end !=
NULL) {
ll_next(dup_end) =
NULL;
}
if (loc_prev >=
0) {
ll_prev(new_begin) =
NULL;
if (dup_begin !=
NULL) {
ll_prev(dup_begin) =
NULL;
}
}
*begin = new_begin;
if (end !=
NULL) {
*end = new_end;
}
return dup_begin;
}
void cx_linked_list_insert_sorted_chain(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *insert_begin,
cx_compare_func cmp_func
) {
cx_linked_list_insert_sorted_chain_impl(
begin, end, loc_prev, loc_next,
insert_begin, cmp_func, true);
}
int cx_linked_list_insert_unique(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *new_node,
cx_compare_func cmp_func
) {
assert(ll_next(new_node) ==
NULL);
return NULL != cx_linked_list_insert_unique_chain(
begin, end, loc_prev, loc_next, new_node, cmp_func);
}
void *cx_linked_list_insert_unique_chain(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *insert_begin,
cx_compare_func cmp_func
) {
return cx_linked_list_insert_sorted_chain_impl(
begin, end, loc_prev, loc_next,
insert_begin, cmp_func, false);
}
size_t cx_linked_list_remove_chain(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *node,
size_t num
) {
assert(node !=
NULL);
assert(loc_next >=
0);
assert(loc_prev >=
0 || begin !=
NULL);
if (num ==
0)
return 0;
void *prev;
if (loc_prev >=
0) {
prev = ll_prev(node);
}
else {
prev = cx_linked_list_prev(*begin, loc_next, node);
}
void *next = ll_next(node);
size_t removed =
1;
for (; removed < num && next !=
NULL ; removed++) {
next = ll_next(next);
}
if (prev ==
NULL) {
if (begin !=
NULL) {
*begin = next;
}
}
else {
ll_next(prev) = next;
}
if (next ==
NULL) {
if (end !=
NULL) {
*end = prev;
}
}
else if (loc_prev >=
0) {
ll_prev(next) = prev;
}
return removed;
}
void cx_linked_list_remove(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
void *node
) {
cx_linked_list_remove_chain(begin, end, loc_prev, loc_next, node,
1);
}
size_t cx_linked_list_size(
const void *node,
ptrdiff_t loc_next
) {
assert(loc_next >=
0);
size_t size =
0;
while (node !=
NULL) {
node = ll_next(node);
size++;
}
return size;
}
#ifndef CX_LINKED_LIST_SORT_SBO_SIZE
#define CX_LINKED_LIST_SORT_SBO_SIZE 1024
#endif
static void cx_linked_list_sort_merge(
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
size_t length,
void *ls,
void *le,
void *re,
cx_compare_func cmp_func,
void **begin,
void **end
) {
void *sbo[
CX_LINKED_LIST_SORT_SBO_SIZE];
void **sorted = length >=
CX_LINKED_LIST_SORT_SBO_SIZE ?
cxMallocDefault(
sizeof(
void *) * length) : sbo;
if (sorted ==
NULL) abort();
void *rc, *lc;
lc = ls;
rc = le;
size_t n =
0;
while (lc && lc != le && rc != re) {
if (cmp_func(ll_data(lc), ll_data(rc)) <=
0) {
sorted[n] = lc;
lc = ll_next(lc);
}
else {
sorted[n] = rc;
rc = ll_next(rc);
}
n++;
}
while (lc && lc != le) {
sorted[n] = lc;
lc = ll_next(lc);
n++;
}
while (rc && rc != re) {
sorted[n] = rc;
rc = ll_next(rc);
n++;
}
if (loc_prev >=
0) ll_prev(sorted[
0]) =
NULL;
for (
size_t i =
0 ; i < length -
1; i++) {
cx_linked_list_link(sorted[i], sorted[i +
1], loc_prev, loc_next);
}
ll_next(sorted[length -
1]) =
NULL;
*begin = sorted[
0];
*end = sorted[length -
1];
if (sorted != sbo) {
cxFreeDefault(sorted);
}
}
void cx_linked_list_sort(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next,
ptrdiff_t loc_data,
cx_compare_func cmp_func
) {
assert(begin !=
NULL);
assert(loc_next >=
0);
assert(loc_data >=
0);
assert(cmp_func);
void *lc, *ls, *le, *re;
ls = *begin;
if (ls ==
NULL)
return;
lc = ls;
size_t ln =
1;
while (ll_next(lc) !=
NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) >
0) {
lc = ll_next(lc);
ln++;
}
le = ll_next(lc);
if (le !=
NULL) {
void *rc;
size_t rn =
1;
rc = le;
while (ll_next(rc) !=
NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) >
0) {
rc = ll_next(rc);
rn++;
}
re = ll_next(rc);
void *sorted_begin, *sorted_end;
cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
ln + rn, ls, le, re, cmp_func,
&sorted_begin, &sorted_end);
size_t remainder_length = cx_linked_list_size(re, loc_next);
if (remainder_length >
0) {
void *remainder = re;
cx_linked_list_sort(&remainder,
NULL, loc_prev, loc_next, loc_data, cmp_func);
cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
ln + rn + remainder_length,
sorted_begin, remainder,
NULL, cmp_func,
&sorted_begin, &sorted_end);
}
*begin = sorted_begin;
if (end) *end = sorted_end;
}
}
int cx_linked_list_compare(
const void *begin_left,
const void *begin_right,
ptrdiff_t loc_advance,
ptrdiff_t loc_data,
cx_compare_func cmp_func
) {
const void *left = begin_left, *right = begin_right;
while (left !=
NULL && right !=
NULL) {
const void *left_data = ll_data(left);
const void *right_data = ll_data(right);
int result = cmp_func(left_data, right_data);
if (result !=
0)
return result;
left = ll_advance(left);
right = ll_advance(right);
}
if (left !=
NULL) {
return 1; }
else if (right !=
NULL) {
return -1; }
else {
return 0; }
}
void cx_linked_list_reverse(
void **begin,
void **end,
ptrdiff_t loc_prev,
ptrdiff_t loc_next
) {
assert(begin !=
NULL);
assert(loc_next >=
0);
void *prev =
NULL;
void *cur = *begin;
while (cur !=
NULL) {
void *next = ll_next(cur);
ll_next(cur) = prev;
if (loc_prev >=
0) {
ll_prev(cur) = next;
}
prev = cur;
cur = next;
}
if (end !=
NULL) {
*end = *begin;
}
*begin = prev;
}
static void *cx_ll_node_at(
const cx_linked_list *list,
size_t index
) {
if (index >= list->base.collection.size) {
return NULL;
}
else if (index > list->base.collection.size /
2) {
return cx_linked_list_at(list->end, list->base.collection.size -
1, list->loc_prev, index);
}
else {
return cx_linked_list_at(list->begin,
0, list->loc_next, index);
}
}
static void *cx_ll_malloc_node(
const cx_linked_list *list) {
size_t n;
if (list->extra_data_len ==
0) {
n = list->loc_data + list->base.collection.elem_size;
}
else {
n = list->loc_extra + list->extra_data_len;
}
return cxZalloc(list->base.collection.allocator, n);
}
static int cx_ll_insert_at(
struct cx_list_s *list,
void *node,
const void *elem
) {
cx_linked_list *ll = (cx_linked_list *) list;
void *new_node = cx_ll_malloc_node(ll);
if (new_node ==
NULL)
return 1;
if (elem !=
NULL) {
memcpy((
char*)new_node + ll->loc_data, elem, list->collection.elem_size);
}
cx_linked_list_insert_chain(
&ll->begin, &ll->end,
ll->loc_prev, ll->loc_next,
node, new_node, new_node
);
list->collection.size++;
return 0;
}
static size_t cx_ll_insert_array(
struct cx_list_s *list,
size_t index,
const void *array,
size_t n
) {
if (index > list->collection.size || n ==
0)
return 0;
void *node = index ==
0 ?
NULL : cx_ll_node_at((cx_linked_list *) list, index -
1);
if (
0 != cx_ll_insert_at(list, node, array))
return 1;
if (n ==
1)
return 1;
cx_linked_list *ll = (cx_linked_list *) list;
node = node ==
NULL ? ((cx_linked_list *) list)->begin :
CX_LL_PTR(node, ll->loc_next);
const char *source = array;
for (
size_t i =
1; i < n; i++) {
if (source !=
NULL) {
source += list->collection.elem_size;
}
if (
0 != cx_ll_insert_at(list, node, source))
return i;
node =
CX_LL_PTR(node, ll->loc_next);
}
return n;
}
static void *cx_ll_insert_element(
struct cx_list_s *list,
size_t index,
const void *element
) {
if (index > list->collection.size)
return NULL;
void *node = index ==
0 ?
NULL : cx_ll_node_at((cx_linked_list *) list, index -
1);
if (cx_ll_insert_at(list, node, element))
return NULL;
cx_linked_list *ll = (cx_linked_list *) list;
if (node ==
NULL) {
return (
char*)(ll->begin) + ll->loc_data;
}
else {
char *next =
CX_LL_PTR(node, ll->loc_next);
return next + ll->loc_data;
}
}
static _Thread_local cx_compare_func cx_ll_insert_sorted_cmp_func;
static _Thread_local
off_t cx_ll_insert_sorted_loc_data;
static int cx_ll_insert_sorted_cmp_helper(
const void *l,
const void *r) {
const char *left = (
const char*)l + cx_ll_insert_sorted_loc_data;
const char *right = (
const char*)r + cx_ll_insert_sorted_loc_data;
return cx_ll_insert_sorted_cmp_func(left, right);
}
static size_t cx_ll_insert_sorted_impl(
struct cx_list_s *list,
const void *array,
size_t n,
bool allow_duplicates
) {
cx_linked_list *ll = (cx_linked_list *) list;
if (n ==
0)
return 0;
void *chain = cx_ll_malloc_node(ll);
if (chain ==
NULL)
return 0;
memcpy((
char*)chain + ll->loc_data, array, list->collection.elem_size);
void *prev = chain;
const char *src = array;
size_t inserted =
1;
for (; inserted < n; inserted++) {
void *next = cx_ll_malloc_node(ll);
if (next ==
NULL)
break;
src += list->collection.elem_size;
memcpy((
char*)next + ll->loc_data, src, list->collection.elem_size);
CX_LL_PTR(prev, ll->loc_next) = next;
CX_LL_PTR(next, ll->loc_prev) = prev;
prev = next;
}
CX_LL_PTR(prev, ll->loc_next) =
NULL;
cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc;
cx_ll_insert_sorted_loc_data = ll->loc_data;
if (allow_duplicates) {
cx_linked_list_insert_sorted_chain(
&ll->begin,
&ll->end,
ll->loc_prev,
ll->loc_next,
chain,
cx_ll_insert_sorted_cmp_helper
);
list->collection.size += inserted;
}
else {
void *duplicates = cx_linked_list_insert_unique_chain(
&ll->begin,
&ll->end,
ll->loc_prev,
ll->loc_next,
chain,
cx_ll_insert_sorted_cmp_helper
);
list->collection.size += inserted;
while (duplicates !=
NULL) {
void *next =
CX_LL_PTR(duplicates, ll->loc_next);
cxFree(list->collection.allocator, duplicates);
duplicates = next;
list->collection.size--;
}
}
return inserted;
}
static size_t cx_ll_insert_sorted(
struct cx_list_s *list,
const void *array,
size_t n
) {
return cx_ll_insert_sorted_impl(list, array, n, true);
}
static size_t cx_ll_insert_unique(
struct cx_list_s *list,
const void *array,
size_t n
) {
return cx_ll_insert_sorted_impl(list, array, n, false);
}
static size_t cx_ll_remove(
struct cx_list_s *list,
size_t index,
size_t num,
void *targetbuf
) {
cx_linked_list *ll = (cx_linked_list *) list;
void *node = cx_ll_node_at(ll, index);
if (node ==
NULL)
return 0;
size_t removed = cx_linked_list_remove_chain(
(
void **) &ll->begin,
(
void **) &ll->end,
ll->loc_prev,
ll->loc_next,
node,
num
);
list->collection.size -= removed;
if (targetbuf ==
NULL) {
char *n = node;
for (
size_t i =
0; i < removed; i++) {
cx_invoke_destructor(list, n + ll->loc_data);
void *next =
CX_LL_PTR(n, ll->loc_next);
cxFree(list->collection.allocator, n);
n = next;
}
}
else {
char *dest = targetbuf;
char *n = node;
for (
size_t i =
0; i < removed; i++) {
memcpy(dest, n + ll->loc_data, list->collection.elem_size);
dest += list->collection.elem_size;
void *next =
CX_LL_PTR(n, ll->loc_next);
cxFree(list->collection.allocator, n);
n = next;
}
}
return removed;
}
static void cx_ll_clear(
struct cx_list_s *list) {
if (list->collection.size ==
0)
return;
cx_linked_list *ll = (cx_linked_list *) list;
char *node = ll->begin;
while (node !=
NULL) {
cx_invoke_destructor(list, node + ll->loc_data);
void *next =
CX_LL_PTR(node, ll->loc_next);
cxFree(list->collection.allocator, node);
node = next;
}
ll->begin = ll->end =
NULL;
list->collection.size =
0;
}
static int cx_ll_swap(
struct cx_list_s *list,
size_t i,
size_t j
) {
if (i >= list->collection.size || j >= list->collection.size)
return 1;
if (i == j)
return 0;
cx_linked_list *ll = (cx_linked_list *) list;
size_t mid = list->collection.size /
2;
size_t left, right;
if (i < j) {
left = i;
right = j;
}
else {
left = j;
right = i;
}
void *nleft =
NULL, *nright =
NULL;
if (left < mid && right < mid) {
nleft = cx_ll_node_at(ll, left);
assert(nleft !=
NULL);
nright = nleft;
for (
size_t c = left; c < right; c++) {
nright =
CX_LL_PTR(nright, ll->loc_next);
}
}
else if (left >= mid && right >= mid) {
nright = cx_ll_node_at(ll, right);
assert(nright !=
NULL);
nleft = nright;
for (
size_t c = right; c > left; c--) {
nleft =
CX_LL_PTR(nleft, ll->loc_prev);
}
}
else {
size_t closest;
size_t other;
size_t diff2boundary = list->collection.size - right -
1;
if (left <= diff2boundary) {
closest = left;
other = right;
nleft = cx_ll_node_at(ll, left);
}
else {
closest = right;
other = left;
diff2boundary = left;
nright = cx_ll_node_at(ll, right);
}
if (right - left <= diff2boundary) {
if (closest == left) {
nright = nleft;
for (
size_t c = left; c < right; c++) {
nright =
CX_LL_PTR(nright, ll->loc_next);
}
}
else {
nleft = nright;
for (
size_t c = right; c > left; c--) {
nleft =
CX_LL_PTR(nleft, ll->loc_prev);
}
}
}
else {
if (closest == left) {
nright = cx_ll_node_at(ll, other);
}
else {
nleft = cx_ll_node_at(ll, other);
}
}
}
void *prev =
CX_LL_PTR(nleft, ll->loc_prev);
void *next =
CX_LL_PTR(nright, ll->loc_next);
void *midstart =
CX_LL_PTR(nleft, ll->loc_next);
void *midend =
CX_LL_PTR(nright, ll->loc_prev);
if (prev ==
NULL) {
ll->begin = nright;
}
else {
CX_LL_PTR(prev, ll->loc_next) = nright;
}
CX_LL_PTR(nright, ll->loc_prev) = prev;
if (midstart == nright) {
CX_LL_PTR(nright, ll->loc_next) = nleft;
CX_LL_PTR(nleft, ll->loc_prev) = nright;
}
else {
CX_LL_PTR(nright, ll->loc_next) = midstart;
CX_LL_PTR(midstart, ll->loc_prev) = nright;
CX_LL_PTR(midend, ll->loc_next) = nleft;
CX_LL_PTR(nleft, ll->loc_prev) = midend;
}
CX_LL_PTR(nleft, ll->loc_next) = next;
if (next ==
NULL) {
ll->end = nleft;
}
else {
CX_LL_PTR(next, ll->loc_prev) = nleft;
}
return 0;
}
static void *cx_ll_at(
const struct cx_list_s *list,
size_t index
) {
cx_linked_list *ll = (cx_linked_list *) list;
char *node = cx_ll_node_at(ll, index);
return node ==
NULL ?
NULL : node + ll->loc_data;
}
static size_t cx_ll_find_remove(
struct cx_list_s *list,
const void *elem,
bool remove
) {
if (list->collection.size ==
0)
return 0;
size_t index;
cx_linked_list *ll = (cx_linked_list *) list;
char *node = cx_linked_list_find(
ll->begin,
ll->loc_next, ll->loc_data,
list->collection.cmpfunc, elem,
&index
);
if (node ==
NULL) {
return list->collection.size;
}
if (remove) {
cx_invoke_destructor(list, node + ll->loc_data);
cx_linked_list_remove(&ll->begin, &ll->end,
ll->loc_prev, ll->loc_next, node);
list->collection.size--;
cxFree(list->collection.allocator, node);
}
return index;
}
static void cx_ll_sort(
struct cx_list_s *list) {
cx_linked_list *ll = (cx_linked_list *) list;
cx_linked_list_sort(&ll->begin, &ll->end,
ll->loc_prev, ll->loc_next, ll->loc_data,
list->collection.cmpfunc);
}
static void cx_ll_reverse(
struct cx_list_s *list) {
cx_linked_list *ll = (cx_linked_list *) list;
cx_linked_list_reverse(&ll->begin, &ll->end, ll->loc_prev, ll->loc_next);
}
static int cx_ll_compare(
const struct cx_list_s *list,
const struct cx_list_s *other
) {
cx_linked_list *left = (cx_linked_list *) list;
cx_linked_list *right = (cx_linked_list *) other;
assert(left->loc_next == right->loc_next);
assert(left->loc_data == right->loc_data);
return cx_linked_list_compare(left->begin, right->begin,
left->loc_next, left->loc_data,
list->collection.cmpfunc);
}
static bool cx_ll_iter_valid(
const void *it) {
const struct cx_iterator_s *iter = it;
return iter->elem_handle !=
NULL;
}
static void cx_ll_iter_next(
void *it) {
struct cx_iterator_s *iter = it;
cx_linked_list *ll = iter->src_handle;
if (iter->base.remove) {
iter->base.remove = false;
struct cx_list_s *list = iter->src_handle;
char *node = iter->elem_handle;
iter->elem_handle =
CX_LL_PTR(node, ll->loc_next);
cx_invoke_destructor(list, node + ll->loc_data);
cx_linked_list_remove(&ll->begin, &ll->end,
ll->loc_prev, ll->loc_next, node);
list->collection.size--;
iter->elem_count--;
cxFree(list->collection.allocator, node);
}
else {
iter->index++;
void *node = iter->elem_handle;
iter->elem_handle =
CX_LL_PTR(node, ll->loc_next);
}
}
static void cx_ll_iter_prev(
void *it) {
struct cx_iterator_s *iter = it;
cx_linked_list *ll = iter->src_handle;
if (iter->base.remove) {
iter->base.remove = false;
struct cx_list_s *list = iter->src_handle;
char *node = iter->elem_handle;
iter->elem_handle =
CX_LL_PTR(node, ll->loc_prev);
iter->index--;
cx_invoke_destructor(list, node + ll->loc_data);
cx_linked_list_remove(&ll->begin, &ll->end,
ll->loc_prev, ll->loc_next, node);
list->collection.size--;
iter->elem_count--;
cxFree(list->collection.allocator, node);
}
else {
iter->index--;
char *node = iter->elem_handle;
iter->elem_handle =
CX_LL_PTR(node, ll->loc_prev);
}
}
static void *cx_ll_iter_current(
const void *it) {
const struct cx_iterator_s *iter = it;
const cx_linked_list *ll = iter->src_handle;
char *node = iter->elem_handle;
return node + ll->loc_data;
}
static CxIterator cx_ll_iterator(
const struct cx_list_s *list,
size_t index,
bool backwards
) {
CxIterator iter;
iter.index = index;
iter.src_handle = (
void*)list;
iter.elem_handle = cx_ll_node_at((
const cx_linked_list *) list, index);
iter.elem_size = list->collection.elem_size;
iter.elem_count = list->collection.size;
iter.base.valid = cx_ll_iter_valid;
iter.base.current = cx_ll_iter_current;
iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
iter.base.allow_remove = true;
iter.base.remove = false;
return iter;
}
static int cx_ll_insert_iter(
CxIterator *iter,
const void *elem,
int prepend
) {
struct cx_list_s *list = iter->src_handle;
cx_linked_list *ll = iter->src_handle;
void *node = iter->elem_handle;
if (node !=
NULL) {
assert(prepend >=
0 && prepend <=
1);
void *choice[
2] = {node,
CX_LL_PTR(node, ll->loc_prev)};
int result = cx_ll_insert_at(list, choice[prepend], elem);
if (result ==
0) {
iter->elem_count++;
if (prepend) {
iter->index++;
}
}
return result;
}
else {
if (cx_ll_insert_element(list, list->collection.size, elem) ==
NULL) {
return 1;
}
iter->elem_count++;
iter->index = list->collection.size;
return 0;
}
}
static void cx_ll_destructor(CxList *list) {
cx_linked_list *ll = (cx_linked_list *) list;
char *node = ll->begin;
while (node) {
cx_invoke_destructor(list, node + ll->loc_data);
void *next =
CX_LL_PTR(node, ll->loc_next);
cxFree(list->collection.allocator, node);
node = next;
}
cxFree(list->collection.allocator, list);
}
static cx_list_class cx_linked_list_class = {
cx_ll_destructor,
cx_ll_insert_element,
cx_ll_insert_array,
cx_ll_insert_sorted,
cx_ll_insert_unique,
cx_ll_insert_iter,
cx_ll_remove,
cx_ll_clear,
cx_ll_swap,
cx_ll_at,
cx_ll_find_remove,
cx_ll_sort,
cx_ll_compare,
cx_ll_reverse,
NULL,
cx_ll_iterator,
};
CxList *cxLinkedListCreate(
const CxAllocator *allocator,
cx_compare_func comparator,
size_t elem_size
) {
if (allocator ==
NULL) {
allocator = cxDefaultAllocator;
}
cx_linked_list *list = cxCalloc(allocator,
1,
sizeof(cx_linked_list));
if (list ==
NULL)
return NULL;
list->loc_prev =
0;
list->loc_next =
sizeof(
void*);
list->loc_data =
sizeof(
void*)*
2;
list->loc_extra =
-1;
list->extra_data_len =
0;
cx_list_init((CxList*)list, &cx_linked_list_class,
allocator, comparator, elem_size);
return (CxList *) list;
}
void cx_linked_list_extra_data(cx_linked_list *list,
size_t len) {
list->extra_data_len = len;
off_t loc_extra = list->loc_data + list->base.collection.elem_size;
size_t alignment = alignof(
void*);
size_t padding = alignment - (loc_extra % alignment);
list->loc_extra = loc_extra + padding;
}