/* * 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 */