xref: /PHP-7.3/ext/xmlrpc/libxmlrpc/queue.c (revision ab52afb9)
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