#include "cx/array_list.h"
#include <assert.h>
#include <string.h>
enum cx_array_copy_result cx_array_copy(
void **target,
size_t *size,
size_t *capacity,
size_t index,
void const *src,
size_t elem_size,
size_t elem_count,
struct cx_array_reallocator_s *reallocator
) {
assert(target !=
NULL);
assert(size !=
NULL);
assert(src !=
NULL);
size_t cap = capacity ==
NULL ? *size : *capacity;
size_t minsize = index + elem_count;
size_t newsize = *size < minsize ? minsize : *size;
bool needrealloc = newsize > cap;
if (needrealloc) {
if (reallocator ==
NULL || capacity ==
NULL) {
return CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED;
}
uintptr_t targetaddr = (
uintptr_t) *target;
uintptr_t srcaddr = (
uintptr_t) src;
bool repairsrc = targetaddr <= srcaddr
&& srcaddr < targetaddr + cap * elem_size;
cap = newsize - (newsize %
16) +
16;
assert(cap > newsize);
void *newmem = reallocator->realloc(
*target, cap, elem_size, reallocator
);
if (newmem ==
NULL) {
return CX_ARRAY_COPY_REALLOC_FAILED;
}
if (repairsrc) {
src = ((
char *) newmem) + (srcaddr - targetaddr);
}
*target = newmem;
*capacity = cap;
}
char *start = *target;
start += index * elem_size;
memmove(start, src, elem_count * elem_size);
*size = newsize;
return CX_ARRAY_COPY_SUCCESS;
}
#ifndef CX_ARRAY_SWAP_SBO_SIZE
#define CX_ARRAY_SWAP_SBO_SIZE 512
#endif
void cx_array_swap(
void *arr,
size_t elem_size,
size_t idx1,
size_t idx2
) {
assert(arr !=
NULL);
if (idx1 == idx2)
return;
char sbo_mem[
CX_ARRAY_SWAP_SBO_SIZE];
void *tmp;
if (elem_size >
CX_ARRAY_SWAP_SBO_SIZE) {
tmp = malloc(elem_size);
if (tmp ==
NULL) abort();
}
else {
tmp = sbo_mem;
}
char *left = arr, *right = arr;
left += idx1 * elem_size;
right += idx2 * elem_size;
memcpy(tmp, left, elem_size);
memcpy(left, right, elem_size);
memcpy(right, tmp, elem_size);
if (tmp != sbo_mem) {
free(tmp);
}
}
typedef struct {
struct cx_list_s base;
void *data;
size_t capacity;
struct cx_array_reallocator_s reallocator;
} cx_array_list;
static void *cx_arl_realloc(
void *array,
size_t capacity,
size_t elem_size,
struct cx_array_reallocator_s *alloc
) {
CxAllocator
const *al = alloc->ptr1;
return cxRealloc(al, array, capacity * elem_size);
}
static void cx_arl_destructor(
struct cx_list_s *list) {
cx_array_list *arl = (cx_array_list *) list;
char *ptr = arl->data;
if (list->simple_destructor) {
for (
size_t i =
0; i < list->size; i++) {
cx_invoke_simple_destructor(list, ptr);
ptr += list->item_size;
}
}
if (list->advanced_destructor) {
for (
size_t i =
0; i < list->size; i++) {
cx_invoke_advanced_destructor(list, ptr);
ptr += list->item_size;
}
}
cxFree(list->allocator, arl->data);
cxFree(list->allocator, list);
}
static size_t cx_arl_insert_array(
struct cx_list_s *list,
size_t index,
void const *array,
size_t n
) {
if (index > list->size || n ==
0)
return 0;
cx_array_list *arl = (cx_array_list *) list;
if (index < list->size) {
char const *first_to_move = (
char const *) arl->data;
first_to_move += index * list->item_size;
size_t elems_to_move = list->size - index;
size_t start_of_moved = index + n;
if (
CX_ARRAY_COPY_SUCCESS != cx_array_copy(
&arl->data,
&list->size,
&arl->capacity,
start_of_moved,
first_to_move,
list->item_size,
elems_to_move,
&arl->reallocator
)) {
return 0;
}
}
if (
CX_ARRAY_COPY_SUCCESS == cx_array_copy(
&arl->data,
&list->size,
&arl->capacity,
index,
array,
list->item_size,
n,
&arl->reallocator
)) {
return n;
}
else {
return 0;
}
}
static int cx_arl_insert_element(
struct cx_list_s *list,
size_t index,
void const *element
) {
return 1 != cx_arl_insert_array(list, index, element,
1);
}
static int cx_arl_insert_iter(
struct cx_mut_iterator_s *iter,
void const *elem,
int prepend
) {
struct cx_list_s *list = iter->src_handle;
if (iter->index < list->size) {
int result = cx_arl_insert_element(
list,
iter->index +
1 - prepend,
elem
);
if (result ==
0 && prepend !=
0) {
iter->index++;
iter->elem_handle = ((
char *) iter->elem_handle) + list->item_size;
}
return result;
}
else {
int result = cx_arl_insert_element(list, list->size, elem);
iter->index = list->size;
return result;
}
}
static int cx_arl_remove(
struct cx_list_s *list,
size_t index
) {
cx_array_list *arl = (cx_array_list *) list;
if (index >= list->size) {
return 1;
}
cx_invoke_destructor(list, ((
char *) arl->data) + index * list->item_size);
if (index == list->size -
1) {
list->size--;
return 0;
}
int result = cx_array_copy(
&arl->data,
&list->size,
&arl->capacity,
index,
((
char *) arl->data) + (index +
1) * list->item_size,
list->item_size,
list->size - index -
1,
&arl->reallocator
);
if (result ==
0) {
list->size--;
}
return result;
}
static void cx_arl_clear(
struct cx_list_s *list) {
if (list->size ==
0)
return;
cx_array_list *arl = (cx_array_list *) list;
char *ptr = arl->data;
if (list->simple_destructor) {
for (
size_t i =
0; i < list->size; i++) {
cx_invoke_simple_destructor(list, ptr);
ptr += list->item_size;
}
}
if (list->advanced_destructor) {
for (
size_t i =
0; i < list->size; i++) {
cx_invoke_advanced_destructor(list, ptr);
ptr += list->item_size;
}
}
memset(arl->data,
0, list->size * list->item_size);
list->size =
0;
}
static int cx_arl_swap(
struct cx_list_s *list,
size_t i,
size_t j
) {
if (i >= list->size || j >= list->size)
return 1;
cx_array_list *arl = (cx_array_list *) list;
cx_array_swap(arl->data, list->item_size, i, j);
return 0;
}
static void *cx_arl_at(
struct cx_list_s
const *list,
size_t index
) {
if (index < list->size) {
cx_array_list
const *arl = (cx_array_list
const *) list;
char *space = arl->data;
return space + index * list->item_size;
}
else {
return NULL;
}
}
static ssize_t cx_arl_find(
struct cx_list_s
const *list,
void const *elem
) {
assert(list->cmpfunc !=
NULL);
assert(list->size <
SIZE_MAX /
2);
char *cur = ((cx_array_list
const *) list)->data;
for (
ssize_t i =
0; i < (
ssize_t) list->size; i++) {
if (
0 == list->cmpfunc(elem, cur)) {
return i;
}
cur += list->item_size;
}
return -
1;
}
static void cx_arl_sort(
struct cx_list_s *list) {
assert(list->cmpfunc !=
NULL);
qsort(((cx_array_list *) list)->data,
list->size,
list->item_size,
list->cmpfunc
);
}
static int cx_arl_compare(
struct cx_list_s
const *list,
struct cx_list_s
const *other
) {
assert(list->cmpfunc !=
NULL);
if (list->size == other->size) {
char const *left = ((cx_array_list
const *) list)->data;
char const *right = ((cx_array_list
const *) other)->data;
for (
size_t i =
0; i < list->size; i++) {
int d = list->cmpfunc(left, right);
if (d !=
0) {
return d;
}
left += list->item_size;
right += other->item_size;
}
return 0;
}
else {
return list->size < other->size ? -
1 :
1;
}
}
static void cx_arl_reverse(
struct cx_list_s *list) {
if (list->size <
2)
return;
void *data = ((cx_array_list
const *) list)->data;
size_t half = list->size /
2;
for (
size_t i =
0; i < half; i++) {
cx_array_swap(data, list->item_size, i, list->size -
1 - i);
}
}
static bool cx_arl_iter_valid(
void const *it) {
struct cx_iterator_s
const *iter = it;
struct cx_list_s
const *list = iter->src_handle;
return iter->index < list->size;
}
static void *cx_arl_iter_current(
void const *it) {
struct cx_iterator_s
const *iter = it;
return iter->elem_handle;
}
static void cx_arl_iter_next(
void *it) {
struct cx_iterator_base_s *itbase = it;
if (itbase->remove) {
struct cx_mut_iterator_s *iter = it;
itbase->remove = false;
cx_arl_remove(iter->src_handle, iter->index);
}
else {
struct cx_iterator_s *iter = it;
iter->index++;
iter->elem_handle =
((
char *) iter->elem_handle)
+ ((
struct cx_list_s
const *) iter->src_handle)->item_size;
}
}
static void cx_arl_iter_prev(
void *it) {
struct cx_iterator_base_s *itbase = it;
struct cx_mut_iterator_s *iter = it;
cx_array_list *
const list = iter->src_handle;
if (itbase->remove) {
itbase->remove = false;
cx_arl_remove(iter->src_handle, iter->index);
}
iter->index--;
if (iter->index < list->base.size) {
iter->elem_handle = ((
char *) list->data)
+ iter->index * list->base.item_size;
}
}
static bool cx_arl_iter_flag_rm(
void *it) {
struct cx_iterator_base_s *iter = it;
if (iter->mutating) {
iter->remove = true;
return true;
}
else {
return false;
}
}
static struct cx_iterator_s cx_arl_iterator(
struct cx_list_s
const *list,
size_t index,
bool backwards
) {
struct cx_iterator_s iter;
iter.index = index;
iter.src_handle = list;
iter.elem_handle = cx_arl_at(list, index);
iter.base.valid = cx_arl_iter_valid;
iter.base.current = cx_arl_iter_current;
iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
iter.base.flag_removal = cx_arl_iter_flag_rm;
iter.base.remove = false;
iter.base.mutating = false;
return iter;
}
static cx_list_class cx_array_list_class = {
cx_arl_destructor,
cx_arl_insert_element,
cx_arl_insert_array,
cx_arl_insert_iter,
cx_arl_remove,
cx_arl_clear,
cx_arl_swap,
cx_arl_at,
cx_arl_find,
cx_arl_sort,
cx_arl_compare,
cx_arl_reverse,
cx_arl_iterator,
};
CxList *cxArrayListCreate(
CxAllocator
const *allocator,
cx_compare_func comparator,
size_t item_size,
size_t initial_capacity
) {
if (allocator ==
NULL) {
allocator = cxDefaultAllocator;
}
cx_array_list *list = cxCalloc(allocator,
1,
sizeof(cx_array_list));
if (list ==
NULL)
return NULL;
list->base.cl = &cx_array_list_class;
list->base.allocator = allocator;
list->base.cmpfunc = comparator;
list->capacity = initial_capacity;
if (item_size >
0) {
list->base.item_size = item_size;
}
else {
item_size =
sizeof(
void *);
cxListStorePointers((CxList *) list);
}
list->data = cxCalloc(allocator, initial_capacity, item_size);
if (list->data ==
NULL) {
cxFree(allocator, list);
return NULL;
}
list->reallocator.realloc = cx_arl_realloc;
list->reallocator.ptr1 = (
void *) allocator;
return (CxList *) list;
}