#include "cx/array_list.h"
#include "cx/compare.h"
#include <assert.h>
#include <string.h>
static void *cx_array_default_realloc(
void *array,
size_t capacity,
size_t elem_size,
__attribute__((__unused__))
struct cx_array_reallocator_s *alloc
) {
return realloc(array, capacity * elem_size);
}
struct cx_array_reallocator_s cx_array_default_reallocator_impl = {
cx_array_default_realloc,
NULL,
NULL,
0,
0
};
struct cx_array_reallocator_s *cx_array_default_reallocator = &cx_array_default_reallocator_impl;
enum cx_array_result cx_array_copy(
void **target,
size_t *size,
size_t *capacity,
size_t index,
const void *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_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_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_SUCCESS;
}
enum cx_array_result cx_array_insert_sorted(
void **target,
size_t *size,
size_t *capacity,
cx_compare_func cmp_func,
const void *sorted_data,
size_t elem_size,
size_t elem_count,
struct cx_array_reallocator_s *reallocator
) {
assert(target !=
NULL);
assert(size !=
NULL);
assert(capacity !=
NULL);
assert(cmp_func !=
NULL);
assert(sorted_data !=
NULL);
assert(reallocator !=
NULL);
if (elem_count ==
0)
return 0;
size_t old_size = *size;
size_t needed_capacity = old_size + elem_count;
if (needed_capacity > *capacity) {
size_t new_capacity = needed_capacity - (needed_capacity %
16) +
16;
void *new_mem = reallocator->realloc(
*target, new_capacity, elem_size, reallocator
);
if (new_mem ==
NULL) {
return CX_ARRAY_REALLOC_FAILED;
}
*target = new_mem;
*capacity = new_capacity;
}
size_t new_size = old_size + elem_count;
*size = new_size;
size_t si =
0, di =
0;
const char *src = sorted_data;
char *dest = *target;
di = cx_array_binary_search_sup(dest, old_size, elem_size, src, cmp_func);
dest += di * elem_size;
size_t buf_size = old_size - di;
size_t bi = new_size - buf_size;
char *bptr = ((
char *) *target) + bi * elem_size;
memmove(bptr, dest, buf_size * elem_size);
while (si < elem_count && bi < new_size) {
size_t copy_len, bytes_copied;
copy_len = cx_array_binary_search_sup(
src,
elem_count - si,
elem_size,
bptr,
cmp_func
);
bytes_copied = copy_len * elem_size;
memcpy(dest, src, bytes_copied);
dest += bytes_copied;
src += bytes_copied;
si += copy_len;
if (si >= elem_count)
break;
copy_len = cx_array_binary_search_sup(
bptr,
new_size - bi,
elem_size,
src,
cmp_func
);
bytes_copied = copy_len * elem_size;
memmove(dest, bptr, bytes_copied);
dest += bytes_copied;
bptr += bytes_copied;
bi += copy_len;
}
if (si < elem_count) {
memcpy(dest, src, elem_size * (elem_count - si));
}
return CX_ARRAY_SUCCESS;
}
size_t cx_array_binary_search_inf(
const void *arr,
size_t size,
size_t elem_size,
const void *elem,
cx_compare_func cmp_func
) {
if (size ==
0)
return 0;
int result;
const char *array = arr;
result = cmp_func(elem, array);
if (result <
0) {
return size;
}
else if (result ==
0) {
return 0;
}
result = cmp_func(elem, array + elem_size * (size -
1));
if (result >=
0) {
return size -
1;
}
size_t left_index =
1;
size_t right_index = size -
1;
size_t pivot_index;
while (left_index <= right_index) {
pivot_index = left_index + (right_index - left_index) /
2;
const char *arr_elem = array + pivot_index * elem_size;
result = cmp_func(elem, arr_elem);
if (result ==
0) {
return pivot_index;
}
else if (result <
0) {
right_index = pivot_index -
1;
}
else {
left_index = pivot_index +
1;
}
}
return result <
0 ? (pivot_index -
1) : pivot_index;
}
#ifndef CX_ARRAY_SWAP_SBO_SIZE
#define CX_ARRAY_SWAP_SBO_SIZE 128
#endif
unsigned cx_array_swap_sbo_size =
CX_ARRAY_SWAP_SBO_SIZE;
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
) {
const CxAllocator *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->collection.simple_destructor) {
for (
size_t i =
0; i < list->collection.size; i++) {
cx_invoke_simple_destructor(list, ptr);
ptr += list->collection.elem_size;
}
}
if (list->collection.advanced_destructor) {
for (
size_t i =
0; i < list->collection.size; i++) {
cx_invoke_advanced_destructor(list, ptr);
ptr += list->collection.elem_size;
}
}
cxFree(list->collection.allocator, arl->data);
cxFree(list->collection.allocator, list);
}
static size_t cx_arl_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;
cx_array_list *arl = (cx_array_list *) list;
if (index < list->collection.size) {
const char *first_to_move = (
const char *) arl->data;
first_to_move += index * list->collection.elem_size;
size_t elems_to_move = list->collection.size - index;
size_t start_of_moved = index + n;
if (
CX_ARRAY_SUCCESS != cx_array_copy(
&arl->data,
&list->collection.size,
&arl->capacity,
start_of_moved,
first_to_move,
list->collection.elem_size,
elems_to_move,
&arl->reallocator
)) {
return 0;
}
}
if (
CX_ARRAY_SUCCESS == cx_array_copy(
&arl->data,
&list->collection.size,
&arl->capacity,
index,
array,
list->collection.elem_size,
n,
&arl->reallocator
)) {
return n;
}
else {
return 0;
}
}
static size_t cx_arl_insert_sorted(
struct cx_list_s *list,
const void *sorted_data,
size_t n
) {
cx_array_list *arl = (cx_array_list *) list;
if (
CX_ARRAY_SUCCESS == cx_array_insert_sorted(
&arl->data,
&list->collection.size,
&arl->capacity,
list->collection.cmpfunc,
sorted_data,
list->collection.elem_size,
n,
&arl->reallocator
)) {
return n;
}
else {
return 0;
}
}
static int cx_arl_insert_element(
struct cx_list_s *list,
size_t index,
const void *element
) {
return 1 != cx_arl_insert_array(list, index, element,
1);
}
static int cx_arl_insert_iter(
struct cx_iterator_s *iter,
const void *elem,
int prepend
) {
struct cx_list_s *list = iter->src_handle.m;
if (iter->index < list->collection.size) {
int result = cx_arl_insert_element(
list,
iter->index +
1 - prepend,
elem
);
if (result ==
0) {
iter->elem_count++;
if (prepend !=
0) {
iter->index++;
iter->elem_handle = ((
char *) iter->elem_handle) + list->collection.elem_size;
}
}
return result;
}
else {
int result = cx_arl_insert_element(list, list->collection.size, elem);
if (result ==
0) {
iter->elem_count++;
iter->index = list->collection.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->collection.size) {
return 1;
}
cx_invoke_destructor(list, ((
char *) arl->data) + index * list->collection.elem_size);
if (index == list->collection.size -
1) {
list->collection.size--;
return 0;
}
int result = cx_array_copy(
&arl->data,
&list->collection.size,
&arl->capacity,
index,
((
char *) arl->data) + (index +
1) * list->collection.elem_size,
list->collection.elem_size,
list->collection.size - index -
1,
&arl->reallocator
);
assert(result ==
0);
list->collection.size--;
return 0;
}
static void cx_arl_clear(
struct cx_list_s *list) {
if (list->collection.size ==
0)
return;
cx_array_list *arl = (cx_array_list *) list;
char *ptr = arl->data;
if (list->collection.simple_destructor) {
for (
size_t i =
0; i < list->collection.size; i++) {
cx_invoke_simple_destructor(list, ptr);
ptr += list->collection.elem_size;
}
}
if (list->collection.advanced_destructor) {
for (
size_t i =
0; i < list->collection.size; i++) {
cx_invoke_advanced_destructor(list, ptr);
ptr += list->collection.elem_size;
}
}
memset(arl->data,
0, list->collection.size * list->collection.elem_size);
list->collection.size =
0;
}
static int cx_arl_swap(
struct cx_list_s *list,
size_t i,
size_t j
) {
if (i >= list->collection.size || j >= list->collection.size)
return 1;
cx_array_list *arl = (cx_array_list *) list;
cx_array_swap(arl->data, list->collection.elem_size, i, j);
return 0;
}
static void *cx_arl_at(
const struct cx_list_s *list,
size_t index
) {
if (index < list->collection.size) {
const cx_array_list *arl = (
const cx_array_list *) list;
char *space = arl->data;
return space + index * list->collection.elem_size;
}
else {
return NULL;
}
}
static ssize_t cx_arl_find_remove(
struct cx_list_s *list,
const void *elem,
bool remove
) {
assert(list->collection.cmpfunc !=
NULL);
assert(list->collection.size <
SIZE_MAX /
2);
char *cur = ((
const cx_array_list *) list)->data;
for (
ssize_t i =
0; i < (
ssize_t) list->collection.size; i++) {
if (
0 == list->collection.cmpfunc(elem, cur)) {
if (remove) {
if (
0 == cx_arl_remove(list, i)) {
return i;
}
else {
return -
1;
}
}
else {
return i;
}
}
cur += list->collection.elem_size;
}
return -
1;
}
static void cx_arl_sort(
struct cx_list_s *list) {
assert(list->collection.cmpfunc !=
NULL);
qsort(((cx_array_list *) list)->data,
list->collection.size,
list->collection.elem_size,
list->collection.cmpfunc
);
}
static int cx_arl_compare(
const struct cx_list_s *list,
const struct cx_list_s *other
) {
assert(list->collection.cmpfunc !=
NULL);
if (list->collection.size == other->collection.size) {
const char *left = ((
const cx_array_list *) list)->data;
const char *right = ((
const cx_array_list *) other)->data;
for (
size_t i =
0; i < list->collection.size; i++) {
int d = list->collection.cmpfunc(left, right);
if (d !=
0) {
return d;
}
left += list->collection.elem_size;
right += other->collection.elem_size;
}
return 0;
}
else {
return list->collection.size < other->collection.size ? -
1 :
1;
}
}
static void cx_arl_reverse(
struct cx_list_s *list) {
if (list->collection.size <
2)
return;
void *data = ((
const cx_array_list *) list)->data;
size_t half = list->collection.size /
2;
for (
size_t i =
0; i < half; i++) {
cx_array_swap(data, list->collection.elem_size, i, list->collection.size -
1 - i);
}
}
static bool cx_arl_iter_valid(
const void *it) {
const struct cx_iterator_s *iter = it;
const struct cx_list_s *list = iter->src_handle.c;
return iter->index < list->collection.size;
}
static void *cx_arl_iter_current(
const void *it) {
const struct cx_iterator_s *iter = it;
return iter->elem_handle;
}
static void cx_arl_iter_next(
void *it) {
struct cx_iterator_s *iter = it;
if (iter->base.remove) {
iter->base.remove = false;
cx_arl_remove(iter->src_handle.m, iter->index);
}
else {
iter->index++;
iter->elem_handle =
((
char *) iter->elem_handle)
+ ((
const struct cx_list_s *) iter->src_handle.c)->collection.elem_size;
}
}
static void cx_arl_iter_prev(
void *it) {
struct cx_iterator_s *iter = it;
const cx_array_list *list = iter->src_handle.c;
if (iter->base.remove) {
iter->base.remove = false;
cx_arl_remove(iter->src_handle.m, iter->index);
}
iter->index--;
if (iter->index < list->base.collection.size) {
iter->elem_handle = ((
char *) list->data)
+ iter->index * list->base.collection.elem_size;
}
}
static struct cx_iterator_s cx_arl_iterator(
const struct cx_list_s *list,
size_t index,
bool backwards
) {
struct cx_iterator_s iter;
iter.index = index;
iter.src_handle.c = list;
iter.elem_handle = cx_arl_at(list, index);
iter.elem_size = list->collection.elem_size;
iter.elem_count = list->collection.size;
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.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_sorted,
cx_arl_insert_iter,
cx_arl_remove,
cx_arl_clear,
cx_arl_swap,
cx_arl_at,
cx_arl_find_remove,
cx_arl_sort,
cx_arl_compare,
cx_arl_reverse,
cx_arl_iterator,
};
CxList *cxArrayListCreate(
const CxAllocator *allocator,
cx_compare_func comparator,
size_t elem_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.collection.allocator = allocator;
list->capacity = initial_capacity;
if (elem_size >
0) {
list->base.collection.elem_size = elem_size;
list->base.collection.cmpfunc = comparator;
}
else {
elem_size =
sizeof(
void *);
list->base.collection.cmpfunc = comparator ==
NULL ? cx_cmp_ptr : comparator;
cxListStorePointers((CxList *) list);
}
list->data = cxCalloc(allocator, initial_capacity, elem_size);
if (list->data ==
NULL) {
cxFree(allocator, list);
return NULL;
}
list->reallocator.realloc = cx_arl_realloc;
list->reallocator.ptr1 = (
void *) allocator;
return (CxList *) list;
}