#include "ll.h" #include "shared.h" #include #include #include #include #include typedef struct dll { void *data; dll_freecb freecb; struct dll *next; struct dll *prev; } dllnode; typedef struct dlinked { int size; dllnode *start; dllnode *end; } dlinkedlist; dllnode * dllnode_init(void *data, dll_freecb fcb) { dllnode *n = xcalloc(1, sizeof(*n)); n->data = data; n->freecb = fcb; n->prev = NULL; n->next = NULL; return n; } dlinkedlist * dlinkedlist_init(void) { dlinkedlist *ll = xcalloc(1, sizeof(*ll)); ll->end = NULL; ll->start = NULL; ll->size = 0; return ll; } void dlinkedlist_free(dlinkedlist *ll) { if(!ll) return; for(dllnode *current = ll->start, *next; current != NULL; ) { next = current->next; // This used to be a function but was causing issues for some inscrutable reason if(current->freecb != NULL) current->freecb(current->data); free(current); current = next; } free(ll); return; } int dlinkedlist_size(const dlinkedlist * const ll) { if(!ll) RETURNWERR(EINVAL, -1); return ll->size; } int dlinkedlist_handlefirstnode(dlinkedlist * const ll, void *data, dll_freecb fcb) { if(!ll) RETURNWERR(EINVAL, 1); if(ll->size > 0) return 1; // Need to handle adding the first element differently than other opers, so do it here dllnode *n = dllnode_init(data, fcb); // dllnode_init already sets n.prev and n.next to null, so no need to do so again ll->end = n; ll->start = n; ll->size = 1; return 0; } int dlinkedlist_xxxend(dlinkedlist * const ll, void *data, dll_freecb fcb, char op) { if(!ll) RETURNWERR(EINVAL, 1); if(op != 'a' && op != 'p') RETURNWERR(EINVAL, 1); if(dlinkedlist_handlefirstnode(ll, data, fcb) == 0) return 0; dllnode *n = dllnode_init(data, fcb); switch (op) { case 'a': n->prev = (ll->end); (ll->end)->next = n; ll->end = n; break; case 'p': n->next = (ll->start); (ll->start)->prev = n; ll->start = n; break; default: XALLOC_EXIT(" got an invalid op token when it shouldn't be possible to recieve one", ); // Technically not an alloc, but also there's no reason I can't reuse a perfectly good macro } ll->size++; return 0; } int dlinkedlist_append(dlinkedlist * const ll, void *data, dll_freecb fcb) { return dlinkedlist_xxxend(ll, data, fcb, 'a'); } int dlinkedlist_prepend(dlinkedlist * const ll, void *data, dll_freecb fcb) { return dlinkedlist_xxxend(ll, data, fcb, 'p'); } dllnode * dlinkedlist_getnode(const dlinkedlist * const ll, int index) { if(!ll) RETURNWERR(EINVAL, NULL); if(index < 0 || index >= ll->size) RETURNWERR(EINVAL, NULL); if(index == 0) return ll->start; if(index == (ll->size - 1)) return ll->end; int spoint = (index <= (ll->size / 2)); dllnode *p = (spoint) ? ll->start : ll->end; int i = (spoint) ? 0 : ll->size - 1; // This should (theoretically) be faster on average for(; p != NULL && ((spoint) ? (i < index) : (i > index)); (spoint) ? i++ : i--, p = (spoint) ? p->next : p->prev); return p; } int dlinkedlist_insert(dlinkedlist * const ll, void *data, dll_freecb fcb, int index) { if(!ll) RETURNWERR(EINVAL, 1); if(index < 0 || index >= ll->size) RETURNWERR(EINVAL, 1); // Handle the special cases of appending or prepending if(index == 0) return dlinkedlist_prepend(ll, data, fcb); if(index == (ll->size - 1)) return dlinkedlist_append(ll, data, fcb); // Insert between 2 nodes // If you're inserting at index 2, the new node becomes index 2, and the previous node at index 2 moves to index 3 dllnode *new = dllnode_init(data, fcb); dllnode *current = dlinkedlist_getnode(ll, index); if(!current) XALLOC_EXIT(" somehow managed to pull a null node from dlinkedlist_getnode(... , ... , ... , %d)", , (index)); (current->prev)->next = new; new->prev = (current->prev); new->next = current; current->prev = new; ll->size++; return 0; } int dlinkedlist_remove(dlinkedlist * const ll, int index) { if(!ll) RETURNWERR(EINVAL, 1); if(index < 0 || index >= ll->size) RETURNWERR(EINVAL, 2); dllnode *current = dlinkedlist_getnode(ll, index); if(!current) abort(); if(index == 0) { ll->start = current->next; ll->start->prev = NULL; } else if(index == (ll->size - 1)) { ll->end = current->prev; ll->end->next = NULL; } else { current->prev->next = current->next; current->next->prev = current->prev; } if(current->freecb != NULL) current->freecb(current->data); free(current); ll->size--; return 0; } void* dlinkedlist_get(const dlinkedlist * const ll, int index) { dllnode *n = dlinkedlist_getnode(ll, index); if(!n) return NULL; return n->data; } int dlinkedlist_foreach(dlinkedlist *ll, int (*callback)(void*)) { if(!ll) RETURNWERR(EINVAL, -1); if(!callback) RETURNWERR(EINVAL, -1); for(dllnode *p = ll->start; p != NULL; p = p->next) callback(p->data); return 0; }