/* * This file is part of corelibs. (https://git.redxen.eu/corelibs) * Copyright (c) 2021 Alex-David Denes * * corelibs is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * corelibs is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with corelibs. If not, see . */ #include "llist.h" #include // va_args #include // uintmax_t #include // malloc() free() static llist_err corelibs_llist_create(void*, void (*)(void*), llist_t**); static llist_err corelibs_llist_link(uintmax_t cnt, ...); static llist_err corelibs_llist_free_list(llist_t*); static llist_err corelibs_llist_free_elem(llist_t*); static llist_err corelibs_llist_set_cont(llist_t*, void*); static llist_err corelibs_llist_set_free(llist_t*, void (*)(void*)); static llist_err corelibs_llist_get_next(const llist_t*, llist_t**); static llist_err corelibs_llist_get_prev(const llist_t*, llist_t**); static llist_err corelibs_llist_get_cont(const llist_t*, void**); static void corelibs_llist_remove(llist_t*); struct llist_t { struct llist_t *next, *prev; void* cont; void (*free)(void*); }; enum { // Avoid collision in error numbers CORELIBS_LLIST_ERR_MARGS = -4, CORELIBS_LLIST_ERR_UNDEF, CORELIBS_LLIST_ERR_NULL, CORELIBS_LLIST_ERR_ALLOC, CORELIBS_LLIST_ERR_UNK = 0, CORELIBS_LLIST_ERR_OK = 1, }; struct corelibs_llist_interface const llist = { .create = corelibs_llist_create, .link = corelibs_llist_link, .free = { .list = corelibs_llist_free_list, .elem = corelibs_llist_free_elem, }, .set = { .cont = corelibs_llist_set_cont, .free = corelibs_llist_set_free, }, .get = { .prev = corelibs_llist_get_prev, .cont = corelibs_llist_get_cont, .next = corelibs_llist_get_next, }, .err = { .ok = CORELIBS_LLIST_ERR_OK, .unknown = CORELIBS_LLIST_ERR_UNK, .undef = CORELIBS_LLIST_ERR_UNDEF, .mem = { .null = CORELIBS_LLIST_ERR_NULL, .alloc = CORELIBS_LLIST_ERR_ALLOC, }, .args = { .mis = CORELIBS_LLIST_ERR_MARGS, }, }, }; static llist_err corelibs_llist_create(void* c, void (*const f)(void*), llist_t** s) { llist_err err = CORELIBS_LLIST_ERR_OK; llist_t* cur = malloc(sizeof(*cur)); if (cur == NULL) { err = CORELIBS_LLIST_ERR_ALLOC; goto ret; } cur->prev = NULL; cur->next = NULL; cur->cont = c; cur->free = f; *s = cur; ret: return err; } static llist_err corelibs_llist_free_list(llist_t* l) { llist_err err = CORELIBS_LLIST_ERR_OK; if (l == NULL) { err = CORELIBS_LLIST_ERR_NULL; goto ret; } // Free previous elements if we have any if (l->prev != NULL) { for (llist_t* p = l->prev; p != NULL;) { llist_t* c = p; p = p->prev; corelibs_llist_remove(c); } } // Free current and next elements for (llist_t* p = l; p != NULL;) { llist_t* c = p; p = p->next; corelibs_llist_remove(c); } ret: return err; } static llist_err corelibs_llist_free_elem(llist_t* l) { llist_err err = CORELIBS_LLIST_ERR_OK; if (l == NULL) { err = CORELIBS_LLIST_ERR_NULL; goto ret; } corelibs_llist_remove(l); ret: return err; } static llist_err corelibs_llist_link(uintmax_t cnt, ...) { llist_err err = CORELIBS_LLIST_ERR_OK; if (cnt <= 1) { err = CORELIBS_LLIST_ERR_MARGS; goto ret; } va_list ap; va_start(ap, cnt); uintmax_t ccnt = 0; llist_t * a = NULL, *b = NULL; while (ccnt < cnt) { a = b; b = va_arg(ap, llist_t*); if (ccnt < 1) continue; // Fill at least first 2 slots if (a != NULL) a->next = b; if (b != NULL) b->prev = a; ccnt++; } va_end(ap); ret: return err; } static llist_err corelibs_llist_get_next(const llist_t* e, llist_t** save) { llist_err err = CORELIBS_LLIST_ERR_OK; if (e == NULL || save == NULL) { err = CORELIBS_LLIST_ERR_NULL; goto ret; } if (e->next == NULL) { err = CORELIBS_LLIST_ERR_UNDEF; goto ret; } *save = e->next; ret: return err; } static llist_err corelibs_llist_get_prev(const llist_t* e, llist_t** save) { llist_err err = CORELIBS_LLIST_ERR_OK; if (e == NULL || save == NULL) { err = CORELIBS_LLIST_ERR_NULL; goto ret; } if (e->prev == NULL) { err = CORELIBS_LLIST_ERR_UNDEF; goto ret; } *save = e->prev; ret: return err; } static llist_err corelibs_llist_get_cont(const llist_t* e, void** save) { llist_err err = CORELIBS_LLIST_ERR_OK; if (e == NULL || save == NULL) { err = CORELIBS_LLIST_ERR_NULL; goto ret; } *save = e->cont; ret: return err; } static llist_err corelibs_llist_set_cont(llist_t* e, void* i) { llist_err err = CORELIBS_LLIST_ERR_OK; if (e == NULL) { err = CORELIBS_LLIST_ERR_NULL; goto ret; } e->cont = i; ret: return err; } static llist_err corelibs_llist_set_free(llist_t* e, void (*const f)(void*)) { llist_err err = CORELIBS_LLIST_ERR_OK; if (e == NULL) { err = CORELIBS_LLIST_ERR_NULL; goto ret; } e->free = f; ret: return err; } // Private function static void corelibs_llist_remove(llist_t* e) { corelibs_llist_link(2, e->prev, e->next); if (e->free != NULL) e->free(e->cont); free(e); }