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