#include "ll.h" #include "shared.h" #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; } int dllnode_free(dllnode **n) { if(!n) RETURNWERR(EINVAL, 1); if(!(*n)) RETURNWERR(EINVAL, 1); if((*n)->freecb != NULL) (*n)->freecb((*n)->data); free(*n); *n = NULL; return 0; } dlinkedlist* dlinkedlist_init(void) { dlinkedlist *ll = xcalloc(1, sizeof(*ll)); ll->end = NULL; ll->start = NULL; ll->size = 0; return ll; } int dlinkedlist_free(dlinkedlist **ll) { if(!ll) RETURNWERR(EINVAL, 1); if(!(*ll)) RETURNWERR(EINVAL, 1); for(dllnode *n; (*ll)->start != NULL;) { n = (*ll)->start->next; dllnode_free(&((*ll)->start)); (*ll)->start = n; } free(*ll); *ll = NULL; return 0; } 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; } dllnode_free(¤t); 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; }