xref: /reactos/sdk/lib/3rdparty/libxml2/list.c (revision 53221834)
1 /*
2  * list.c: lists handling implementation
3  *
4  * Copyright (C) 2000 Gary Pennington and Daniel Veillard.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
11  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
12  * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
13  * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
14  *
15  * Author: Gary.Pennington@uk.sun.com
16  */
17 
18 #define IN_LIBXML
19 #include "libxml.h"
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <libxml/xmlmemory.h>
24 #include <libxml/list.h>
25 #include <libxml/globals.h>
26 
27 /*
28  * Type definition are kept internal
29  */
30 
31 struct _xmlLink
32 {
33     struct _xmlLink *next;
34     struct _xmlLink *prev;
35     void *data;
36 };
37 
38 struct _xmlList
39 {
40     xmlLinkPtr sentinel;
41     void (*linkDeallocator)(xmlLinkPtr );
42     int (*linkCompare)(const void *, const void*);
43 };
44 
45 /************************************************************************
46  *                                    *
47  *                Interfaces                *
48  *                                    *
49  ************************************************************************/
50 
51 /**
52  * xmlLinkDeallocator:
53  * @l:  a list
54  * @lk:  a link
55  *
56  * Unlink and deallocate @lk from list @l
57  */
58 static void
59 xmlLinkDeallocator(xmlListPtr l, xmlLinkPtr lk)
60 {
61     (lk->prev)->next = lk->next;
62     (lk->next)->prev = lk->prev;
63     if(l->linkDeallocator)
64         l->linkDeallocator(lk);
65     xmlFree(lk);
66 }
67 
68 /**
69  * xmlLinkCompare:
70  * @data0:  first data
71  * @data1:  second data
72  *
73  * Compares two arbitrary data
74  *
75  * Returns -1, 0 or 1 depending on whether data1 is greater equal or smaller
76  *          than data0
77  */
78 static int
79 xmlLinkCompare(const void *data0, const void *data1)
80 {
81     if (data0 < data1)
82         return (-1);
83     else if (data0 == data1)
84 	return (0);
85     return (1);
86 }
87 
88 /**
89  * xmlListLowerSearch:
90  * @l:  a list
91  * @data:  a data
92  *
93  * Search data in the ordered list walking from the beginning
94  *
95  * Returns the link containing the data or NULL
96  */
97 static xmlLinkPtr
98 xmlListLowerSearch(xmlListPtr l, void *data)
99 {
100     xmlLinkPtr lk;
101 
102     if (l == NULL)
103         return(NULL);
104     for(lk = l->sentinel->next;lk != l->sentinel && l->linkCompare(lk->data, data) <0 ;lk = lk->next);
105     return lk;
106 }
107 
108 /**
109  * xmlListHigherSearch:
110  * @l:  a list
111  * @data:  a data
112  *
113  * Search data in the ordered list walking backward from the end
114  *
115  * Returns the link containing the data or NULL
116  */
117 static xmlLinkPtr
118 xmlListHigherSearch(xmlListPtr l, void *data)
119 {
120     xmlLinkPtr lk;
121 
122     if (l == NULL)
123         return(NULL);
124     for(lk = l->sentinel->prev;lk != l->sentinel && l->linkCompare(lk->data, data) >0 ;lk = lk->prev);
125     return lk;
126 }
127 
128 /**
129  * xmlListSearch:
130  * @l:  a list
131  * @data:  a data
132  *
133  * Search data in the list
134  *
135  * Returns the link containing the data or NULL
136  */
137 static xmlLinkPtr
138 xmlListLinkSearch(xmlListPtr l, void *data)
139 {
140     xmlLinkPtr lk;
141     if (l == NULL)
142         return(NULL);
143     lk = xmlListLowerSearch(l, data);
144     if (lk == l->sentinel)
145         return NULL;
146     else {
147         if (l->linkCompare(lk->data, data) ==0)
148             return lk;
149         return NULL;
150     }
151 }
152 
153 /**
154  * xmlListLinkReverseSearch:
155  * @l:  a list
156  * @data:  a data
157  *
158  * Search data in the list processing backward
159  *
160  * Returns the link containing the data or NULL
161  */
162 static xmlLinkPtr
163 xmlListLinkReverseSearch(xmlListPtr l, void *data)
164 {
165     xmlLinkPtr lk;
166     if (l == NULL)
167         return(NULL);
168     lk = xmlListHigherSearch(l, data);
169     if (lk == l->sentinel)
170         return NULL;
171     else {
172         if (l->linkCompare(lk->data, data) ==0)
173             return lk;
174         return NULL;
175     }
176 }
177 
178 /**
179  * xmlListCreate:
180  * @deallocator:  an optional deallocator function
181  * @compare:  an optional comparison function
182  *
183  * Create a new list
184  *
185  * Returns the new list or NULL in case of error
186  */
187 xmlListPtr
188 xmlListCreate(xmlListDeallocator deallocator, xmlListDataCompare compare)
189 {
190     xmlListPtr l;
191     if (NULL == (l = (xmlListPtr )xmlMalloc( sizeof(xmlList)))) {
192         xmlGenericError(xmlGenericErrorContext,
193 		        "Cannot initialize memory for list");
194         return (NULL);
195     }
196     /* Initialize the list to NULL */
197     memset(l, 0, sizeof(xmlList));
198 
199     /* Add the sentinel */
200     if (NULL ==(l->sentinel = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
201         xmlGenericError(xmlGenericErrorContext,
202 		        "Cannot initialize memory for sentinel");
203 	xmlFree(l);
204         return (NULL);
205     }
206     l->sentinel->next = l->sentinel;
207     l->sentinel->prev = l->sentinel;
208     l->sentinel->data = NULL;
209 
210     /* If there is a link deallocator, use it */
211     if (deallocator != NULL)
212         l->linkDeallocator = deallocator;
213     /* If there is a link comparator, use it */
214     if (compare != NULL)
215         l->linkCompare = compare;
216     else /* Use our own */
217         l->linkCompare = xmlLinkCompare;
218     return l;
219 }
220 
221 /**
222  * xmlListSearch:
223  * @l:  a list
224  * @data:  a search value
225  *
226  * Search the list for an existing value of @data
227  *
228  * Returns the value associated to @data or NULL in case of error
229  */
230 void *
231 xmlListSearch(xmlListPtr l, void *data)
232 {
233     xmlLinkPtr lk;
234     if (l == NULL)
235         return(NULL);
236     lk = xmlListLinkSearch(l, data);
237     if (lk)
238         return (lk->data);
239     return NULL;
240 }
241 
242 /**
243  * xmlListReverseSearch:
244  * @l:  a list
245  * @data:  a search value
246  *
247  * Search the list in reverse order for an existing value of @data
248  *
249  * Returns the value associated to @data or NULL in case of error
250  */
251 void *
252 xmlListReverseSearch(xmlListPtr l, void *data)
253 {
254     xmlLinkPtr lk;
255     if (l == NULL)
256         return(NULL);
257     lk = xmlListLinkReverseSearch(l, data);
258     if (lk)
259         return (lk->data);
260     return NULL;
261 }
262 
263 /**
264  * xmlListInsert:
265  * @l:  a list
266  * @data:  the data
267  *
268  * Insert data in the ordered list at the beginning for this value
269  *
270  * Returns 0 in case of success, 1 in case of failure
271  */
272 int
273 xmlListInsert(xmlListPtr l, void *data)
274 {
275     xmlLinkPtr lkPlace, lkNew;
276 
277     if (l == NULL)
278         return(1);
279     lkPlace = xmlListLowerSearch(l, data);
280     /* Add the new link */
281     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
282     if (lkNew == NULL) {
283         xmlGenericError(xmlGenericErrorContext,
284 		        "Cannot initialize memory for new link");
285         return (1);
286     }
287     lkNew->data = data;
288     lkPlace = lkPlace->prev;
289     lkNew->next = lkPlace->next;
290     (lkPlace->next)->prev = lkNew;
291     lkPlace->next = lkNew;
292     lkNew->prev = lkPlace;
293     return 0;
294 }
295 
296 /**
297  * xmlListAppend:
298  * @l:  a list
299  * @data:  the data
300  *
301  * Insert data in the ordered list at the end for this value
302  *
303  * Returns 0 in case of success, 1 in case of failure
304  */
305 int xmlListAppend(xmlListPtr l, void *data)
306 {
307     xmlLinkPtr lkPlace, lkNew;
308 
309     if (l == NULL)
310         return(1);
311     lkPlace = xmlListHigherSearch(l, data);
312     /* Add the new link */
313     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
314     if (lkNew == NULL) {
315         xmlGenericError(xmlGenericErrorContext,
316 		        "Cannot initialize memory for new link");
317         return (1);
318     }
319     lkNew->data = data;
320     lkNew->next = lkPlace->next;
321     (lkPlace->next)->prev = lkNew;
322     lkPlace->next = lkNew;
323     lkNew->prev = lkPlace;
324     return 0;
325 }
326 
327 /**
328  * xmlListDelete:
329  * @l:  a list
330  *
331  * Deletes the list and its associated data
332  */
333 void xmlListDelete(xmlListPtr l)
334 {
335     if (l == NULL)
336         return;
337 
338     xmlListClear(l);
339     xmlFree(l->sentinel);
340     xmlFree(l);
341 }
342 
343 /**
344  * xmlListRemoveFirst:
345  * @l:  a list
346  * @data:  list data
347  *
348  * Remove the first instance associated to data in the list
349  *
350  * Returns 1 if a deallocation occurred, or 0 if not found
351  */
352 int
353 xmlListRemoveFirst(xmlListPtr l, void *data)
354 {
355     xmlLinkPtr lk;
356 
357     if (l == NULL)
358         return(0);
359     /*Find the first instance of this data */
360     lk = xmlListLinkSearch(l, data);
361     if (lk != NULL) {
362         xmlLinkDeallocator(l, lk);
363         return 1;
364     }
365     return 0;
366 }
367 
368 /**
369  * xmlListRemoveLast:
370  * @l:  a list
371  * @data:  list data
372  *
373  * Remove the last instance associated to data in the list
374  *
375  * Returns 1 if a deallocation occurred, or 0 if not found
376  */
377 int
378 xmlListRemoveLast(xmlListPtr l, void *data)
379 {
380     xmlLinkPtr lk;
381 
382     if (l == NULL)
383         return(0);
384     /*Find the last instance of this data */
385     lk = xmlListLinkReverseSearch(l, data);
386     if (lk != NULL) {
387 	xmlLinkDeallocator(l, lk);
388         return 1;
389     }
390     return 0;
391 }
392 
393 /**
394  * xmlListRemoveAll:
395  * @l:  a list
396  * @data:  list data
397  *
398  * Remove the all instance associated to data in the list
399  *
400  * Returns the number of deallocation, or 0 if not found
401  */
402 int
403 xmlListRemoveAll(xmlListPtr l, void *data)
404 {
405     int count=0;
406 
407     if (l == NULL)
408         return(0);
409 
410     while(xmlListRemoveFirst(l, data))
411         count++;
412     return count;
413 }
414 
415 /**
416  * xmlListClear:
417  * @l:  a list
418  *
419  * Remove the all data in the list
420  */
421 void
422 xmlListClear(xmlListPtr l)
423 {
424     xmlLinkPtr  lk;
425 
426     if (l == NULL)
427         return;
428     lk = l->sentinel->next;
429     while(lk != l->sentinel) {
430         xmlLinkPtr next = lk->next;
431 
432         xmlLinkDeallocator(l, lk);
433         lk = next;
434     }
435 }
436 
437 /**
438  * xmlListEmpty:
439  * @l:  a list
440  *
441  * Is the list empty ?
442  *
443  * Returns 1 if the list is empty, 0 if not empty and -1 in case of error
444  */
445 int
446 xmlListEmpty(xmlListPtr l)
447 {
448     if (l == NULL)
449         return(-1);
450     return (l->sentinel->next == l->sentinel);
451 }
452 
453 /**
454  * xmlListFront:
455  * @l:  a list
456  *
457  * Get the first element in the list
458  *
459  * Returns the first element in the list, or NULL
460  */
461 xmlLinkPtr
462 xmlListFront(xmlListPtr l)
463 {
464     if (l == NULL)
465         return(NULL);
466     return (l->sentinel->next);
467 }
468 
469 /**
470  * xmlListEnd:
471  * @l:  a list
472  *
473  * Get the last element in the list
474  *
475  * Returns the last element in the list, or NULL
476  */
477 xmlLinkPtr
478 xmlListEnd(xmlListPtr l)
479 {
480     if (l == NULL)
481         return(NULL);
482     return (l->sentinel->prev);
483 }
484 
485 /**
486  * xmlListSize:
487  * @l:  a list
488  *
489  * Get the number of elements in the list
490  *
491  * Returns the number of elements in the list or -1 in case of error
492  */
493 int
494 xmlListSize(xmlListPtr l)
495 {
496     xmlLinkPtr lk;
497     int count=0;
498 
499     if (l == NULL)
500         return(-1);
501     /* TODO: keep a counter in xmlList instead */
502     for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);
503     return count;
504 }
505 
506 /**
507  * xmlListPopFront:
508  * @l:  a list
509  *
510  * Removes the first element in the list
511  */
512 void
513 xmlListPopFront(xmlListPtr l)
514 {
515     if(!xmlListEmpty(l))
516         xmlLinkDeallocator(l, l->sentinel->next);
517 }
518 
519 /**
520  * xmlListPopBack:
521  * @l:  a list
522  *
523  * Removes the last element in the list
524  */
525 void
526 xmlListPopBack(xmlListPtr l)
527 {
528     if(!xmlListEmpty(l))
529         xmlLinkDeallocator(l, l->sentinel->prev);
530 }
531 
532 /**
533  * xmlListPushFront:
534  * @l:  a list
535  * @data:  new data
536  *
537  * add the new data at the beginning of the list
538  *
539  * Returns 1 if successful, 0 otherwise
540  */
541 int
542 xmlListPushFront(xmlListPtr l, void *data)
543 {
544     xmlLinkPtr lkPlace, lkNew;
545 
546     if (l == NULL)
547         return(0);
548     lkPlace = l->sentinel;
549     /* Add the new link */
550     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
551     if (lkNew == NULL) {
552         xmlGenericError(xmlGenericErrorContext,
553 		        "Cannot initialize memory for new link");
554         return (0);
555     }
556     lkNew->data = data;
557     lkNew->next = lkPlace->next;
558     (lkPlace->next)->prev = lkNew;
559     lkPlace->next = lkNew;
560     lkNew->prev = lkPlace;
561     return 1;
562 }
563 
564 /**
565  * xmlListPushBack:
566  * @l:  a list
567  * @data:  new data
568  *
569  * add the new data at the end of the list
570  *
571  * Returns 1 if successful, 0 otherwise
572  */
573 int
574 xmlListPushBack(xmlListPtr l, void *data)
575 {
576     xmlLinkPtr lkPlace, lkNew;
577 
578     if (l == NULL)
579         return(0);
580     lkPlace = l->sentinel->prev;
581     /* Add the new link */
582     if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
583         xmlGenericError(xmlGenericErrorContext,
584 		        "Cannot initialize memory for new link");
585         return (0);
586     }
587     lkNew->data = data;
588     lkNew->next = lkPlace->next;
589     (lkPlace->next)->prev = lkNew;
590     lkPlace->next = lkNew;
591     lkNew->prev = lkPlace;
592     return 1;
593 }
594 
595 /**
596  * xmlLinkGetData:
597  * @lk:  a link
598  *
599  * See Returns.
600  *
601  * Returns a pointer to the data referenced from this link
602  */
603 void *
604 xmlLinkGetData(xmlLinkPtr lk)
605 {
606     if (lk == NULL)
607         return(NULL);
608     return lk->data;
609 }
610 
611 /**
612  * xmlListReverse:
613  * @l:  a list
614  *
615  * Reverse the order of the elements in the list
616  */
617 void
618 xmlListReverse(xmlListPtr l)
619 {
620     xmlLinkPtr lk;
621     xmlLinkPtr lkPrev;
622 
623     if (l == NULL)
624         return;
625     lkPrev = l->sentinel;
626     for (lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
627         lkPrev->next = lkPrev->prev;
628         lkPrev->prev = lk;
629         lkPrev = lk;
630     }
631     /* Fix up the last node */
632     lkPrev->next = lkPrev->prev;
633     lkPrev->prev = lk;
634 }
635 
636 /**
637  * xmlListSort:
638  * @l:  a list
639  *
640  * Sort all the elements in the list
641  */
642 void
643 xmlListSort(xmlListPtr l)
644 {
645     xmlListPtr lTemp;
646 
647     if (l == NULL)
648         return;
649     if(xmlListEmpty(l))
650         return;
651 
652     /* I think that the real answer is to implement quicksort, the
653      * alternative is to implement some list copying procedure which
654      * would be based on a list copy followed by a clear followed by
655      * an insert. This is slow...
656      */
657 
658     if (NULL ==(lTemp = xmlListDup(l)))
659         return;
660     xmlListClear(l);
661     xmlListMerge(l, lTemp);
662     xmlListDelete(lTemp);
663     return;
664 }
665 
666 /**
667  * xmlListWalk:
668  * @l:  a list
669  * @walker:  a processing function
670  * @user:  a user parameter passed to the walker function
671  *
672  * Walk all the element of the first from first to last and
673  * apply the walker function to it
674  */
675 void
676 xmlListWalk(xmlListPtr l, xmlListWalker walker, void *user) {
677     xmlLinkPtr lk;
678 
679     if ((l == NULL) || (walker == NULL))
680         return;
681     for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
682         if((walker(lk->data, user)) == 0)
683                 break;
684     }
685 }
686 
687 /**
688  * xmlListReverseWalk:
689  * @l:  a list
690  * @walker:  a processing function
691  * @user:  a user parameter passed to the walker function
692  *
693  * Walk all the element of the list in reverse order and
694  * apply the walker function to it
695  */
696 void
697 xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, void *user) {
698     xmlLinkPtr lk;
699 
700     if ((l == NULL) || (walker == NULL))
701         return;
702     for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {
703         if((walker(lk->data, user)) == 0)
704                 break;
705     }
706 }
707 
708 /**
709  * xmlListMerge:
710  * @l1:  the original list
711  * @l2:  the new list
712  *
713  * include all the elements of the second list in the first one and
714  * clear the second list
715  */
716 void
717 xmlListMerge(xmlListPtr l1, xmlListPtr l2)
718 {
719     xmlListCopy(l1, l2);
720     xmlListClear(l2);
721 }
722 
723 /**
724  * xmlListDup:
725  * @old:  the list
726  *
727  * Duplicate the list
728  *
729  * Returns a new copy of the list or NULL in case of error
730  */
731 xmlListPtr
732 xmlListDup(const xmlListPtr old)
733 {
734     xmlListPtr cur;
735 
736     if (old == NULL)
737         return(NULL);
738     /* Hmmm, how to best deal with allocation issues when copying
739      * lists. If there is a de-allocator, should responsibility lie with
740      * the new list or the old list. Surely not both. I'll arbitrarily
741      * set it to be the old list for the time being whilst I work out
742      * the answer
743      */
744     if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare)))
745         return (NULL);
746     if (0 != xmlListCopy(cur, old))
747         return NULL;
748     return cur;
749 }
750 
751 /**
752  * xmlListCopy:
753  * @cur:  the new list
754  * @old:  the old list
755  *
756  * Move all the element from the old list in the new list
757  *
758  * Returns 0 in case of success 1 in case of error
759  */
760 int
761 xmlListCopy(xmlListPtr cur, const xmlListPtr old)
762 {
763     /* Walk the old tree and insert the data into the new one */
764     xmlLinkPtr lk;
765 
766     if ((old == NULL) || (cur == NULL))
767         return(1);
768     for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {
769         if (0 !=xmlListInsert(cur, lk->data)) {
770             xmlListDelete(cur);
771             return (1);
772         }
773     }
774     return (0);
775 }
776 /* xmlListUnique() */
777 /* xmlListSwap */
778 #define bottom_list
779 #include "elfgcchack.h"
780