blob: 756912a2e1dca258e3547d2b5f1a2489a6097ad9 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
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
Daniel Veillard34ce8be2002-03-18 19:37:11 +000018#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000019#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000020
21#include <stdlib.h>
22#include <string.h>
23#include <libxml/xmlmemory.h>
24#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000025#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000026
27/*
28 * Type definition are kept internal
29 */
30
31struct _xmlLink
32{
33 struct _xmlLink *next;
34 struct _xmlLink *prev;
35 void *data;
36};
37
38struct _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 */
58static void
59xmlLinkDeallocator(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 */
78static int
79xmlLinkCompare(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 */
97static xmlLinkPtr
98xmlListLowerSearch(xmlListPtr l, void *data)
99{
100 xmlLinkPtr lk;
101
102 for(lk = l->sentinel->next;lk != l->sentinel && l->linkCompare(lk->data, data) <0 ;lk = lk->next);
103 return lk;
104}
105
106/**
107 * xmlListHigherSearch:
108 * @l: a list
109 * @data: a data
110 *
111 * Search data in the ordered list walking backward from the end
112 *
113 * Returns the link containing the data or NULL
114 */
115static xmlLinkPtr
116xmlListHigherSearch(xmlListPtr l, void *data)
117{
118 xmlLinkPtr lk;
119
120 for(lk = l->sentinel->prev;lk != l->sentinel && l->linkCompare(lk->data, data) >0 ;lk = lk->prev);
121 return lk;
122}
123
124/**
125 * xmlListSearch:
126 * @l: a list
127 * @data: a data
128 *
129 * Search data in the list
130 *
131 * Returns the link containing the data or NULL
132 */
133static xmlLinkPtr
134xmlListLinkSearch(xmlListPtr l, void *data)
135{
136 xmlLinkPtr lk;
137 lk = xmlListLowerSearch(l, data);
138 if (lk == l->sentinel)
139 return NULL;
140 else {
141 if (l->linkCompare(lk->data, data) ==0)
142 return lk;
143 return NULL;
144 }
145}
146
147/**
148 * xmlListLinkReverseSearch:
149 * @l: a list
150 * @data: a data
151 *
152 * Search data in the list processing backward
153 *
154 * Returns the link containing the data or NULL
155 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000156static xmlLinkPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000157xmlListLinkReverseSearch(xmlListPtr l, void *data)
158{
159 xmlLinkPtr lk;
160 lk = xmlListHigherSearch(l, data);
161 if (lk == l->sentinel)
162 return NULL;
163 else {
164 if (l->linkCompare(lk->data, data) ==0)
165 return lk;
166 return NULL;
167 }
168}
169
170/**
171 * xmlListCreate:
172 * @deallocator: an optional deallocator function
173 * @compare: an optional comparison function
174 *
175 * Create a new list
176 *
177 * Returns the new list or NULL in case of error
178 */
179xmlListPtr
180xmlListCreate(xmlListDeallocator deallocator, xmlListDataCompare compare)
181{
182 xmlListPtr l;
183 if (NULL == (l = (xmlListPtr )xmlMalloc( sizeof(xmlList)))) {
184 perror("Cannot initialize memory for list");
185 return (NULL);
186 }
187 /* Initialize the list to NULL */
188 memset(l, 0, sizeof(xmlList));
189
190 /* Add the sentinel */
191 if (NULL ==(l->sentinel = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
192 perror("Cannot initialize memory for sentinel");
193 xmlFree(l);
194 return (NULL);
195 }
196 l->sentinel->next = l->sentinel;
197 l->sentinel->prev = l->sentinel;
198 l->sentinel->data = NULL;
199
200 /* If there is a link deallocator, use it */
201 if (deallocator != NULL)
202 l->linkDeallocator = deallocator;
203 /* If there is a link comparator, use it */
204 if (compare != NULL)
205 l->linkCompare = compare;
206 else /* Use our own */
207 l->linkCompare = xmlLinkCompare;
208 return l;
209}
210
211/**
212 * xmlListSearch:
213 * @l: a list
214 * @data: a search value
215 *
216 * Search the list for an existing value of @data
217 *
218 * Returns the value associated to @data or NULL in case of error
219 */
220void *
221xmlListSearch(xmlListPtr l, void *data)
222{
223 xmlLinkPtr lk;
224 lk = xmlListLinkSearch(l, data);
225 if (lk)
226 return (lk->data);
227 return NULL;
228}
229
230/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000231 * xmlListReverseSearch:
Owen Taylor3473f882001-02-23 17:55:21 +0000232 * @l: a list
233 * @data: a search value
234 *
235 * Search the list in reverse order for an existing value of @data
236 *
237 * Returns the value associated to @data or NULL in case of error
238 */
239void *
240xmlListReverseSearch(xmlListPtr l, void *data)
241{
242 xmlLinkPtr lk;
243 lk = xmlListLinkReverseSearch(l, data);
244 if (lk)
245 return (lk->data);
246 return NULL;
247}
248
249/**
250 * xmlListInsert:
251 * @l: a list
252 * @data: the data
253 *
254 * Insert data in the ordered list at the beginning for this value
255 *
256 * Returns 0 in case of success, 1 in case of failure
257 */
258int
259xmlListInsert(xmlListPtr l, void *data)
260{
261 xmlLinkPtr lkPlace, lkNew;
262
263 lkPlace = xmlListLowerSearch(l, data);
264 /* Add the new link */
265 lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
266 if (lkNew == NULL) {
267 perror("Cannot initialize memory for new link");
268 return (1);
269 }
270 lkNew->data = data;
271 lkPlace = lkPlace->prev;
272 lkNew->next = lkPlace->next;
273 (lkPlace->next)->prev = lkNew;
274 lkPlace->next = lkNew;
275 lkNew->prev = lkPlace;
276 return 0;
277}
278
279/**
280 * xmlListAppend:
281 * @l: a list
282 * @data: the data
283 *
284 * Insert data in the ordered list at the end for this value
285 *
286 * Returns 0 in case of success, 1 in case of failure
287 */
288int xmlListAppend(xmlListPtr l, void *data)
289{
290 xmlLinkPtr lkPlace, lkNew;
291
292 lkPlace = xmlListHigherSearch(l, data);
293 /* Add the new link */
294 lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
295 if (lkNew == NULL) {
296 perror("Cannot initialize memory for new link");
297 return (0);
298 }
299 lkNew->data = data;
300 lkNew->next = lkPlace->next;
301 (lkPlace->next)->prev = lkNew;
302 lkPlace->next = lkNew;
303 lkNew->prev = lkPlace;
304 return 1;
305}
306
307/**
308 * xmlListDelete:
309 * @l: a list
310 *
311 * Deletes the list and its associated data
312 */
313void xmlListDelete(xmlListPtr l)
314{
315 xmlListClear(l);
316 xmlFree(l->sentinel);
317 xmlFree(l);
318}
319
320/**
321 * xmlListRemoveFirst:
322 * @l: a list
323 * @data: list data
324 *
325 * Remove the first instance associated to data in the list
326 *
327 * Returns 1 if a deallocation occured, or 0 if not found
328 */
329int
330xmlListRemoveFirst(xmlListPtr l, void *data)
331{
332 xmlLinkPtr lk;
333
334 /*Find the first instance of this data */
335 lk = xmlListLinkSearch(l, data);
336 if (lk != NULL) {
337 xmlLinkDeallocator(l, lk);
338 return 1;
339 }
340 return 0;
341}
342
343/**
344 * xmlListRemoveLast:
345 * @l: a list
346 * @data: list data
347 *
348 * Remove the last instance associated to data in the list
349 *
350 * Returns 1 if a deallocation occured, or 0 if not found
351 */
352int
353xmlListRemoveLast(xmlListPtr l, void *data)
354{
355 xmlLinkPtr lk;
356
357 /*Find the last instance of this data */
358 lk = xmlListLinkReverseSearch(l, data);
359 if (lk != NULL) {
360 xmlLinkDeallocator(l, lk);
361 return 1;
362 }
363 return 0;
364}
365
366/**
367 * xmlListRemoveAll:
368 * @l: a list
369 * @data: list data
370 *
371 * Remove the all instance associated to data in the list
372 *
373 * Returns the number of deallocation, or 0 if not found
374 */
375int
376xmlListRemoveAll(xmlListPtr l, void *data)
377{
378 int count=0;
379
380
381 while(xmlListRemoveFirst(l, data))
382 count++;
383 return count;
384}
385
386/**
387 * xmlListClear:
388 * @l: a list
389 *
390 * Remove the all data in the list
391 */
392void
393xmlListClear(xmlListPtr l)
394{
395 xmlLinkPtr lk = l->sentinel->next;
396
397 while(lk != l->sentinel) {
398 xmlLinkPtr next = lk->next;
399
400 xmlLinkDeallocator(l, lk);
401 lk = next;
402 }
403}
404
405/**
406 * xmlListEmpty:
407 * @l: a list
408 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000409 * Is the list empty ?
410 *
Owen Taylor3473f882001-02-23 17:55:21 +0000411 * Returns 1 if the list is empty, 0 otherwise
412 */
413int
414xmlListEmpty(xmlListPtr l)
415{
416 return (l->sentinel->next == l->sentinel);
417}
418
419/**
420 * xmlListFront:
421 * @l: a list
422 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000423 * Get the first element in the list
424 *
Owen Taylor3473f882001-02-23 17:55:21 +0000425 * Returns the first element in the list, or NULL
426 */
427xmlLinkPtr
428xmlListFront(xmlListPtr l)
429{
430 return (l->sentinel->next);
431}
432
433/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000434 * xmlListEnd:
Owen Taylor3473f882001-02-23 17:55:21 +0000435 * @l: a list
436 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000437 * Get the last element in the list
438 *
Owen Taylor3473f882001-02-23 17:55:21 +0000439 * Returns the last element in the list, or NULL
440 */
441xmlLinkPtr
442xmlListEnd(xmlListPtr l)
443{
444 return (l->sentinel->prev);
445}
446
447/**
448 * xmlListSize:
449 * @l: a list
450 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000451 * Get the number of elements in the list
452 *
Owen Taylor3473f882001-02-23 17:55:21 +0000453 * Returns the number of elements in the list
454 */
455int
456xmlListSize(xmlListPtr l)
457{
458 xmlLinkPtr lk;
459 int count=0;
460
461 /* TODO: keep a counter in xmlList instead */
462 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);
463 return count;
464}
465
466/**
467 * xmlListPopFront:
468 * @l: a list
469 *
470 * Removes the first element in the list
471 */
472void
473xmlListPopFront(xmlListPtr l)
474{
475 if(!xmlListEmpty(l))
476 xmlLinkDeallocator(l, l->sentinel->next);
477}
478
479/**
480 * xmlListPopBack:
481 * @l: a list
482 *
483 * Removes the last element in the list
484 */
485void
486xmlListPopBack(xmlListPtr l)
487{
488 if(!xmlListEmpty(l))
489 xmlLinkDeallocator(l, l->sentinel->prev);
490}
491
492/**
493 * xmlListPushFront:
494 * @l: a list
495 * @data: new data
496 *
497 * add the new data at the beginning of the list
498 *
499 * Returns 1 if successful, 0 otherwise
500 */
501int
502xmlListPushFront(xmlListPtr l, void *data)
503{
504 xmlLinkPtr lkPlace, lkNew;
505
506 lkPlace = l->sentinel;
507 /* Add the new link */
508 lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
509 if (lkNew == NULL) {
510 perror("Cannot initialize memory for new link");
511 return (0);
512 }
513 lkNew->data = data;
514 lkNew->next = lkPlace->next;
515 (lkPlace->next)->prev = lkNew;
516 lkPlace->next = lkNew;
517 lkNew->prev = lkPlace;
518 return 1;
519}
520
521/**
522 * xmlListPushBack:
523 * @l: a list
524 * @data: new data
525 *
526 * add the new data at the end of the list
527 *
528 * Returns 1 if successful, 0 otherwise
529 */
530int
531xmlListPushBack(xmlListPtr l, void *data)
532{
533 xmlLinkPtr lkPlace, lkNew;
534
535 lkPlace = l->sentinel->prev;
536 /* Add the new link */
537 if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
538 perror("Cannot initialize memory for new link");
539 return (0);
540 }
541 lkNew->data = data;
542 lkNew->next = lkPlace->next;
543 (lkPlace->next)->prev = lkNew;
544 lkPlace->next = lkNew;
545 lkNew->prev = lkPlace;
546 return 1;
547}
548
549/**
550 * xmlLinkGetData:
551 * @lk: a link
552 *
553 * See Returns.
554 *
555 * Returns a pointer to the data referenced from this link
556 */
557void *
558xmlLinkGetData(xmlLinkPtr lk)
559{
560 return lk->data;
561}
562
563/**
564 * xmlListReverse:
565 * @l: a list
566 *
567 * Reverse the order of the elements in the list
568 */
569void
570xmlListReverse(xmlListPtr l) {
571 xmlLinkPtr lk;
572 xmlLinkPtr lkPrev = l->sentinel;
573
574 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
575 lkPrev->next = lkPrev->prev;
576 lkPrev->prev = lk;
577 lkPrev = lk;
578 }
579 /* Fix up the last node */
580 lkPrev->next = lkPrev->prev;
581 lkPrev->prev = lk;
582}
583
584/**
585 * xmlListSort:
586 * @l: a list
587 *
588 * Sort all the elements in the list
589 */
590void
591xmlListSort(xmlListPtr l)
592{
593 xmlListPtr lTemp;
594
595 if(xmlListEmpty(l))
596 return;
597
598 /* I think that the real answer is to implement quicksort, the
599 * alternative is to implement some list copying procedure which
600 * would be based on a list copy followed by a clear followed by
601 * an insert. This is slow...
602 */
603
604 if (NULL ==(lTemp = xmlListDup(l)))
605 return;
606 xmlListClear(l);
607 xmlListMerge(l, lTemp);
608 xmlListDelete(lTemp);
609 return;
610}
611
612/**
613 * xmlListWalk:
614 * @l: a list
615 * @walker: a processing function
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000616 * @user: a user parameter passed to the walker function
Owen Taylor3473f882001-02-23 17:55:21 +0000617 *
618 * Walk all the element of the first from first to last and
619 * apply the walker function to it
620 */
621void
622xmlListWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
623 xmlLinkPtr lk;
624
625 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
626 if((walker(lk->data, user)) == 0)
627 break;
628 }
629}
630
631/**
632 * xmlListReverseWalk:
633 * @l: a list
634 * @walker: a processing function
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000635 * @user: a user parameter passed to the walker function
Owen Taylor3473f882001-02-23 17:55:21 +0000636 *
637 * Walk all the element of the list in reverse order and
638 * apply the walker function to it
639 */
640void
641xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
642 xmlLinkPtr lk;
643
644 for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {
645 if((walker(lk->data, user)) == 0)
646 break;
647 }
648}
649
650/**
651 * xmlListMerge:
652 * @l1: the original list
653 * @l2: the new list
654 *
655 * include all the elements of the second list in the first one and
656 * clear the second list
657 */
658void
659xmlListMerge(xmlListPtr l1, xmlListPtr l2)
660{
661 xmlListCopy(l1, l2);
662 xmlListClear(l2);
663}
664
665/**
666 * xmlListDup:
667 * @old: the list
668 *
669 * Duplicate the list
670 *
671 * Returns a new copy of the list or NULL in case of error
672 */
673xmlListPtr
674xmlListDup(const xmlListPtr old)
675{
676 xmlListPtr cur;
677 /* Hmmm, how to best deal with allocation issues when copying
678 * lists. If there is a de-allocator, should responsibility lie with
679 * the new list or the old list. Surely not both. I'll arbitrarily
680 * set it to be the old list for the time being whilst I work out
681 * the answer
682 */
683 if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare)))
684 return (NULL);
685 if (0 != xmlListCopy(cur, old))
686 return NULL;
687 return cur;
688}
689
690/**
691 * xmlListCopy:
692 * @cur: the new list
693 * @old: the old list
694 *
695 * Move all the element from the old list in the new list
696 *
697 * Returns 0 in case of success 1 in case of error
698 */
699int
700xmlListCopy(xmlListPtr cur, const xmlListPtr old)
701{
702 /* Walk the old tree and insert the data into the new one */
703 xmlLinkPtr lk;
704
705 for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {
706 if (0 !=xmlListInsert(cur, lk->data)) {
707 xmlListDelete(cur);
708 return (1);
709 }
710 }
711 return (0);
712}
713/* xmlListUnique() */
714/* xmlListSwap */