/*
* Basic list library
*
* Copyright 2001-2009 Nicolas Bernard
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
/**\bug _reeentrant code is untested and buggy */
#include
#include
#include
#include
#include
#include "list.h"
struct link {
struct link* prev;
void* data;
struct link* next;
};
typedef struct {
/*@null@*/ struct link* first;
/*@null@*/ struct link* last;
/*@null@*/ struct link* lcurrent;
unsigned int currentpos;
#ifdef _REENTRANT
pthread_mutex_t list_lock;
#endif /* _REENTRANT */
unsigned int nb_of_elt;
} list;
static list* LA[MAXLIST];
static inline list_error
list_lock(list_t L)
{
#ifdef _REENTRANT
int err = pthread_mutex_lock(&LA[L]->list_lock);
if (err) {
char errbuf[40]; /* Flawfinder: ignore */
strerror_r(err, errbuf, 40);
fprintf(stderr, "locking: %s", errbuf);
return MUTEX_ERROR_LOCK;
}
#else
L = 0; /* to avoid a warning */
#endif
return NO_ERROR;
}
static inline list_error
list_unlock(list_t L)
{
#ifdef _REENTRANT
if (pthread_mutex_unlock(&LA[L]->list_lock))
return MUTEX_ERROR_UNLOCK;
#else
L = 0; /* to avoid a warning */
#endif
return NO_ERROR;
}
static inline int
list_vrfy(list_t L)
{
if (L < 0)
return -1;
if (L >= MAXLIST)
return -2;
if (LA[L] == NULL)
return -3;
return 0;
}
void
list_stat(FILE* f)
{
#ifndef NDEBUG
unsigned int count = 0;
list_t idx = 0;
for (idx = 0; idx < MAXLIST; idx++) {
if (LA[idx] != NULL)
count++;
}
fprintf(f, "%u/%d list in use\n", count, MAXLIST);
for (idx = 0; idx < MAXLIST; idx++) {
if (LA[idx] != NULL) {
fprintf(f, "#list[%d]: %d; ", idx, list_size(idx));
}
}
fprintf(f, "\n");
#endif
}
list_t
list_create(void)
{ /** \bug we should put a mutex on LA */
list_t idx = 0;
for (idx = 0; idx < MAXLIST; idx++) {
if (LA[idx] == NULL)
break;
}
if (idx >= MAXLIST || LA[idx] != NULL)
return -1;
LA[idx] = (list*) malloc(sizeof(list));
if (LA[idx] == NULL)
return -1;
LA[idx]->first = NULL;
LA[idx]->last = NULL;
LA[idx]->lcurrent = NULL;
LA[idx]->currentpos = 0;
LA[idx]->nb_of_elt = 0;
#ifdef _REENTRANT
pthread_mutex_init(&LA[idx]->list_lock, NULL);
#endif /* _REENTRANT */
return idx;
}
list_error
list_delete(list_t L)
{
struct link* next = NULL;
struct link* current = NULL;
list_error eunl, err = NO_ERROR;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
err = list_lock(L);
if (err != NO_ERROR)
return err;
next = LA[L]->first;
if (next != NULL) do {
current = next;
next = next->next;
free(current->data);
current->data = NULL;
free(current);
} while(next != NULL);/* old: && next != LA[L]->last); */
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
#ifdef _REENTRANT
pthread_mutex_destroy(&LA[L]->list_lock);
#endif /* _REENTRANT */
free(LA[L]);
// if (L == 0) fprintf(stderr, "\n\n\t\tdeleting list 0\n\n");
LA[L] = NULL;
return NO_ERROR;
}
/*@null@*/
static struct link*
link_create(void* data)
{
struct link* l = (struct link *) malloc(sizeof(struct link));
if (l == NULL)
return NULL;
l->data = data; l->prev = NULL; l->next = NULL;
return l;
}
static list_error
list_add_head_internal(list_t L, void* data)
{
struct link* l = NULL;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
l = link_create(data);
if (l == NULL)
return NO_MEM;
if (LA[L]->first != NULL) {
l->next = LA[L]->first; l->next->prev = l; LA[L]->first = l;
} else { /* L is empty */
assert(LA[L]->last == NULL);
LA[L]->first = l; LA[L]->last = l;
}
LA[L]->nb_of_elt++;
if (LA[L]->currentpos)
LA[L]->currentpos++;
return NO_ERROR;
}
list_error
list_add_head(list_t L, void* data)
{
list_error e, el;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
el = list_lock(L);
if (el != NO_ERROR)
return el;
e = list_add_head_internal(L, data);
el = list_unlock(L);
assert(el == NO_ERROR);
return e;
}
static list_error
list_add_tail_internal(list_t L, void* data)
{
struct link* l = NULL;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
l = link_create(data);
if (l == NULL)
return NO_MEM;
if (LA[L]->last != NULL) {
l->prev = LA[L]->last; l->prev->next = l; LA[L]->last = l;
} else { /* L is empty */
assert(LA[L]->first == NULL);
LA[L]->first = l; LA[L]->last = l;
}
LA[L]->nb_of_elt++;
return NO_ERROR;
}
list_error
list_add_tail(list_t L, void* data)
{
list_error e, el;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
el = list_lock(L);
if (el != NO_ERROR)
return el;
e = list_add_tail_internal(L, data);
el = list_unlock(L);
assert(el == NO_ERROR);
return e;
}
list_error
list_add_at_pos(list_t L, unsigned int pos, void* data)
{
list_error ret, eunl;
struct link* l = NULL;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
l = link_create(data);
if (l == NULL)
return NO_MEM;
if (list_lock(L) != NO_ERROR) {
free(l);
return MUTEX_ERROR_LOCK;
}
if (pos) {
if (pos > LA[L]->nb_of_elt) {
ret = ERR_NO_SUCH_POS;
} else {
if (pos == LA[L]->nb_of_elt) {
ret = list_add_tail_internal(L, data);
} else {
unsigned int i = 1;
struct link* current = LA[L]->first->next;
for (i = 1; i != pos; i++) {
current = current->next;
}
current->prev->next = l;
l->prev = current->prev;
current->prev = l;
l->next = current;
LA[L]->nb_of_elt++;
ret = NO_ERROR;
}
}
} else {
ret = list_add_head_internal(L, data);
}
if (pos && pos <= LA[L]->currentpos)
LA[L]->currentpos++;
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return ret;
}
static void
list_del_head_internal(list_t L, bool del)
{
struct link* l = NULL;
if (list_vrfy(L) != 0)
return;
l = LA[L]->first;
if (l != NULL) {
LA[L]->first = l->next;
if (LA[L]->nb_of_elt == 1)
LA[L]->last = NULL;
else
LA[L]->first->prev = NULL;
if (LA[L]->lcurrent == l)
LA[L]->lcurrent = LA[L]->first;
if (del) {
free(l->data);
l->data = NULL;
}
free(l);
LA[L]->nb_of_elt--;
if (LA[L]->currentpos)
LA[L]->currentpos--;
}
}
list_error
list_del_head(list_t L)
{
list_error ret, eunl;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
ret = list_lock(L);
if (ret != NO_ERROR)
return ret;
list_del_head_internal(L, true);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return NO_ERROR;
}
static void
list_del_tail_internal(list_t L, bool del)
{
struct link* l = NULL;
if (list_vrfy(L) != 0)
return;
l = LA[L]->last;
if (l != NULL) {
LA[L]->last = l->prev;
if (LA[L]->nb_of_elt == 1)
LA[L]->first = NULL;
else
LA[L]->last->next = NULL;
if (LA[L]->lcurrent == l)
LA[L]->lcurrent = NULL;
if (del) {
free(l->data);
l->data = NULL;
}
free(l);
LA[L]->nb_of_elt--;
if (LA[L]->currentpos == LA[L]->nb_of_elt - 1)
LA[L]->currentpos = 0;
}
}
list_error
list_del_tail(list_t L)
{
list_error ret, eunl;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
ret = list_lock(L);
if (ret != NO_ERROR)
return ret;
list_del_tail_internal(L, true);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return NO_ERROR;
}
list_error
list_remove_tail(list_t L)
{
list_error ret, eunl;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
ret = list_lock(L);
if (ret != NO_ERROR)
return ret;
list_del_tail_internal(L, false);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return NO_ERROR;
}
list_error
list_del_at_pos_int(list_t L, unsigned int pos, bool del)
{
list_error ret, eunl;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
if (list_lock(L) != NO_ERROR)
return MUTEX_ERROR_LOCK;
if (pos) {
if (pos >= LA[L]->nb_of_elt) {
ret = ERR_NO_SUCH_POS;
} else {
if (pos == LA[L]->nb_of_elt - 1) {
list_del_tail_internal(L, del);
ret = NO_ERROR;
} else { /* not the first, not the last... */
unsigned int i = 1;
struct link* current = LA[L]->first;
for (i = 1; i <= pos; i++) {
current = current->next;
}
current->prev->next = current->next;
current->next->prev = current->prev;
if (LA[L]->lcurrent == current)
LA[L]->lcurrent = current->next;
if (del) {
free(current->data);
current->data = NULL;
}
free(current);
LA[L]->nb_of_elt--;
ret = NO_ERROR;
if (LA[L]->currentpos > pos)
LA[L]->currentpos--;
}
}
} else {
list_del_head_internal(L, del);
ret = NO_ERROR;
}
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return ret;
}
list_error list_del_at_pos(list_t l, unsigned int pos)
{
return list_del_at_pos_int(l, pos, true);
}
list_error list_remove_at_pos(list_t l, unsigned int pos)
{
return list_del_at_pos_int(l, pos, false);
}
static inline unsigned int
list_size_internal(list_t L)
{
return LA[L]->nb_of_elt;
}
int
list_size(list_t L)
{
int ret = 0;
list_error eunl;
if (list_vrfy(L) != 0)
return -1;
if (list_lock(L) != NO_ERROR)
return -1;
ret = (int) list_size_internal(L); /** \bug unsigned --> signed */
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return ret;
}
/*@null@*/
static inline void*
list_get_head_internal(list_t L)
{
if (LA[L]->first == NULL)
return NULL;
return LA[L]->first->data;
}
/*@null@*/
void*
list_get_head(list_t L)
{
void* ret = NULL;
list_error eunl;
if (list_vrfy(L) != 0)
return NULL;
if (list_lock(L) != NO_ERROR)
return NULL;
ret = list_get_head_internal(L);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return ret;
}
/*@null@*/
static inline void*
list_get_tail_internal(list_t L)
{
if (LA[L]->last == NULL)
return NULL;
return LA[L]->last->data;
}
/*@null@*/
void*
list_get_tail(list_t L)
{
void* ret = NULL;
list_error eunl;
if (list_vrfy(L) != 0)
return NULL;
if (list_lock(L) != NO_ERROR)
return NULL;
ret = list_get_tail_internal(L);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return ret;
}
/*@null@*/
void*
list_get_at_pos(list_t L, unsigned int pos)
{
list_error eunl;
void* ret;
if (list_vrfy(L) != 0)
return NULL;
if (list_lock(L) != NO_ERROR)
return NULL;
if (pos) {
if (pos >= LA[L]->nb_of_elt) {
ret = NULL;
} else {
if (pos == LA[L]->nb_of_elt - 1) {
ret = list_get_tail_internal(L);
} else {
unsigned int i = 0;
struct link* current = LA[L]->first;
for (i = 0; i != pos; i++) {
current = current->next;
}
ret = current->data;
}
}
} else {
ret = list_get_head_internal(L);
}
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return ret;
}
/*@null@*/
void*
list_pop(list_t L)
{
list_error eunl;
void* ret = NULL;
struct link* l = NULL;
if (list_vrfy(L) != 0)
return NULL;
if (list_lock(L) != NO_ERROR)
return NULL;
l = LA[L]->last;
if (l != NULL) {
LA[L]->last = l->prev;
if (l->prev != NULL)
l->prev->next = NULL;
if (LA[L]->nb_of_elt == 1)
LA[L]->first = LA[L]->last; /* = NULL */
if (LA[L]->lcurrent == l)
LA[L]->lcurrent = NULL;
ret = l->data;
l->data = NULL;
free(l);
LA[L]->nb_of_elt--;
if (LA[L]->currentpos == list_size_internal(L) - 1)
LA[L]->currentpos = 0;
}
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return ret;
}
list_error
list_search(list_t L, void* elt, int (eq(void*,void*)), unsigned int* position)
{
unsigned int i = 0;
list_error eunl, ret = NO_FOUND;
struct link* li;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
if (list_lock(L) != NO_ERROR)
return MUTEX_ERROR_LOCK;
li = LA[L]->first;
for (i = 0; i < LA[L]->nb_of_elt; i++) {
if (eq(li->data, elt)) {
*position = i;
ret = NO_ERROR;
break;
}
li = li->next;
}
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return ret;
}
/*@null@*/
static void*
list_current_reset_internal(list_t L)
{
LA[L]->lcurrent = LA[L]->first;
LA[L]->currentpos = 0;
if (LA[L]->lcurrent != NULL)
return LA[L]->lcurrent->data;
return NULL;
}
/*@null@*/
void*
list_current_reset(list_t L)
{
void* elt = NULL;
list_error eunl;
if (list_vrfy(L) != 0)
return NULL;
if (list_lock(L) != NO_ERROR)
return NULL;
elt = list_current_reset_internal(L);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return elt;
}
/*@null@*/
static inline void*
list_next_internal(list_t L)
{
if (LA[L]->lcurrent == NULL)
return NULL;
LA[L]->lcurrent = LA[L]->lcurrent->next;
LA[L]->currentpos++;
if (LA[L]->lcurrent != NULL)
return LA[L]->lcurrent->data;
return NULL;
}
/*@null@*/
static inline void*
list_previous_internal(list_t L)
{
if (LA[L]->lcurrent == NULL)
return NULL;
LA[L]->lcurrent = LA[L]->lcurrent->prev;
LA[L]->currentpos--;
if (LA[L]->lcurrent != NULL)
return LA[L]->lcurrent->data;
LA[L]->currentpos = 0;
return NULL;
}
/*@null@*/
void*
list_next(list_t L)
{
void* elt = NULL;
list_error eunl;
if (list_vrfy(L) != 0)
return NULL;
if (list_lock(L) != NO_ERROR)
return NULL;
if (list_size_internal(L) != 0) {
elt = list_next_internal(L);
}
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return elt;
}
/*@null@*/
void*
list_current(list_t L)
{
void* elt = NULL;
list_error eunl;
if (list_vrfy(L) != 0)
return NULL;
if (list_lock(L) != NO_ERROR)
return NULL;
if (list_size_internal(L) != 0)
if (LA[L]->lcurrent != NULL)
elt = LA[L]->lcurrent->data;
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return elt;
}
/*@null@*/
void*
list_previous(list_t L)
{
void* elt = NULL;
list_error eunl;
if (list_vrfy(L) != 0)
return NULL;
if (list_lock(L) != NO_ERROR)
return NULL;
if (list_size_internal(L) == 0)
return NULL;
elt = list_previous_internal(L);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return elt;
}
list_error
list_current_setpos(list_t L, unsigned int position)
{
struct link* next = NULL;
unsigned int i = 0;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
if (LA[L]->nb_of_elt <= position)
return NO_FOUND;
next = LA[L]->first;
for (i = 0; i < position; i++) {
if (next == NULL)
return INTERNAL_ERROR;
next = next->next;
}
LA[L]->currentpos = position;
if (next == NULL)
return INTERNAL_ERROR;
LA[L]->lcurrent = next;
return NO_ERROR;
}
unsigned int
list_current_getpos(list_t L)
{
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
if ((int) LA[L]->currentpos >= list_size(L))
return 0;
return LA[L]->currentpos;
}
static void
list_current_del_internal(list_t L, bool del)
{
struct link* next;
if (LA[L]->lcurrent == NULL)
return;
if (LA[L]->lcurrent == LA[L]->last) {
list_del_tail_internal(L, del);
return;
}
if (LA[L]->lcurrent == LA[L]->first) {
list_del_head_internal(L, del);
return;
}
LA[L]->lcurrent->prev->next = LA[L]->lcurrent->next;
LA[L]->lcurrent->next->prev = LA[L]->lcurrent->prev;
LA[L]->nb_of_elt--;
next = LA[L]->lcurrent->next;
if (del) {
free(LA[L]->lcurrent->data);
LA[L]->lcurrent->data = NULL;
}
free(LA[L]->lcurrent);
LA[L]->lcurrent = next;
}
list_error
list_current_del(list_t L)
{
list_error eunl;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
if (list_size_internal(L) == 0)
return ERR_NO_SUCH_POS;
if (list_lock(L) != NO_ERROR)
return MUTEX_ERROR_LOCK;
list_current_del_internal(L, true);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return NO_ERROR;
}
list_error
list_current_remove(list_t L)
{
list_error eunl;
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
if (list_size_internal(L) == 0)
return ERR_NO_SUCH_POS;
if (list_lock(L) != NO_ERROR)
return MUTEX_ERROR_LOCK;
list_current_del_internal(L, false);
eunl = list_unlock(L);
assert(eunl == NO_ERROR);
return NO_ERROR;
}
#ifdef WITH_DATALOC
list_error
list_pack(list_t L, struct dataloc* loc, int eltsize,
int (*cbeltwrite)(struct dataloc* loc, const void* elt))
{/**\bug we don't take the mutex. */
if (list_vrfy(L) != 0)
return NO_SUCH_LIST;
if (eltsize <= 0 && cbeltwrite == NULL)
return BAD_PARAMETERS;
if (eltsize > 0 && cbeltwrite != NULL)
return BAD_PARAMETERS;
if (loc == NULL)
return BAD_PARAMETERS;
int err = writeloc(loc, &LA[L]->nb_of_elt, sizeof LA[L]->nb_of_elt);
if (err < 0)
return IO_ERROR;
unsigned int count = 0;
unsigned int currentpos = 0;
struct link* currentsav = LA[L]->lcurrent;
struct link* next = LA[L]->first;
if (next != NULL) do {
if (cbeltwrite != NULL) {
assert(eltsize <= 0);
if (cbeltwrite(loc, next->data) < 0)
return IO_ERROR;
} else {
err = writeloc(loc, next->data, eltsize);
if (err < 0)
return IO_ERROR;
}
if (next == currentsav)
currentpos = count;
count++;
} while ((next = next->next) != NULL);
if (count != LA[L]->nb_of_elt)
return INTERNAL_ERROR;
{
uint8_t c = currentsav != NULL ? 1 : 0;
err = writeloc(loc, &c, sizeof c);
if (err < 0)
return IO_ERROR;
if (currentsav != NULL) {
assert(currentpos == LA[L]->currentpos);
int err = writeloc(loc, ¤tpos, sizeof currentpos);
if (err < 0)
return IO_ERROR;
}
}
return NO_ERROR;
}
list_t
list_unpack(struct dataloc* loc, list_error *e, int eltsize,
int (*cbeltread)(struct dataloc* loc, void** elt))
{
if (e == NULL)
return -1;
if (loc == NULL) {
*e = BAD_PARAMETERS;
return -1;
}
if (eltsize <= 0 && cbeltread == NULL) {
*e = BAD_PARAMETERS;
return -1;
}
if (eltsize > 0 && cbeltread != NULL) {
*e = BAD_PARAMETERS;
return -1;
}
list_t L = list_create();
if (L == (list_t) -1) {
*e = NO_MEM;
return -1;
}
unsigned int nbelt = 0;
int err = readloc(loc, &nbelt, sizeof nbelt);
if (err < 0) {
*e = IO_ERROR;
return -1;
}
for (unsigned int i = 0; i < nbelt; i++) {
void* elt = NULL;
if (cbeltread != NULL) {
assert(eltsize <= 0);
err = cbeltread(loc, &elt);
if (err < 0) {
*e = IO_ERROR;
return -1;
}
} else {
elt = calloc(1, eltsize);
if (elt == NULL) {
*e = NO_MEM;
return -1;
}
err = readloc(loc, elt, eltsize);
if (err < 0) {
*e = IO_ERROR;
free(elt);
return -1;
}
}
list_error lerr = list_add_tail(L, elt);
if (lerr != NO_ERROR) {
*e = lerr;
if (cbeltread == NULL)
free(elt);
/**\bug: if cbeltread != NULL, the elt it allocated
is not freed (we cannot, it could be an tree
structure or whatever... */
return -1;
}
}
if (nbelt != LA[L]->nb_of_elt) {
*e = INTERNAL_ERROR;
return -1;
}
{
uint8_t iscurrent = 0;
int err = readloc(loc, &iscurrent, sizeof iscurrent);
if (err < 0 || (err == 0 && iscurrent != 0 && iscurrent != 1)) {
*e = IO_ERROR;
return -1;
}
if (iscurrent == 1) {
unsigned int currentpos = 0;
err = readloc(loc, ¤tpos, sizeof currentpos);
if (err < 0) {
*e = IO_ERROR;
return -1;
}
list_error lerr = list_current_setpos(L, currentpos);
if (lerr != NO_ERROR)
return lerr;
}
}
return L;
}
#endif /* WITH_DATALOC */