#include <sys/types.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "slist.h"
#define GROW_SIZE 256
#define PTR_SIZE (sizeof(intptr_t))
#ifdef SLIST_DEBUG
#include <stdio.h>
#define SLIST_ASSERT(cond) \
if (!(cond)) { \
fprintf(stderr, \
"\nAssertion failure("#cond") at (%s):%s:%d\n", \
__func__, __FILE__, __LINE__); \
}
#else
#define SLIST_ASSERT(cond)
#endif
#define VALID_IDX(_list, _idx) \
(((_list)->first_idx <= (_list)->last_idx) \
? (((_list)->first_idx <= (_idx) && (_idx) < (_list)->last_idx)? 1 : 0)\
: (((_list)->first_idx <= (_idx) || (_idx) < (_list)->last_idx)? 1 : 0))
#define REAL_IDX(_list, _idx) \
(((_list)->first_idx + (_idx)) % (_list)->list_size)
#define VIRT_IDX(_list, _idx) (((_list)->first_idx <= (_idx)) \
? (_idx) - (_list)->first_idx \
: (_list)->list_size - (_list)->first_idx + (_idx))
#define DECR_IDX(_list, _memb) \
(_list)->_memb = ((_list)->list_size + --((_list)->_memb)) \
% (_list)->list_size
#define INCR_IDX(_list, _memb) \
(_list)->_memb = (++((_list)->_memb)) % (_list)->list_size
static int slist_grow (slist *);
static int slist_grow0 (slist *, int);
static __inline void slist_swap0 (slist *, int, int);
static __inline void slist_qsort0(slist *, int (*)(const void *, const void *), int, int);
#define itr_is_valid(list) ((list)->itr_next >= 0)
#define itr_invalidate(list) ((list)->itr_next = -1)
void
slist_init(slist *list)
{
memset(list, 0, sizeof(slist));
itr_invalidate(list);
}
int
slist_set_size(slist *list, int size)
{
if (size > list->list_size)
return slist_grow0(list, size - list->list_size);
return 0;
}
void
slist_fini(slist *list)
{
free(list->list);
slist_init(list);
}
int
slist_length(slist *list)
{
return
(list->first_idx <= list->last_idx)
? (list->last_idx - list->first_idx)
: (list->list_size - list->first_idx + list->last_idx);
}
static int
slist_grow0(slist *list, int grow_sz)
{
int size_new;
void **list_new = NULL;
if (slist_length(list) + 1 < list->list_size)
return 0;
size_new = list->list_size + grow_sz;
if ((list_new = realloc(list->list, PTR_SIZE * size_new))
== NULL)
return -1;
memset(&list_new[list->list_size], 0,
PTR_SIZE * (size_new - list->list_size));
list->list = list_new;
if (list->last_idx < list->first_idx && list->last_idx >= 0) {
if (list->last_idx <= grow_sz) {
memmove(&list->list[list->list_size],
&list->list[0], PTR_SIZE * list->last_idx);
list->last_idx = list->list_size + list->last_idx;
} else {
memmove(&list->list[list->list_size],
&list->list[0], PTR_SIZE * grow_sz);
memmove(&list->list[0], &list->list[grow_sz],
PTR_SIZE *(list->last_idx - grow_sz));
list->last_idx -= grow_sz;
}
}
list->list_size = size_new;
return 0;
}
static int
slist_grow(slist *list)
{
return slist_grow0(list, GROW_SIZE);
}
void *
slist_add(slist *list, void *item)
{
if (slist_grow(list) != 0)
return NULL;
list->list[list->last_idx] = item;
if (list->itr_next == -2) {
list->itr_next = list->last_idx;
}
INCR_IDX(list, last_idx);
return item;
}
#define slist_get0(list_, idx) ((list_)->list[REAL_IDX((list_), (idx))])
int
slist_add_all(slist *list, slist *add_items)
{
int i, n;
n = slist_length(add_items);
for (i = 0; i < n; i++) {
if (slist_add(list, slist_get0(add_items, i)) == NULL)
return 1;
}
return 0;
}
void *
slist_get(slist *list, int idx)
{
SLIST_ASSERT(idx >= 0);
SLIST_ASSERT(slist_length(list) > idx);
if (idx < 0 || slist_length(list) <= idx)
return NULL;
return slist_get0(list, idx);
}
int
slist_set(slist *list, int idx, void *item)
{
SLIST_ASSERT(idx >= 0);
SLIST_ASSERT(slist_length(list) > idx);
if (idx < 0 || slist_length(list) <= idx)
return -1;
list->list[REAL_IDX(list, idx)] = item;
return 0;
}
void *
slist_remove_first(slist *list)
{
void *oldVal;
if (slist_length(list) <= 0)
return NULL;
oldVal = list->list[list->first_idx];
if (itr_is_valid(list) && list->itr_next == list->first_idx)
INCR_IDX(list, itr_next);
if (!VALID_IDX(list, list->itr_next))
itr_invalidate(list);
INCR_IDX(list, first_idx);
return oldVal;
}
void *
slist_remove_last(slist *list)
{
if (slist_length(list) <= 0)
return NULL;
DECR_IDX(list, last_idx);
if (!VALID_IDX(list, list->itr_next))
itr_invalidate(list);
return list->list[list->last_idx];
}
void
slist_remove_all(slist *list)
{
void **list0 = list->list;
slist_init(list);
list->list = list0;
}
static __inline void
slist_swap0(slist *list, int m, int n)
{
void *m0;
itr_invalidate(list);
m0 = list->list[REAL_IDX(list, m)];
list->list[REAL_IDX(list, m)] = list->list[REAL_IDX(list, n)];
list->list[REAL_IDX(list, n)] = m0;
}
void
slist_swap(slist *list, int m, int n)
{
int len;
len = slist_length(list);
SLIST_ASSERT(m >= 0);
SLIST_ASSERT(n >= 0);
SLIST_ASSERT(len > m);
SLIST_ASSERT(len > n);
if (m < 0 || n < 0)
return;
if (m >= len || n >= len)
return;
slist_swap0(list, m, n);
}
void *
slist_remove(slist *list, int idx)
{
int first, last, idx0, reset_itr;
void *oldVal;
SLIST_ASSERT(idx >= 0);
SLIST_ASSERT(slist_length(list) > idx);
if (idx < 0 || slist_length(list) <= idx)
return NULL;
idx0 = REAL_IDX(list, idx);
oldVal = list->list[idx0];
reset_itr = 0;
first = -1;
last = -1;
if (list->itr_next == idx0) {
INCR_IDX(list, itr_next);
if (!VALID_IDX(list, list->itr_next))
list->itr_next = -2;
}
if (list->first_idx < list->last_idx) {
if (idx0 - list->first_idx < list->last_idx - idx0) {
first = list->first_idx;
INCR_IDX(list, first_idx);
} else {
last = list->last_idx;
DECR_IDX(list, last_idx);
}
} else {
if (list->first_idx <= idx0) {
first = list->first_idx;
INCR_IDX(list, first_idx);
} else {
last = list->last_idx;
DECR_IDX(list, last_idx);
}
}
if (last != -1 && last != 0 && last != idx0) {
if (itr_is_valid(list) &&
idx0 <= list->itr_next && list->itr_next <= last) {
DECR_IDX(list, itr_next);
if (!VALID_IDX(list, list->itr_next))
itr_invalidate(list);
}
memmove(&list->list[idx0], &list->list[idx0 + 1],
(PTR_SIZE) * (last - idx0));
}
if (first != -1 && first != idx0) {
if (itr_is_valid(list) &&
first <= list->itr_next && list->itr_next <= idx0) {
INCR_IDX(list, itr_next);
if (!VALID_IDX(list, list->itr_next))
itr_invalidate(list);
}
memmove(&list->list[first + 1], &list->list[first],
(PTR_SIZE) * (idx0 - first));
}
if (list->first_idx == list->last_idx) {
list->first_idx = 0;
list->last_idx = 0;
}
return oldVal;
}
void
slist_shuffle(slist *list)
{
int i, len;
len = slist_length(list);
for (i = len; i > 1; i--)
slist_swap0(list, i - 1, (int)arc4random_uniform(i));
}
void
slist_itr_first(slist *list)
{
list->itr_next = list->first_idx;
if (!VALID_IDX(list, list->itr_next))
itr_invalidate(list);
}
int
slist_itr_has_next(slist *list)
{
if (list->itr_next < 0)
return 0;
return VALID_IDX(list, list->itr_next);
}
void *
slist_itr_next(slist *list)
{
void *rval;
if (!itr_is_valid(list))
return NULL;
SLIST_ASSERT(VALID_IDX(list, list->itr_next));
if (list->list == NULL)
return NULL;
rval = list->list[list->itr_next];
list->itr_curr = list->itr_next;
INCR_IDX(list, itr_next);
if (!VALID_IDX(list, list->itr_next))
list->itr_next = -2;
return rval;
}
void *
slist_itr_remove(slist *list)
{
SLIST_ASSERT(list != NULL);
return slist_remove(list, VIRT_IDX(list, list->itr_curr));
}
void
slist_qsort(slist *list, int (*compar)(const void *, const void *))
{
if (list->first_idx != list->last_idx)
slist_qsort0(list, compar, 0, slist_length(list) - 1);
}
static __inline void
slist_qsort0(slist *list, int (*compar)(const void *, const void *), int l,
int r)
{
int i, j;
void *p;
i = l;
j = r;
p = slist_get0(list, (j + i) / 2);
while (i <= j) {
while (compar(slist_get0(list, i), p) < 0)
i++;
while (compar(slist_get0(list, j), p) > 0)
j--;
if (i <= j)
slist_swap0(list, i++, j--);
}
if (l < j)
slist_qsort0(list, compar, l, j);
if (i < r)
slist_qsort0(list, compar, i, r);
}