1 static const char rcsid[] = "#(@) $Id$";
2
3 /*
4 * Date last modified: Jan 2001
5 * Modifications by Dan Libby (dan@libby.com), including:
6 * - various fixes, null checks, etc
7 * - addition of Q_Iter funcs, macros
8 */
9
10
11 /*-**************************************************************
12 *
13 * File : q.c
14 *
15 * Author: Peter Yard [1993.01.02] -- 02 Jan 1993
16 *
17 * Disclaimer: This code is released to the public domain.
18 *
19 * Description:
20 * Generic double ended queue (Deque pronounced DEK) for handling
21 * any data types, with sorting.
22 *
23 * By use of various functions in this module the caller
24 * can create stacks, queues, lists, doubly linked lists,
25 * sorted lists, indexed lists. All lists are dynamic.
26 *
27 * It is the responsibility of the caller to malloc and free
28 * memory for insertion into the queue. A pointer to the object
29 * is used so that not only can any data be used but various kinds
30 * of data can be pushed on the same queue if one so wished e.g.
31 * various length string literals mixed with pointers to structures
32 * or integers etc.
33 *
34 * Enhancements:
35 * A future improvement would be the option of multiple "cursors"
36 * so that multiple locations could occur in the one queue to allow
37 * placemarkers and additional flexibility. Perhaps even use queue
38 * itself to have a list of cursors.
39 *
40 * Usage:
41 *
42 * /x init queue x/
43 * queue q;
44 * Q_Init(&q);
45 *
46 * To create a stack :
47 *
48 * Q_PushHead(&q, &mydata1); /x push x/
49 * Q_PushHead(&q, &mydata2);
50 * .....
51 * data_ptr = Q_PopHead(&q); /x pop x/
52 * .....
53 * data_ptr = Q_Head(&q); /x top of stack x/
54 *
55 * To create a FIFO:
56 *
57 * Q_PushHead(&q, &mydata1);
58 * .....
59 * data_ptr = Q_PopTail(&q);
60 *
61 * To create a double list:
62 *
63 * data_ptr = Q_Head(&q);
64 * ....
65 * data_ptr = Q_Next(&q);
66 * data_ptr = Q_Tail(&q);
67 * if (Q_IsEmpty(&q)) ....
68 * .....
69 * data_ptr = Q_Previous(&q);
70 *
71 * To create a sorted list:
72 *
73 * Q_PushHead(&q, &mydata1); /x push x/
74 * Q_PushHead(&q, &mydata2);
75 * .....
76 * if (!Q_Sort(&q, MyFunction))
77 * .. error ..
78 *
79 * /x fill in key field of mydata1.
80 * * NB: Q_Find does linear search
81 * x/
82 *
83 * if (Q_Find(&q, &mydata1, MyFunction))
84 * {
85 * /x found it, queue cursor now at correct record x/
86 * /x can retrieve with x/
87 * data_ptr = Q_Get(&q);
88 *
89 * /x alter data , write back with x/
90 * Q_Put(&q, data_ptr);
91 * }
92 *
93 * /x Search with binary search x/
94 * if (Q_Seek(&q, &mydata, MyFunction))
95 * /x etc x/
96 *
97 *
98 ****************************************************************/
99
100 #include <stdlib.h>
101 #include <php.h>
102 #include "queue.h"
103
104 static void QuickSort(void *list[], int low, int high,
105 int (*Comp)(const void *, const void *));
106 static int Q_BSearch(queue *q, void *key,
107 int (*Comp)(const void *, const void *));
108
109 /* The index: a pointer to pointers */
110
111 static void **queue_index;
112 static datanode **queue_posn_index;
113
114
115 /***
116 *
117 ** function : Q_Init
118 *
119 ** purpose : Initialise queue object and pointers.
120 *
121 ** parameters : 'queue' pointer.
122 *
123 ** returns : True_ if init successful else False_
124 *
125 ** comments :
126 ***/
127
Q_Init(queue * q)128 int Q_Init(queue *q)
129 {
130 if(q) {
131 q->head = q->tail = NULL;
132 q->cursor = q->head;
133 q->size = 0;
134 q->sorted = False_;
135 }
136
137 return True_;
138 }
139
140 /***
141 *
142 ** function : Q_AtHead
143 *
144 ** purpose : tests if cursor is at head of queue
145 *
146 ** parameters : 'queue' pointer.
147 *
148 ** returns : boolean - True_ is at head else False_
149 *
150 ** comments :
151 *
152 ***/
153
Q_AtHead(queue * q)154 int Q_AtHead(queue *q)
155 {
156 return(q && q->cursor == q->head);
157 }
158
159
160 /***
161 *
162 ** function : Q_AtTail
163 *
164 ** purpose : boolean test if cursor at tail of queue
165 *
166 ** parameters : 'queue' pointer to test.
167 *
168 ** returns : True_ or False_
169 *
170 ** comments :
171 *
172 ***/
173
Q_AtTail(queue * q)174 int Q_AtTail(queue *q)
175 {
176 return(q && q->cursor == q->tail);
177 }
178
179
180 /***
181 *
182 ** function : Q_IsEmpty
183 *
184 ** purpose : test if queue has nothing in it.
185 *
186 ** parameters : 'queue' pointer
187 *
188 ** returns : True_ if IsEmpty queue, else False_
189 *
190 ** comments :
191 *
192 ***/
193
Q_IsEmpty(queue * q)194 inline int Q_IsEmpty(queue *q)
195 {
196 return(!q || q->size == 0);
197 }
198
199 /***
200 *
201 ** function : Q_Size
202 *
203 ** purpose : return the number of elements in the queue
204 *
205 ** parameters : queue pointer
206 *
207 ** returns : number of elements
208 *
209 ** comments :
210 *
211 ***/
212
Q_Size(queue * q)213 int Q_Size(queue *q)
214 {
215 return q ? q->size : 0;
216 }
217
218
219 /***
220 *
221 ** function : Q_Head
222 *
223 ** purpose : position queue cursor to first element (head) of queue.
224 *
225 ** parameters : 'queue' pointer
226 *
227 ** returns : pointer to data at head. If queue is IsEmpty returns NULL
228 *
229 ** comments :
230 *
231 ***/
232
Q_Head(queue * q)233 void *Q_Head(queue *q)
234 {
235 if(Q_IsEmpty(q))
236 return NULL;
237
238 q->cursor = q->head;
239
240 return q->cursor->data;
241 }
242
243
244 /***
245 *
246 ** function : Q_Tail
247 *
248 ** purpose : locate cursor at tail of queue.
249 *
250 ** parameters : 'queue' pointer
251 *
252 ** returns : pointer to data at tail , if queue IsEmpty returns NULL
253 *
254 ** comments :
255 *
256 ***/
257
Q_Tail(queue * q)258 void *Q_Tail(queue *q)
259 {
260 if(Q_IsEmpty(q))
261 return NULL;
262
263 q->cursor = q->tail;
264
265 return q->cursor->data;
266 }
267
268
269 /***
270 *
271 ** function : Q_PushHead
272 *
273 ** purpose : put a data pointer at the head of the queue
274 *
275 ** parameters : 'queue' pointer, void pointer to the data.
276 *
277 ** returns : True_ if success else False_ if unable to push data.
278 *
279 ** comments :
280 *
281 ***/
282
Q_PushHead(queue * q,void * d)283 int Q_PushHead(queue *q, void *d)
284 {
285 if(q && d) {
286 node *n;
287 datanode *p;
288
289 p = emalloc(sizeof(datanode));
290 if(p == NULL)
291 return False_;
292
293 n = q->head;
294
295 q->head = (node*)p;
296 q->head->prev = NULL;
297
298 if(q->size == 0) {
299 q->head->next = NULL;
300 q->tail = q->head;
301 }
302 else {
303 q->head->next = (datanode*)n;
304 n->prev = q->head;
305 }
306
307 q->head->data = d;
308 q->size++;
309
310 q->cursor = q->head;
311
312 q->sorted = False_;
313
314 return True_;
315 }
316 return False_;
317 }
318
319
320
321 /***
322 *
323 ** function : Q_PushTail
324 *
325 ** purpose : put a data element pointer at the tail of the queue
326 *
327 ** parameters : queue pointer, pointer to the data
328 *
329 ** returns : True_ if data pushed, False_ if data not inserted.
330 *
331 ** comments :
332 *
333 ***/
334
Q_PushTail(queue * q,void * d)335 int Q_PushTail(queue *q, void *d)
336 {
337 if(q && d) {
338 node *p;
339 datanode *n;
340
341 n = emalloc(sizeof(datanode));
342 if(n == NULL)
343 return False_;
344
345 p = q->tail;
346 q->tail = (node *)n;
347
348 if(q->size == 0) {
349 q->tail->prev = NULL;
350 q->head = q->tail;
351 }
352 else {
353 q->tail->prev = (datanode *)p;
354 p->next = q->tail;
355 }
356
357 q->tail->next = NULL;
358
359 q->tail->data = d;
360 q->cursor = q->tail;
361 q->size++;
362
363 q->sorted = False_;
364
365 return True_;
366 }
367 return False_;
368 }
369
370
371
372 /***
373 *
374 ** function : Q_PopHead
375 *
376 ** purpose : remove and return the top element at the head of the
377 * queue.
378 *
379 ** parameters : queue pointer
380 *
381 ** returns : pointer to data element or NULL if queue is IsEmpty.
382 *
383 ** comments :
384 *
385 ***/
386
Q_PopHead(queue * q)387 void *Q_PopHead(queue *q)
388 {
389 datanode *n;
390 void *d;
391
392 if(Q_IsEmpty(q))
393 return NULL;
394
395 d = q->head->data;
396 n = q->head->next;
397 efree(q->head);
398
399 q->size--;
400
401 if(q->size == 0)
402 q->head = q->tail = q->cursor = NULL;
403 else {
404 q->head = (node *)n;
405 q->head->prev = NULL;
406 q->cursor = q->head;
407 }
408
409 q->sorted = False_;
410
411 return d;
412 }
413
414
415 /***
416 *
417 ** function : Q_PopTail
418 *
419 ** purpose : remove element from tail of queue and return data.
420 *
421 ** parameters : queue pointer
422 *
423 ** returns : pointer to data element that was at tail. NULL if queue
424 * IsEmpty.
425 *
426 ** comments :
427 *
428 ***/
429
Q_PopTail(queue * q)430 void *Q_PopTail(queue *q)
431 {
432 datanode *p;
433 void *d;
434
435 if(Q_IsEmpty(q))
436 return NULL;
437
438 d = q->tail->data;
439 p = q->tail->prev;
440 efree(q->tail);
441 q->size--;
442
443 if(q->size == 0)
444 q->head = q->tail = q->cursor = NULL;
445 else {
446 q->tail = (node *)p;
447 q->tail->next = NULL;
448 q->cursor = q->tail;
449 }
450
451 q->sorted = False_;
452
453 return d;
454 }
455
456
457
458 /***
459 *
460 ** function : Q_Next
461 *
462 ** purpose : Move to the next element in the queue without popping
463 *
464 ** parameters : queue pointer.
465 *
466 ** returns : pointer to data element of new element or NULL if end
467 * of the queue.
468 *
469 ** comments : This uses the cursor for the current position. Q_Next
470 * only moves in the direction from the head of the queue
471 * to the tail.
472 ***/
473
Q_Next(queue * q)474 void *Q_Next(queue *q)
475 {
476 if(!q)
477 return NULL;
478
479 if(!q->cursor || q->cursor->next == NULL)
480 return NULL;
481
482 q->cursor = (node *)q->cursor->next;
483
484 return q->cursor->data ;
485 }
486
487
488
489 /***
490 *
491 ** function : Q_Previous
492 *
493 ** purpose : Opposite of Q_Next. Move to next element closer to the
494 * head of the queue.
495 *
496 ** parameters : pointer to queue
497 *
498 ** returns : pointer to data of new element else NULL if queue IsEmpty
499 *
500 ** comments : Makes cursor move towards the head of the queue.
501 *
502 ***/
503
Q_Previous(queue * q)504 void *Q_Previous(queue *q)
505 {
506 if(!q)
507 return NULL;
508
509 if(q->cursor->prev == NULL)
510 return NULL;
511
512 q->cursor = (node *)q->cursor->prev;
513
514 return q->cursor->data;
515 }
516
517
Q_Iter_Del(queue * q,q_iter iter)518 void *Q_Iter_Del(queue *q, q_iter iter)
519 {
520 void *d;
521 datanode *n, *p;
522
523 if(!q)
524 return NULL;
525
526 if(iter == NULL)
527 return NULL;
528
529 if(iter == (q_iter)q->head)
530 return Q_PopHead(q);
531
532 if(iter == (q_iter)q->tail)
533 return Q_PopTail(q);
534
535 n = ((node*)iter)->next;
536 p = ((node*)iter)->prev;
537 d = ((node*)iter)->data;
538
539 efree(iter);
540
541 if(p) {
542 p->next = n;
543 }
544 if (q->cursor == (node*)iter) {
545 if (p) {
546 q->cursor = p;
547 } else {
548 q->cursor = n;
549 }
550 }
551
552
553 if (n != NULL) {
554 n->prev = p;
555 }
556
557 q->size--;
558
559 q->sorted = False_;
560
561 return d;
562 }
563
564
565
566 /***
567 *
568 ** function : Q_DelCur
569 *
570 ** purpose : Delete the current queue element as pointed to by
571 * the cursor.
572 *
573 ** parameters : queue pointer
574 *
575 ** returns : pointer to data element.
576 *
577 ** comments : WARNING! It is the responsibility of the caller to
578 * free any memory. Queue cannot distinguish between
579 * pointers to literals and malloced memory.
580 *
581 ***/
582
Q_DelCur(queue * q)583 void *Q_DelCur(queue* q) {
584 if(q) {
585 return Q_Iter_Del(q, (q_iter)q->cursor);
586 }
587 return 0;
588 }
589
590
591 /***
592 *
593 ** function : Q_Destroy
594 *
595 ** purpose : Free all queue resources
596 *
597 ** parameters : queue pointer
598 *
599 ** returns : null.
600 *
601 ** comments : WARNING! It is the responsibility of the caller to
602 * free any memory. Queue cannot distinguish between
603 * pointers to literals and malloced memory.
604 *
605 ***/
606
Q_Destroy(queue * q)607 void Q_Destroy(queue *q)
608 {
609 while(!Q_IsEmpty(q)) {
610 Q_PopHead(q);
611 }
612 }
613
614
615 /***
616 *
617 ** function : Q_Get
618 *
619 ** purpose : get the pointer to the data at the cursor location
620 *
621 ** parameters : queue pointer
622 *
623 ** returns : data element pointer
624 *
625 ** comments :
626 *
627 ***/
628
Q_Get(queue * q)629 void *Q_Get(queue *q)
630 {
631 if(!q)
632 return NULL;
633
634 if(q->cursor == NULL)
635 return NULL;
636 return q->cursor->data;
637 }
638
639
640
641 /***
642 *
643 ** function : Q_Put
644 *
645 ** purpose : replace pointer to data with new pointer to data.
646 *
647 ** parameters : queue pointer, data pointer
648 *
649 ** returns : boolean- True_ if successful, False_ if cursor at NULL
650 *
651 ** comments :
652 *
653 ***/
654
Q_Put(queue * q,void * data)655 int Q_Put(queue *q, void *data)
656 {
657 if(q && data) {
658 if(q->cursor == NULL)
659 return False_;
660
661 q->cursor->data = data;
662 return True_;
663 }
664 return False_;
665 }
666
667
668 /***
669 *
670 ** function : Q_Find
671 *
672 ** purpose : Linear search of queue for match with key in *data
673 *
674 ** parameters : queue pointer q, data pointer with data containing key
675 * comparison function here called Comp.
676 *
677 ** returns : True_ if found , False_ if not in queue.
678 *
679 ** comments : Useful for small queues that are constantly changing
680 * and would otherwise need constant sorting with the
681 * Q_Seek function.
682 * For description of Comp see Q_Sort.
683 * Queue cursor left on position found item else at end.
684 *
685 ***/
686
Q_Find(queue * q,void * data,int (* Comp)(const void *,const void *))687 int Q_Find(queue *q, void *data,
688 int (*Comp)(const void *, const void *))
689 {
690 void *d;
691
692 if (q == NULL) {
693 return False_;
694 }
695
696 d = Q_Head(q);
697 do {
698 if(Comp(d, data) == 0)
699 return True_;
700 d = Q_Next(q);
701 } while(!Q_AtTail(q));
702
703 if(Comp(d, data) == 0)
704 return True_;
705
706 return False_;
707 }
708
709 /*======== Sorted Queue and Index functions ========= */
710
711
QuickSort(void * list[],int low,int high,int (* Comp)(const void *,const void *))712 static void QuickSort(void *list[], int low, int high,
713 int (*Comp)(const void *, const void *))
714 {
715 int flag = 1, i, j;
716 void *key, *temp;
717
718 if(low < high) {
719 i = low;
720 j = high + 1;
721
722 key = list[ low ];
723
724 while(flag) {
725 i++;
726 while(Comp(list[i], key) < 0)
727 i++;
728
729 j--;
730 while(Comp(list[j], key) > 0)
731 j--;
732
733 if(i < j) {
734 temp = list[i];
735 list[i] = list[j];
736 list[j] = temp;
737 }
738 else flag = 0;
739 }
740
741 temp = list[low];
742 list[low] = list[j];
743 list[j] = temp;
744
745 QuickSort(list, low, j-1, Comp);
746 QuickSort(list, j+1, high, Comp);
747 }
748 }
749
750
751 /***
752 *
753 ** function : Q_Sort
754 *
755 ** purpose : sort the queue and allow index style access.
756 *
757 ** parameters : queue pointer, comparison function compatible with
758 * with 'qsort'.
759 *
760 ** returns : True_ if sort succeeded. False_ if error occurred.
761 *
762 ** comments : Comp function supplied by caller must return
763 * -1 if data1 < data2
764 * 0 if data1 == data2
765 * +1 if data1 > data2
766 *
767 * for Comp(data1, data2)
768 *
769 * If queue is already sorted it frees the memory of the
770 * old index and starts again.
771 *
772 ***/
773
Q_Sort(queue * q,int (* Comp)(const void *,const void *))774 int Q_Sort(queue *q, int (*Comp)(const void *, const void *))
775 {
776 int i;
777 void *d;
778 datanode *dn;
779
780 /* if already sorted free memory for tag array */
781
782 if(q->sorted) {
783 efree(queue_index);
784 efree(queue_posn_index);
785 q->sorted = False_;
786 }
787
788 /* Now allocate memory of array, array of pointers */
789
790 queue_index = emalloc(q->size * sizeof(q->cursor->data));
791 if(queue_index == NULL)
792 return False_;
793
794 queue_posn_index = emalloc(q->size * sizeof(q->cursor));
795 if(queue_posn_index == NULL) {
796 efree(queue_index);
797 return False_;
798 }
799
800 /* Walk queue putting pointers into array */
801
802 d = Q_Head(q);
803 for(i=0; i < q->size; i++) {
804 queue_index[i] = d;
805 queue_posn_index[i] = q->cursor;
806 d = Q_Next(q);
807 }
808
809 /* Now sort the index */
810
811 QuickSort(queue_index, 0, q->size - 1, Comp);
812
813 /* Rearrange the actual queue into correct order */
814
815 dn = q->head;
816 i = 0;
817 while(dn != NULL) {
818 dn->data = queue_index[i++];
819 dn = dn->next;
820 }
821
822 /* Re-position to original element */
823
824 if(d != NULL)
825 Q_Find(q, d, Comp);
826 else Q_Head(q);
827
828 q->sorted = True_;
829
830 return True_;
831 }
832
833
834 /***
835 *
836 ** function : Q_BSearch
837 *
838 ** purpose : binary search of queue index for node containing key
839 *
840 ** parameters : queue pointer 'q', data pointer of key 'key',
841 * Comp comparison function.
842 *
843 ** returns : integer index into array of node pointers,
844 * or -1 if not found.
845 *
846 ** comments : see Q_Sort for description of 'Comp' function.
847 *
848 ***/
849
Q_BSearch(queue * q,void * key,int (* Comp)(const void *,const void *))850 static int Q_BSearch( queue *q, void *key,
851 int (*Comp)(const void *, const void*))
852 {
853 int low, mid, hi, val;
854
855 low = 0;
856 hi = q->size - 1;
857
858 while(low <= hi) {
859 mid = (low + hi) / 2;
860 val = Comp(key, queue_index[ mid ]);
861
862 if(val < 0)
863 hi = mid - 1;
864
865 else if(val > 0)
866 low = mid + 1;
867
868 else /* Success */
869 return mid;
870 }
871
872 /* Not Found */
873
874 return -1;
875 }
876
877
878 /***
879 *
880 ** function : Q_Seek
881 *
882 ** purpose : use index to locate data according to key in 'data'
883 *
884 ** parameters : queue pointer 'q', data pointer 'data', Comp comparison
885 * function.
886 *
887 ** returns : pointer to data or NULL if could not find it or could
888 * not sort queue.
889 *
890 ** comments : see Q_Sort for description of 'Comp' function.
891 *
892 ***/
893
Q_Seek(queue * q,void * data,int (* Comp)(const void *,const void *))894 void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *))
895 {
896 int idx;
897
898 if (q == NULL) {
899 return NULL;
900 }
901
902 if(!q->sorted) {
903 if(!Q_Sort(q, Comp))
904 return NULL;
905 }
906
907 idx = Q_BSearch(q, data, Comp);
908
909 if(idx < 0)
910 return NULL;
911
912 q->cursor = queue_posn_index[idx];
913
914 return queue_index[idx];
915 }
916
917
918
919 /***
920 *
921 ** function : Q_Insert
922 *
923 ** purpose : Insert an element into an indexed queue
924 *
925 ** parameters : queue pointer 'q', data pointer 'data', Comp comparison
926 * function.
927 *
928 ** returns : pointer to data or NULL if could not find it or could
929 * not sort queue.
930 *
931 ** comments : see Q_Sort for description of 'Comp' function.
932 * WARNING! This code can be very slow since each new
933 * element means a new Q_Sort. Should only be used for
934 * the insertion of the odd element ,not the piecemeal
935 * building of an entire queue.
936 ***/
937
Q_Insert(queue * q,void * data,int (* Comp)(const void *,const void *))938 int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *))
939 {
940 if (q == NULL) {
941 return False_;
942 }
943
944 Q_PushHead(q, data);
945
946 if(!Q_Sort(q, Comp))
947 return False_;
948
949 return True_;
950 }
951
952 /* read only funcs for iterating through queue. above funcs modify queue */
Q_Iter_Head(queue * q)953 q_iter Q_Iter_Head(queue *q) {
954 return q ? (q_iter)q->head : NULL;
955 }
956
Q_Iter_Tail(queue * q)957 q_iter Q_Iter_Tail(queue *q) {
958 return q ? (q_iter)q->tail : NULL;
959 }
960
Q_Iter_Next(q_iter qi)961 q_iter Q_Iter_Next(q_iter qi) {
962 return qi ? (q_iter)((node*)qi)->next : NULL;
963 }
964
Q_Iter_Prev(q_iter qi)965 q_iter Q_Iter_Prev(q_iter qi) {
966 return qi ? (q_iter)((node*)qi)->prev : NULL;
967 }
968
Q_Iter_Get(q_iter qi)969 void * Q_Iter_Get(q_iter qi) {
970 return qi ? ((node*)qi)->data : NULL;
971 }
972
Q_Iter_Put(q_iter qi,void * data)973 int Q_Iter_Put(q_iter qi, void* data) {
974 if(qi) {
975 ((node*)qi)->data = data;
976 return True_;
977 }
978 return False_;
979 }
980