/* llist.c - Linked list functions * * Linked list structures have a next pointer as their first element. */ #include "toys.h" // Callback function to free data pointer of double_list or arg_list void llist_free_arg(void *node) { struct arg_list *d = node; free(d->arg); free(d); } void llist_free_double(void *node) { struct double_list *d = node; free(d->data); free(d); } // Call a function (such as free()) on each element of a linked list. void llist_traverse(void *list, void (*using)(void *node)) { void *old = list; while (list) { void *pop = llist_pop(&list); using(pop); // End doubly linked list too. if (old == list) break; } } // Return the first item from the list, advancing the list (which must be called // as &list) void *llist_pop(void *list) { // I'd use a void ** for the argument, and even accept the typecast in all // callers as documentation you need the &, except the stupid compiler // would then scream about type-punned pointers. Screw it. void **llist = (void **)list; void **next = (void **)*llist; *llist = *next; return (void *)next; } // Remove first item from &list and return it void *dlist_pop(void *list) { struct double_list **pdlist = (struct double_list **)list, *dlist = *pdlist; if (!dlist) return 0; if (dlist->next == dlist) *pdlist = 0; else { if (dlist->next) dlist->next->prev = dlist->prev; if (dlist->prev) dlist->prev->next = dlist->next; *pdlist = dlist->next; } return dlist; } // remove last item from &list and return it (stack pop) void *dlist_lpop(void *list) { struct double_list *dl = *(struct double_list **)list; void *v = 0; if (dl) { dl = dl->prev; v = dlist_pop(&dl); if (!dl) *(void **)list = 0; } return v; } // Append to list in-order (*list unchanged unless empty, ->prev is new node) void dlist_add_nomalloc(struct double_list **list, struct double_list *new) { if (*list) { new->next = *list; new->prev = (*list)->prev; (*list)->prev->next = new; (*list)->prev = new; } else *list = new->next = new->prev = new; } // Add an entry to the end of a doubly linked list struct double_list *dlist_add(struct double_list **list, char *data) { struct double_list *new = xmalloc(sizeof(struct double_list)); new->data = data; dlist_add_nomalloc(list, new); return new; } // Terminate circular list for traversal in either direction. Returns end *. void *dlist_terminate(void *list) { struct double_list *end = list; if (!end || !end->prev) return 0; end = end->prev; end->next->prev = 0; end->next = 0; return end; } // Find num in cache struct num_cache *get_num_cache(struct num_cache *cache, long long num) { while (cache) { if (num==cache->num) return cache; cache = cache->next; } return 0; } // Uniquely add num+data to cache. Updates *cache, returns pointer to existing // entry if it was already there. struct num_cache *add_num_cache(struct num_cache **cache, long long num, void *data, int len) { struct num_cache *old = get_num_cache(*cache, num); if (old) return old; old = xzalloc(sizeof(struct num_cache)+len); old->next = *cache; old->num = num; memcpy(old->data, data, len); *cache = old; return 0; }