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