| /* |
| * dlist.c |
| * |
| * Copyright (C) 2003 Eric J Bohm |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 021110307 USA |
| * |
| */ |
| |
| |
| /* Double linked list implementation. |
| |
| * You allocate the data and give dlist the pointer. |
| * If your data is complex set the dlist->del_func to a an appropriate |
| * delete function. Otherwise dlist will just use free. |
| |
| */ |
| #include <stdlib.h> |
| #include "dlist.h" |
| |
| /* |
| * Return pointer to node at marker. |
| * else null if no nodes. |
| */ |
| |
| inline void *dlist_mark(Dlist *list) |
| { |
| if(list->marker!=NULL) |
| return(list->marker->data); |
| else |
| return(NULL); |
| } |
| |
| /* |
| * Set marker to start. |
| */ |
| |
| inline void dlist_start(Dlist *list) |
| { |
| list->marker=list->head; |
| } |
| |
| /* |
| * Set marker to end. |
| */ |
| |
| inline void dlist_end(Dlist *list) |
| { |
| list->marker=list->head; |
| } |
| |
| /* internal use function |
| * quickie inline to consolidate the marker movement logic |
| * in one place |
| * |
| * when direction true it moves marker after |
| * when direction false it moves marker before. |
| * return pointer to data at new marker |
| * if nowhere to move the marker in desired direction return null |
| */ |
| inline void *_dlist_mark_move(Dlist *list,int direction) |
| { |
| if(direction) |
| { |
| if( list->marker && list->marker->next!=NULL) |
| list->marker=list->marker->next; |
| else |
| return(NULL); |
| } |
| else |
| { |
| if( list->marker && list->marker->prev!=NULL) |
| list->marker=list->marker->prev; |
| else |
| return(NULL); |
| } |
| if(list->marker!=list->head) |
| return(list->marker->data); |
| else |
| return(NULL); |
| } |
| |
| /* |
| * Create new linked list to store nodes of datasize. |
| * return null if list cannot be created. |
| */ |
| Dlist *dlist_new(size_t datasize) |
| { |
| Dlist *list=NULL; |
| if((list=malloc(sizeof(Dlist)))) |
| { |
| list->marker=NULL; |
| list->count=0L; |
| list->data_size=datasize; |
| list->del_func=free; |
| list->head=&(list->headnode); |
| list->head->prev=NULL; |
| list->head->next=NULL; |
| list->head->data=NULL; |
| } |
| return(list); |
| } |
| |
| /* |
| * Create new linked list to store nodes of datasize set list |
| * data node delete function to the passed in del_func |
| * return null if list cannot be created. |
| */ |
| Dlist *dlist_new_with_delete(size_t datasize,void (*del_func)(void*)) |
| { |
| Dlist *list=NULL; |
| list=dlist_new(datasize); |
| if(list!=NULL) |
| list->del_func=del_func; |
| return(list); |
| } |
| |
| |
| /* |
| * remove marker node from list |
| * call data_delete function on data if registered. |
| * otherwise call free. |
| * when direction true it moves marker after |
| * when direction false it moves marker before. |
| * free marker node |
| * return nothing. |
| */ |
| void dlist_delete(Dlist *list,int direction) |
| { |
| if((list->marker != list->head)&&(list->marker!=NULL)) |
| { |
| DL_node *corpse; |
| corpse=list->marker; |
| _dlist_mark_move(list,direction); |
| if(list->head->next==corpse) |
| list->head->next=corpse->next; |
| if(list->head->prev==corpse) |
| list->head->prev=corpse->prev; |
| if(corpse->prev!=NULL) //should be impossible |
| corpse->prev->next=corpse->next; |
| if(corpse->next!=NULL) //should be impossible |
| corpse->next->prev=corpse->prev; |
| list->del_func(corpse->data); |
| list->count--; |
| free(corpse); |
| } |
| } |
| |
| /* |
| * Insert node containing data at marker. |
| * If direction true it inserts after. |
| * If direction false it inserts before. |
| * move marker to inserted node |
| * return pointer to inserted node |
| */ |
| void *dlist_insert(Dlist *list,void *data,int direction) |
| { |
| DL_node *new_node=NULL; |
| if(list==NULL || data==NULL) |
| return(NULL); |
| if(list->marker==NULL) //in case the marker ends up unset |
| list->marker=list->head; |
| if((new_node=malloc(sizeof(DL_node)))) |
| { |
| new_node->data=data; |
| new_node->prev=NULL; |
| new_node->next=NULL; |
| list->count++; |
| if(list->head->next==NULL) //no l |
| { |
| list->head->next=list->head->prev=new_node; |
| new_node->prev=list->head; |
| new_node->next=list->head; |
| } |
| else if(direction) |
| { |
| new_node->next=list->marker->next; |
| new_node->prev=list->marker; |
| list->marker->next->prev=new_node; |
| list->marker->next=new_node; |
| } |
| else |
| { |
| new_node->prev=list->marker->prev; |
| new_node->next=list->marker; |
| list->marker->prev->next=new_node; |
| list->marker->prev=new_node; |
| } |
| list->marker=new_node; |
| } |
| else |
| { |
| return(NULL); |
| } |
| return(list->marker->data); |
| } |
| |
| /* internal use only |
| * Insert dl_node at marker. |
| * If direction true it inserts after. |
| * If direction false it inserts before. |
| * move marker to inserted node |
| * return pointer to inserted node |
| */ |
| void *_dlist_insert_dlnode(struct dlist *list,struct dl_node *new_node,int direction) |
| { |
| if(list==NULL || new_node==NULL) |
| return(NULL); |
| if(list->marker==NULL) //in case the marker ends up unset |
| list->marker=list->head; |
| list->count++; |
| if(list->head->next==NULL) |
| { |
| list->head->next=list->head->prev=new_node; |
| new_node->prev=list->head; |
| new_node->next=list->head; |
| } |
| else if(direction) |
| { |
| new_node->next=list->marker->next; |
| new_node->prev=list->marker; |
| list->marker->next->prev=new_node; |
| list->marker->next=new_node; |
| } |
| else |
| { |
| new_node->prev=list->marker->prev; |
| new_node->next=list->marker; |
| list->marker->prev->next=new_node; |
| list->marker->prev=new_node; |
| } |
| list->marker=new_node; |
| return(list->marker); |
| } |
| |
| |
| |
| /* |
| * Remove DL_node from list without deallocating data. |
| * if marker == killme . |
| * when direction true it moves marker after |
| * when direction false it moves marker before. |
| * to previous if there is no next. |
| */ |
| void *_dlist_remove(Dlist *list,DL_node *killme,int direction) |
| { |
| if(killme!=NULL) |
| { |
| void *killer_data=killme->data; |
| // take care of head and marker pointers. |
| if(list->marker==killme) |
| _dlist_mark_move(list,direction); |
| if(killme ==list->head->next) |
| list->head->next=killme->next; |
| if(killme==list->head->prev) |
| list->head->prev=killme->prev; |
| // remove from list |
| if(killme->prev !=NULL) |
| killme->prev->next=killme->next; |
| if(killme->next !=NULL) |
| killme->next->prev=killme->prev; |
| list->count--; |
| free(killme); |
| return(killer_data); |
| } |
| else |
| return (NULL); |
| } |
| |
| /* |
| * move dl_node from source to dest |
| * if marker == target . |
| * when direction true it moves marker after |
| * when direction false it moves marker before. |
| * to previous if there is no next. |
| */ |
| void dlist_move(struct dlist *source, struct dlist *dest, struct dl_node *target,int direction) |
| { |
| |
| if(target!=NULL) |
| { |
| if(target==source->head) |
| { |
| //not even going to try |
| } |
| else |
| { |
| // take care of head and marker pointers. |
| if(source->marker==target) |
| _dlist_mark_move(source,direction); |
| if(target ==source->head->next) |
| source->head->next=target->next; |
| if(target==source->head->prev) |
| source->head->prev=target->prev; |
| // remove from list |
| if(source->count==1) |
| { |
| target->prev=NULL; |
| target->next=NULL; |
| source->head->next=NULL; |
| source->head->prev=NULL; |
| } |
| else |
| { |
| if(target->prev !=NULL) |
| target->prev->next=target->next; |
| if(target->next !=NULL) |
| target->next->prev=target->prev; |
| target->prev=NULL; |
| target->next=NULL; |
| } |
| source->count--; |
| _dlist_insert_dlnode(dest,target,direction); |
| } |
| } |
| } |
| |
| |
| /* |
| * Insert node containing data after end. |
| */ |
| void dlist_push(Dlist *list,void *data) |
| { |
| list->marker=list->head->prev; |
| dlist_insert(list,data,1); |
| } |
| |
| /* |
| * Insert node containing data at start. |
| */ |
| |
| void dlist_unshift(Dlist *list,void *data) |
| |
| { |
| list->marker=list->head->next; |
| dlist_insert(list,data,0); |
| } |
| |
| void dlist_unshift_sorted(Dlist *list, void *data, |
| int (*sorter)(void *new_elem, void *old_elem)) |
| { |
| if (list->count == 0) |
| dlist_unshift(list, data); |
| else { |
| list->marker=list->head->next; |
| dlist_insert_sorted(list, data, sorter); |
| } |
| } |
| |
| /* |
| * Remove end node from list. |
| * Return pointer to data in removed node. |
| * Null if no nodes. |
| */ |
| |
| void *dlist_pop(Dlist *list) |
| { |
| return(_dlist_remove(list,list->head->prev,0)); |
| } |
| |
| /* |
| * Remove start node from list. |
| * Return pointer to data in removed node. |
| * Null if no nodes. |
| */ |
| |
| void *dlist_shift(Dlist *list) |
| { |
| return(_dlist_remove(list,list->head->next,1)); |
| } |
| |
| |
| /* |
| * destroy the list freeing all memory |
| */ |
| |
| |
| void dlist_destroy(Dlist *list) |
| { |
| if(list !=NULL) |
| { |
| dlist_start(list); |
| dlist_next(list); |
| while (dlist_mark(list)) { |
| dlist_delete(list,1); |
| } |
| free(list); |
| } |
| } |
| |
| /** |
| * Return void pointer to list_data element matching comp function criteria |
| * else null |
| * Does not move the marker. |
| */ |
| |
| void *dlist_find_custom(struct dlist *list, void *target, int (*comp)(void *, void *)) |
| { |
| /* test the comp function on each node */ |
| struct dl_node *nodepointer; |
| dlist_for_each_nomark(list,nodepointer) |
| if(comp(target,nodepointer->data)) |
| return(nodepointer->data); |
| return(NULL); |
| } |
| |
| /** |
| * Apply the node_operation function to each data node in the list |
| */ |
| void dlist_transform(struct dlist *list, void (*node_operation)(void *)) |
| { |
| struct dl_node *nodepointer; |
| dlist_for_each_nomark(list,nodepointer) |
| node_operation(nodepointer->data); |
| } |
| |
| /** |
| * insert new into list in sorted order |
| * sorter function in form int sorter(new,ith) |
| * must return 1 for when new should go before ith |
| * else 0 |
| * return pointer to inserted node |
| * NOTE: assumes list is already sorted |
| */ |
| void *dlist_insert_sorted(struct dlist *list, void *new, int (*sorter)(void *, void *)) |
| { |
| for(dlist_start(list),dlist_next(list); \ |
| list->marker!=list->head && !sorter(new,list->marker->data);dlist_next(list)); |
| return(dlist_insert_before(list,new)); |
| } |
| |
| /* |
| * NOTE: internal use only |
| */ |
| int _dlist_merge(struct dlist *listsource, struct dlist *listdest, unsigned int passcount, int (*compare)(void *, void *)) |
| { |
| |
| struct dl_node *l1head; |
| struct dl_node *l2head; |
| struct dl_node *target; |
| unsigned int l1count=0; |
| unsigned int l2count=0; |
| unsigned int mergecount=0; |
| while(listsource->count>0) |
| { |
| l1head=listsource->head->next; |
| l2head=l1head; |
| while((l1count<passcount)&&(l2head!=listsource->head)) |
| { |
| l2head=l2head->next; |
| l1count++; |
| } |
| // so now we have two lists to merge |
| |
| if(l2head==listsource->head) |
| {// l2count |
| l2count=0; |
| } |
| else |
| { |
| l2count=passcount; |
| } |
| while(l1count>0 || l2count>0) |
| { |
| mergecount++; |
| if((l2count>0)&&(l1count>0)) |
| { |
| // we have things to merge |
| int result=compare(l1head->data,l2head->data); |
| if(result>0) |
| { |
| // move from l2 |
| target=l2head; |
| l2head=l2head->next; |
| dlist_move(listsource,listdest,target,1); |
| l2count--; |
| if(l2head==listsource->head) |
| l2count=0; |
| } |
| else |
| { |
| // move from l1 |
| target=l1head; |
| l1head=l1head->next; |
| dlist_move(listsource,listdest,target,1); |
| l1count--; |
| } |
| } |
| else if(l1count>0) |
| { |
| // only have l1 to work with |
| while(l1count>0) |
| { |
| target=l1head; |
| l1head=l1head->next; |
| dlist_move(listsource,listdest,target,1); |
| l1count--; |
| } |
| } |
| else if(l2count>0) |
| { |
| // only have l2 to work with |
| while(l2count>0) |
| { |
| if(l2head==listsource->head) |
| { |
| l2count=0; |
| } |
| else |
| { |
| target=l2head; |
| l2head=l2head->next; |
| dlist_move(listsource,listdest,target,1); |
| l2count--; |
| } |
| } |
| } |
| else |
| { //nothing left and this should be unreachable |
| } |
| } |
| } |
| return(mergecount); |
| } |
| |
| /** |
| * mergesort the list based on compare |
| * compare function in form int sorter(void * a,void * b) |
| * must return >0 for a after b |
| * must return <0 for a before b |
| * else 0 |
| |
| * NOTE: mergesort changes the mark pointer |
| */ |
| void dlist_sort_custom(struct dlist *list, int (*compare)(void *, void *)) |
| { |
| |
| struct dlist *listsource, *listdest, *swap; |
| struct dlist *templist; |
| unsigned int passcount = 1; |
| unsigned int mergecount = 1; |
| |
| if(list->count<2) |
| return; |
| |
| dlist_start(list); |
| templist = dlist_new(list->data_size); |
| |
| templist->del_func = list->del_func; |
| |
| // do nothing if there isn't anything to sort |
| listsource = list; |
| listdest = templist; |
| while(mergecount>0) |
| { |
| mergecount=_dlist_merge(listsource, listdest, passcount, compare); |
| if(mergecount>1) |
| { |
| passcount=passcount*2; |
| //start new pass |
| swap=listsource; |
| listsource=listdest; |
| listdest=swap; |
| } |
| } |
| // now put the input list pointers right |
| // list pointers = newlist pointers |
| // including the forward and next nodes prev and back pointers |
| if(list->count==0) |
| {//copy |
| list->marker = listdest->marker; |
| list->count = listdest->count; |
| list->data_size = listdest->data_size; |
| list->del_func = listdest->del_func; |
| list->head->prev = listdest->head->prev; |
| list->head->next = listdest->head->next; |
| list->head->data = listdest->head->data; |
| list->head->next->prev=list->head; |
| list->head->prev->next=list->head; |
| templist->head->next=NULL; |
| templist->head->prev=NULL; |
| templist->count=0; |
| } |
| else |
| {// no need to copy |
| |
| } |
| |
| dlist_destroy(templist); |
| } |
| |
| /* |
| * The dlist_filter_sort() function scans the list, calling |
| * filter() on each list entry. Entries for which filter() |
| * returns zero are discarded from the list. Then the list is |
| * sorted using mergesort() with the comparison function compare() |
| * and returned. If filter is NULL, all entries are selected. |
| */ |
| void dlist_filter_sort(struct dlist *list, int (*filter) (void *), |
| int (*compare) (void *, void *)) |
| { |
| struct dl_node *nodepointer,*temp; |
| void *data; |
| |
| if(!list->count) |
| return; |
| |
| /* if there is no filter function, directly sort all the entries */ |
| if (!filter) |
| goto sort; |
| |
| /* filter the unwanted entries in the list */ |
| nodepointer = list->head->next; |
| while(nodepointer!=list->head) { |
| if (!filter(nodepointer->data)) { |
| temp = nodepointer->next; |
| data = _dlist_remove(list, nodepointer, 0); |
| if(data) |
| list->del_func(data); |
| nodepointer = temp; |
| } |
| else |
| nodepointer=nodepointer->next; |
| } |
| |
| sort: |
| /* sort out the entries */ |
| dlist_sort_custom(list, compare); |
| } |
| |
| |
| /* internal use function |
| swaps elements a and b |
| No sense in juggling node pointers when we can just swap the data pointers |
| */ |
| |
| void _dlist_swap(struct dlist *list, struct dl_node *a, struct dl_node *b) |
| { |
| |
| void *swap; |
| |
| (void)list; |
| swap=a->data; |
| a->data=b->data; |
| b->data=swap; |
| |
| } |
| |