diff options
Diffstat (limited to 'ext/xmlrpc/libxmlrpc/queue.c')
-rw-r--r-- | ext/xmlrpc/libxmlrpc/queue.c | 982 |
1 files changed, 982 insertions, 0 deletions
diff --git a/ext/xmlrpc/libxmlrpc/queue.c b/ext/xmlrpc/libxmlrpc/queue.c new file mode 100644 index 0000000..2418738 --- /dev/null +++ b/ext/xmlrpc/libxmlrpc/queue.c @@ -0,0 +1,982 @@ +static const char rcsid[] = "#(@) $Id$"; + +/* + * Date last modified: Jan 2001 + * Modifications by Dan Libby (dan@libby.com), including: + * - various fixes, null checks, etc + * - addition of Q_Iter funcs, macros + */ + + +/*-************************************************************** + * + * File : q.c + * + * Author: Peter Yard [1993.01.02] -- 02 Jan 1993 + * + * Disclaimer: This code is released to the public domain. + * + * Description: + * Generic double ended queue (Deque pronounced DEK) for handling + * any data types, with sorting. + * + * By use of various functions in this module the caller + * can create stacks, queues, lists, doubly linked lists, + * sorted lists, indexed lists. All lists are dynamic. + * + * It is the responsibility of the caller to malloc and free + * memory for insertion into the queue. A pointer to the object + * is used so that not only can any data be used but various kinds + * of data can be pushed on the same queue if one so wished e.g. + * various length string literals mixed with pointers to structures + * or integers etc. + * + * Enhancements: + * A future improvement would be the option of multiple "cursors" + * so that multiple locations could occur in the one queue to allow + * placemarkers and additional flexibility. Perhaps even use queue + * itself to have a list of cursors. + * + * Usage: + * + * /x init queue x/ + * queue q; + * Q_Init(&q); + * + * To create a stack : + * + * Q_PushHead(&q, &mydata1); /x push x/ + * Q_PushHead(&q, &mydata2); + * ..... + * data_ptr = Q_PopHead(&q); /x pop x/ + * ..... + * data_ptr = Q_Head(&q); /x top of stack x/ + * + * To create a FIFO: + * + * Q_PushHead(&q, &mydata1); + * ..... + * data_ptr = Q_PopTail(&q); + * + * To create a double list: + * + * data_ptr = Q_Head(&q); + * .... + * data_ptr = Q_Next(&q); + * data_ptr = Q_Tail(&q); + * if (Q_IsEmpty(&q)) .... + * ..... + * data_ptr = Q_Previous(&q); + * + * To create a sorted list: + * + * Q_PushHead(&q, &mydata1); /x push x/ + * Q_PushHead(&q, &mydata2); + * ..... + * if (!Q_Sort(&q, MyFunction)) + * .. error .. + * + * /x fill in key field of mydata1. + * * NB: Q_Find does linear search + * x/ + * + * if (Q_Find(&q, &mydata1, MyFunction)) + * { + * /x found it, queue cursor now at correct record x/ + * /x can retrieve with x/ + * data_ptr = Q_Get(&q); + * + * /x alter data , write back with x/ + * Q_Put(&q, data_ptr); + * } + * + * /x Search with binary search x/ + * if (Q_Seek(&q, &mydata, MyFunction)) + * /x etc x/ + * + * + ****************************************************************/ + +#ifdef _WIN32 +#include "xmlrpc_win32.h" +#endif +#include <stdlib.h> +#include "queue.h" + + +static void QuickSort(void *list[], int low, int high, + int (*Comp)(const void *, const void *)); +static int Q_BSearch(queue *q, void *key, + int (*Comp)(const void *, const void *)); + +/* The index: a pointer to pointers */ + +static void **index; +static datanode **posn_index; + + +/*** + * + ** function : Q_Init + * + ** purpose : Initialise queue object and pointers. + * + ** parameters : 'queue' pointer. + * + ** returns : True_ if init successful else False_ + * + ** comments : + ***/ + +int Q_Init(queue *q) +{ + if(q) { + q->head = q->tail = NULL; + q->cursor = q->head; + q->size = 0; + q->sorted = False_; + } + + return True_; +} + +/*** + * + ** function : Q_AtHead + * + ** purpose : tests if cursor is at head of queue + * + ** parameters : 'queue' pointer. + * + ** returns : boolean - True_ is at head else False_ + * + ** comments : + * + ***/ + +int Q_AtHead(queue *q) +{ + return(q && q->cursor == q->head); +} + + +/*** + * + ** function : Q_AtTail + * + ** purpose : boolean test if cursor at tail of queue + * + ** parameters : 'queue' pointer to test. + * + ** returns : True_ or False_ + * + ** comments : + * + ***/ + +int Q_AtTail(queue *q) +{ + return(q && q->cursor == q->tail); +} + + +/*** + * + ** function : Q_IsEmpty + * + ** purpose : test if queue has nothing in it. + * + ** parameters : 'queue' pointer + * + ** returns : True_ if IsEmpty queue, else False_ + * + ** comments : + * + ***/ + +inline int Q_IsEmpty(queue *q) +{ + return(!q || q->size == 0); +} + +/*** + * + ** function : Q_Size + * + ** purpose : return the number of elements in the queue + * + ** parameters : queue pointer + * + ** returns : number of elements + * + ** comments : + * + ***/ + +int Q_Size(queue *q) +{ + return q ? q->size : 0; +} + + +/*** + * + ** function : Q_Head + * + ** purpose : position queue cursor to first element (head) of queue. + * + ** parameters : 'queue' pointer + * + ** returns : pointer to data at head. If queue is IsEmpty returns NULL + * + ** comments : + * + ***/ + +void *Q_Head(queue *q) +{ + if(Q_IsEmpty(q)) + return NULL; + + q->cursor = q->head; + + return q->cursor->data; +} + + +/*** + * + ** function : Q_Tail + * + ** purpose : locate cursor at tail of queue. + * + ** parameters : 'queue' pointer + * + ** returns : pointer to data at tail , if queue IsEmpty returns NULL + * + ** comments : + * + ***/ + +void *Q_Tail(queue *q) +{ + if(Q_IsEmpty(q)) + return NULL; + + q->cursor = q->tail; + + return q->cursor->data; +} + + +/*** + * + ** function : Q_PushHead + * + ** purpose : put a data pointer at the head of the queue + * + ** parameters : 'queue' pointer, void pointer to the data. + * + ** returns : True_ if success else False_ if unable to push data. + * + ** comments : + * + ***/ + +int Q_PushHead(queue *q, void *d) +{ + if(q && d) { + node *n; + datanode *p; + + p = malloc(sizeof(datanode)); + if(p == NULL) + return False_; + + n = q->head; + + q->head = (node*)p; + q->head->prev = NULL; + + if(q->size == 0) { + q->head->next = NULL; + q->tail = q->head; + } + else { + q->head->next = (datanode*)n; + n->prev = q->head; + } + + q->head->data = d; + q->size++; + + q->cursor = q->head; + + q->sorted = False_; + + return True_; + } + return False_; +} + + + +/*** + * + ** function : Q_PushTail + * + ** purpose : put a data element pointer at the tail of the queue + * + ** parameters : queue pointer, pointer to the data + * + ** returns : True_ if data pushed, False_ if data not inserted. + * + ** comments : + * + ***/ + +int Q_PushTail(queue *q, void *d) +{ + if(q && d) { + node *p; + datanode *n; + + n = malloc(sizeof(datanode)); + if(n == NULL) + return False_; + + p = q->tail; + q->tail = (node *)n; + + if(q->size == 0) { + q->tail->prev = NULL; + q->head = q->tail; + } + else { + q->tail->prev = (datanode *)p; + p->next = q->tail; + } + + q->tail->next = NULL; + + q->tail->data = d; + q->cursor = q->tail; + q->size++; + + q->sorted = False_; + + return True_; + } + return False_; +} + + + +/*** + * + ** function : Q_PopHead + * + ** purpose : remove and return the top element at the head of the + * queue. + * + ** parameters : queue pointer + * + ** returns : pointer to data element or NULL if queue is IsEmpty. + * + ** comments : + * + ***/ + +void *Q_PopHead(queue *q) +{ + datanode *n; + void *d; + + if(Q_IsEmpty(q)) + return NULL; + + d = q->head->data; + n = q->head->next; + free(q->head); + + q->size--; + + if(q->size == 0) + q->head = q->tail = q->cursor = NULL; + else { + q->head = (node *)n; + q->head->prev = NULL; + q->cursor = q->head; + } + + q->sorted = False_; + + return d; +} + + +/*** + * + ** function : Q_PopTail + * + ** purpose : remove element from tail of queue and return data. + * + ** parameters : queue pointer + * + ** returns : pointer to data element that was at tail. NULL if queue + * IsEmpty. + * + ** comments : + * + ***/ + +void *Q_PopTail(queue *q) +{ + datanode *p; + void *d; + + if(Q_IsEmpty(q)) + return NULL; + + d = q->tail->data; + p = q->tail->prev; + free(q->tail); + q->size--; + + if(q->size == 0) + q->head = q->tail = q->cursor = NULL; + else { + q->tail = (node *)p; + q->tail->next = NULL; + q->cursor = q->tail; + } + + q->sorted = False_; + + return d; +} + + + +/*** + * + ** function : Q_Next + * + ** purpose : Move to the next element in the queue without popping + * + ** parameters : queue pointer. + * + ** returns : pointer to data element of new element or NULL if end + * of the queue. + * + ** comments : This uses the cursor for the current position. Q_Next + * only moves in the direction from the head of the queue + * to the tail. + ***/ + +void *Q_Next(queue *q) +{ + if(!q) + return NULL; + + if(!q->cursor || q->cursor->next == NULL) + return NULL; + + q->cursor = (node *)q->cursor->next; + + return q->cursor->data ; +} + + + +/*** + * + ** function : Q_Previous + * + ** purpose : Opposite of Q_Next. Move to next element closer to the + * head of the queue. + * + ** parameters : pointer to queue + * + ** returns : pointer to data of new element else NULL if queue IsEmpty + * + ** comments : Makes cursor move towards the head of the queue. + * + ***/ + +void *Q_Previous(queue *q) +{ + if(!q) + return NULL; + + if(q->cursor->prev == NULL) + return NULL; + + q->cursor = (node *)q->cursor->prev; + + return q->cursor->data; +} + + +void *Q_Iter_Del(queue *q, q_iter iter) +{ + void *d; + datanode *n, *p; + + if(!q) + return NULL; + + if(iter == NULL) + return NULL; + + if(iter == (q_iter)q->head) + return Q_PopHead(q); + + if(iter == (q_iter)q->tail) + return Q_PopTail(q); + + n = ((node*)iter)->next; + p = ((node*)iter)->prev; + d = ((node*)iter)->data; + + free(iter); + + if(p) { + p->next = n; + } + if (q->cursor == (node*)iter) { + if (p) { + q->cursor = p; + } else { + q->cursor = n; + } + } + + + if (n != NULL) { + n->prev = p; + } + + q->size--; + + q->sorted = False_; + + return d; +} + + + +/*** + * + ** function : Q_DelCur + * + ** purpose : Delete the current queue element as pointed to by + * the cursor. + * + ** parameters : queue pointer + * + ** returns : pointer to data element. + * + ** comments : WARNING! It is the responsibility of the caller to + * free any memory. Queue cannot distinguish between + * pointers to literals and malloced memory. + * + ***/ + +void *Q_DelCur(queue* q) { + if(q) { + return Q_Iter_Del(q, (q_iter)q->cursor); + } + return 0; +} + + +/*** + * + ** function : Q_Destroy + * + ** purpose : Free all queue resources + * + ** parameters : queue pointer + * + ** returns : null. + * + ** comments : WARNING! It is the responsibility of the caller to + * free any memory. Queue cannot distinguish between + * pointers to literals and malloced memory. + * + ***/ + +void Q_Destroy(queue *q) +{ + while(!Q_IsEmpty(q)) { + Q_PopHead(q); + } +} + + +/*** + * + ** function : Q_Get + * + ** purpose : get the pointer to the data at the cursor location + * + ** parameters : queue pointer + * + ** returns : data element pointer + * + ** comments : + * + ***/ + +void *Q_Get(queue *q) +{ + if(!q) + return NULL; + + if(q->cursor == NULL) + return NULL; + return q->cursor->data; +} + + + +/*** + * + ** function : Q_Put + * + ** purpose : replace pointer to data with new pointer to data. + * + ** parameters : queue pointer, data pointer + * + ** returns : boolean- True_ if successful, False_ if cursor at NULL + * + ** comments : + * + ***/ + +int Q_Put(queue *q, void *data) +{ + if(q && data) { + if(q->cursor == NULL) + return False_; + + q->cursor->data = data; + return True_; + } + return False_; +} + + +/*** + * + ** function : Q_Find + * + ** purpose : Linear search of queue for match with key in *data + * + ** parameters : queue pointer q, data pointer with data containing key + * comparison function here called Comp. + * + ** returns : True_ if found , False_ if not in queue. + * + ** comments : Useful for small queues that are constantly changing + * and would otherwise need constant sorting with the + * Q_Seek function. + * For description of Comp see Q_Sort. + * Queue cursor left on position found item else at end. + * + ***/ + +int Q_Find(queue *q, void *data, + int (*Comp)(const void *, const void *)) +{ + void *d; + + if (q == NULL) { + return False_; + } + + d = Q_Head(q); + do { + if(Comp(d, data) == 0) + return True_; + d = Q_Next(q); + } while(!Q_AtTail(q)); + + if(Comp(d, data) == 0) + return True_; + + return False_; +} + +/*======== Sorted Queue and Index functions ========= */ + + +static void QuickSort(void *list[], int low, int high, + int (*Comp)(const void *, const void *)) +{ + int flag = 1, i, j; + void *key, *temp; + + if(low < high) { + i = low; + j = high + 1; + + key = list[ low ]; + + while(flag) { + i++; + while(Comp(list[i], key) < 0) + i++; + + j--; + while(Comp(list[j], key) > 0) + j--; + + if(i < j) { + temp = list[i]; + list[i] = list[j]; + list[j] = temp; + } + else flag = 0; + } + + temp = list[low]; + list[low] = list[j]; + list[j] = temp; + + QuickSort(list, low, j-1, Comp); + QuickSort(list, j+1, high, Comp); + } +} + + +/*** + * + ** function : Q_Sort + * + ** purpose : sort the queue and allow index style access. + * + ** parameters : queue pointer, comparison function compatible with + * with 'qsort'. + * + ** returns : True_ if sort succeeded. False_ if error occurred. + * + ** comments : Comp function supplied by caller must return + * -1 if data1 < data2 + * 0 if data1 == data2 + * +1 if data1 > data2 + * + * for Comp(data1, data2) + * + * If queue is already sorted it frees the memory of the + * old index and starts again. + * + ***/ + +int Q_Sort(queue *q, int (*Comp)(const void *, const void *)) +{ + int i; + void *d; + datanode *dn; + + /* if already sorted free memory for tag array */ + + if(q->sorted) { + free(index); + free(posn_index); + q->sorted = False_; + } + + /* Now allocate memory of array, array of pointers */ + + index = malloc(q->size * sizeof(q->cursor->data)); + if(index == NULL) + return False_; + + posn_index = malloc(q->size * sizeof(q->cursor)); + if(posn_index == NULL) { + free(index); + return False_; + } + + /* Walk queue putting pointers into array */ + + d = Q_Head(q); + for(i=0; i < q->size; i++) { + index[i] = d; + posn_index[i] = q->cursor; + d = Q_Next(q); + } + + /* Now sort the index */ + + QuickSort(index, 0, q->size - 1, Comp); + + /* Rearrange the actual queue into correct order */ + + dn = q->head; + i = 0; + while(dn != NULL) { + dn->data = index[i++]; + dn = dn->next; + } + + /* Re-position to original element */ + + if(d != NULL) + Q_Find(q, d, Comp); + else Q_Head(q); + + q->sorted = True_; + + return True_; +} + + +/*** + * + ** function : Q_BSearch + * + ** purpose : binary search of queue index for node containing key + * + ** parameters : queue pointer 'q', data pointer of key 'key', + * Comp comparison function. + * + ** returns : integer index into array of node pointers, + * or -1 if not found. + * + ** comments : see Q_Sort for description of 'Comp' function. + * + ***/ + +static int Q_BSearch( queue *q, void *key, + int (*Comp)(const void *, const void*)) +{ + int low, mid, hi, val; + + low = 0; + hi = q->size - 1; + + while(low <= hi) { + mid = (low + hi) / 2; + val = Comp(key, index[ mid ]); + + if(val < 0) + hi = mid - 1; + + else if(val > 0) + low = mid + 1; + + else /* Success */ + return mid; + } + + /* Not Found */ + + return -1; +} + + +/*** + * + ** function : Q_Seek + * + ** purpose : use index to locate data according to key in 'data' + * + ** parameters : queue pointer 'q', data pointer 'data', Comp comparison + * function. + * + ** returns : pointer to data or NULL if could not find it or could + * not sort queue. + * + ** comments : see Q_Sort for description of 'Comp' function. + * + ***/ + +void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *)) +{ + int idx; + + if (q == NULL) { + return NULL; + } + + if(!q->sorted) { + if(!Q_Sort(q, Comp)) + return NULL; + } + + idx = Q_BSearch(q, data, Comp); + + if(idx < 0) + return NULL; + + q->cursor = posn_index[idx]; + + return index[idx]; +} + + + +/*** + * + ** function : Q_Insert + * + ** purpose : Insert an element into an indexed queue + * + ** parameters : queue pointer 'q', data pointer 'data', Comp comparison + * function. + * + ** returns : pointer to data or NULL if could not find it or could + * not sort queue. + * + ** comments : see Q_Sort for description of 'Comp' function. + * WARNING! This code can be very slow since each new + * element means a new Q_Sort. Should only be used for + * the insertion of the odd element ,not the piecemeal + * building of an entire queue. + ***/ + +int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *)) +{ + if (q == NULL) { + return False_; + } + + Q_PushHead(q, data); + + if(!Q_Sort(q, Comp)) + return False_; + + return True_; +} + +/* read only funcs for iterating through queue. above funcs modify queue */ +q_iter Q_Iter_Head(queue *q) { + return q ? (q_iter)q->head : NULL; +} + +q_iter Q_Iter_Tail(queue *q) { + return q ? (q_iter)q->tail : NULL; +} + +q_iter Q_Iter_Next(q_iter qi) { + return qi ? (q_iter)((node*)qi)->next : NULL; +} + +q_iter Q_Iter_Prev(q_iter qi) { + return qi ? (q_iter)((node*)qi)->prev : NULL; +} + +void * Q_Iter_Get(q_iter qi) { + return qi ? ((node*)qi)->data : NULL; +} + +int Q_Iter_Put(q_iter qi, void* data) { + if(qi) { + ((node*)qi)->data = data; + return True_; + } + return False_; +} |