blob: 18a82973e5c36109e71c4f54dcd18909e8c31ca2 [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)))) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000184 xmlGenericError(xmlGenericErrorContext,
185 "Cannot initialize memory for list");
Owen Taylor3473f882001-02-23 17:55:21 +0000186 return (NULL);
187 }
188 /* Initialize the list to NULL */
189 memset(l, 0, sizeof(xmlList));
190
191 /* Add the sentinel */
192 if (NULL ==(l->sentinel = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000193 xmlGenericError(xmlGenericErrorContext,
194 "Cannot initialize memory for sentinel");
Owen Taylor3473f882001-02-23 17:55:21 +0000195 xmlFree(l);
196 return (NULL);
197 }
198 l->sentinel->next = l->sentinel;
199 l->sentinel->prev = l->sentinel;
200 l->sentinel->data = NULL;
201
202 /* If there is a link deallocator, use it */
203 if (deallocator != NULL)
204 l->linkDeallocator = deallocator;
205 /* If there is a link comparator, use it */
206 if (compare != NULL)
207 l->linkCompare = compare;
208 else /* Use our own */
209 l->linkCompare = xmlLinkCompare;
210 return l;
211}
212
213/**
214 * xmlListSearch:
215 * @l: a list
216 * @data: a search value
217 *
218 * Search the list for an existing value of @data
219 *
220 * Returns the value associated to @data or NULL in case of error
221 */
222void *
223xmlListSearch(xmlListPtr l, void *data)
224{
225 xmlLinkPtr lk;
226 lk = xmlListLinkSearch(l, data);
227 if (lk)
228 return (lk->data);
229 return NULL;
230}
231
232/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000233 * xmlListReverseSearch:
Owen Taylor3473f882001-02-23 17:55:21 +0000234 * @l: a list
235 * @data: a search value
236 *
237 * Search the list in reverse order for an existing value of @data
238 *
239 * Returns the value associated to @data or NULL in case of error
240 */
241void *
242xmlListReverseSearch(xmlListPtr l, void *data)
243{
244 xmlLinkPtr lk;
245 lk = xmlListLinkReverseSearch(l, data);
246 if (lk)
247 return (lk->data);
248 return NULL;
249}
250
251/**
252 * xmlListInsert:
253 * @l: a list
254 * @data: the data
255 *
256 * Insert data in the ordered list at the beginning for this value
257 *
258 * Returns 0 in case of success, 1 in case of failure
259 */
260int
261xmlListInsert(xmlListPtr l, void *data)
262{
263 xmlLinkPtr lkPlace, lkNew;
264
265 lkPlace = xmlListLowerSearch(l, data);
266 /* Add the new link */
267 lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
268 if (lkNew == NULL) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000269 xmlGenericError(xmlGenericErrorContext,
270 "Cannot initialize memory for new link");
Owen Taylor3473f882001-02-23 17:55:21 +0000271 return (1);
272 }
273 lkNew->data = data;
274 lkPlace = lkPlace->prev;
275 lkNew->next = lkPlace->next;
276 (lkPlace->next)->prev = lkNew;
277 lkPlace->next = lkNew;
278 lkNew->prev = lkPlace;
279 return 0;
280}
281
282/**
283 * xmlListAppend:
284 * @l: a list
285 * @data: the data
286 *
287 * Insert data in the ordered list at the end for this value
288 *
289 * Returns 0 in case of success, 1 in case of failure
290 */
291int xmlListAppend(xmlListPtr l, void *data)
292{
293 xmlLinkPtr lkPlace, lkNew;
294
295 lkPlace = xmlListHigherSearch(l, data);
296 /* Add the new link */
297 lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
298 if (lkNew == NULL) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000299 xmlGenericError(xmlGenericErrorContext,
300 "Cannot initialize memory for new link");
Owen Taylor3473f882001-02-23 17:55:21 +0000301 return (0);
302 }
303 lkNew->data = data;
304 lkNew->next = lkPlace->next;
305 (lkPlace->next)->prev = lkNew;
306 lkPlace->next = lkNew;
307 lkNew->prev = lkPlace;
308 return 1;
309}
310
311/**
312 * xmlListDelete:
313 * @l: a list
314 *
315 * Deletes the list and its associated data
316 */
317void xmlListDelete(xmlListPtr l)
318{
319 xmlListClear(l);
320 xmlFree(l->sentinel);
321 xmlFree(l);
322}
323
324/**
325 * xmlListRemoveFirst:
326 * @l: a list
327 * @data: list data
328 *
329 * Remove the first instance associated to data in the list
330 *
331 * Returns 1 if a deallocation occured, or 0 if not found
332 */
333int
334xmlListRemoveFirst(xmlListPtr l, void *data)
335{
336 xmlLinkPtr lk;
337
338 /*Find the first instance of this data */
339 lk = xmlListLinkSearch(l, data);
340 if (lk != NULL) {
341 xmlLinkDeallocator(l, lk);
342 return 1;
343 }
344 return 0;
345}
346
347/**
348 * xmlListRemoveLast:
349 * @l: a list
350 * @data: list data
351 *
352 * Remove the last instance associated to data in the list
353 *
354 * Returns 1 if a deallocation occured, or 0 if not found
355 */
356int
357xmlListRemoveLast(xmlListPtr l, void *data)
358{
359 xmlLinkPtr lk;
360
361 /*Find the last instance of this data */
362 lk = xmlListLinkReverseSearch(l, data);
363 if (lk != NULL) {
364 xmlLinkDeallocator(l, lk);
365 return 1;
366 }
367 return 0;
368}
369
370/**
371 * xmlListRemoveAll:
372 * @l: a list
373 * @data: list data
374 *
375 * Remove the all instance associated to data in the list
376 *
377 * Returns the number of deallocation, or 0 if not found
378 */
379int
380xmlListRemoveAll(xmlListPtr l, void *data)
381{
382 int count=0;
383
384
385 while(xmlListRemoveFirst(l, data))
386 count++;
387 return count;
388}
389
390/**
391 * xmlListClear:
392 * @l: a list
393 *
394 * Remove the all data in the list
395 */
396void
397xmlListClear(xmlListPtr l)
398{
399 xmlLinkPtr lk = l->sentinel->next;
400
401 while(lk != l->sentinel) {
402 xmlLinkPtr next = lk->next;
403
404 xmlLinkDeallocator(l, lk);
405 lk = next;
406 }
407}
408
409/**
410 * xmlListEmpty:
411 * @l: a list
412 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000413 * Is the list empty ?
414 *
Owen Taylor3473f882001-02-23 17:55:21 +0000415 * Returns 1 if the list is empty, 0 otherwise
416 */
417int
418xmlListEmpty(xmlListPtr l)
419{
420 return (l->sentinel->next == l->sentinel);
421}
422
423/**
424 * xmlListFront:
425 * @l: a list
426 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000427 * Get the first element in the list
428 *
Owen Taylor3473f882001-02-23 17:55:21 +0000429 * Returns the first element in the list, or NULL
430 */
431xmlLinkPtr
432xmlListFront(xmlListPtr l)
433{
434 return (l->sentinel->next);
435}
436
437/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000438 * xmlListEnd:
Owen Taylor3473f882001-02-23 17:55:21 +0000439 * @l: a list
440 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000441 * Get the last element in the list
442 *
Owen Taylor3473f882001-02-23 17:55:21 +0000443 * Returns the last element in the list, or NULL
444 */
445xmlLinkPtr
446xmlListEnd(xmlListPtr l)
447{
448 return (l->sentinel->prev);
449}
450
451/**
452 * xmlListSize:
453 * @l: a list
454 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000455 * Get the number of elements in the list
456 *
Owen Taylor3473f882001-02-23 17:55:21 +0000457 * Returns the number of elements in the list
458 */
459int
460xmlListSize(xmlListPtr l)
461{
462 xmlLinkPtr lk;
463 int count=0;
464
465 /* TODO: keep a counter in xmlList instead */
466 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);
467 return count;
468}
469
470/**
471 * xmlListPopFront:
472 * @l: a list
473 *
474 * Removes the first element in the list
475 */
476void
477xmlListPopFront(xmlListPtr l)
478{
479 if(!xmlListEmpty(l))
480 xmlLinkDeallocator(l, l->sentinel->next);
481}
482
483/**
484 * xmlListPopBack:
485 * @l: a list
486 *
487 * Removes the last element in the list
488 */
489void
490xmlListPopBack(xmlListPtr l)
491{
492 if(!xmlListEmpty(l))
493 xmlLinkDeallocator(l, l->sentinel->prev);
494}
495
496/**
497 * xmlListPushFront:
498 * @l: a list
499 * @data: new data
500 *
501 * add the new data at the beginning of the list
502 *
503 * Returns 1 if successful, 0 otherwise
504 */
505int
506xmlListPushFront(xmlListPtr l, void *data)
507{
508 xmlLinkPtr lkPlace, lkNew;
509
510 lkPlace = l->sentinel;
511 /* Add the new link */
512 lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
513 if (lkNew == NULL) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000514 xmlGenericError(xmlGenericErrorContext,
515 "Cannot initialize memory for new link");
Owen Taylor3473f882001-02-23 17:55:21 +0000516 return (0);
517 }
518 lkNew->data = data;
519 lkNew->next = lkPlace->next;
520 (lkPlace->next)->prev = lkNew;
521 lkPlace->next = lkNew;
522 lkNew->prev = lkPlace;
523 return 1;
524}
525
526/**
527 * xmlListPushBack:
528 * @l: a list
529 * @data: new data
530 *
531 * add the new data at the end of the list
532 *
533 * Returns 1 if successful, 0 otherwise
534 */
535int
536xmlListPushBack(xmlListPtr l, void *data)
537{
538 xmlLinkPtr lkPlace, lkNew;
539
540 lkPlace = l->sentinel->prev;
541 /* Add the new link */
542 if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000543 xmlGenericError(xmlGenericErrorContext,
544 "Cannot initialize memory for new link");
Owen Taylor3473f882001-02-23 17:55:21 +0000545 return (0);
546 }
547 lkNew->data = data;
548 lkNew->next = lkPlace->next;
549 (lkPlace->next)->prev = lkNew;
550 lkPlace->next = lkNew;
551 lkNew->prev = lkPlace;
552 return 1;
553}
554
555/**
556 * xmlLinkGetData:
557 * @lk: a link
558 *
559 * See Returns.
560 *
561 * Returns a pointer to the data referenced from this link
562 */
563void *
564xmlLinkGetData(xmlLinkPtr lk)
565{
566 return lk->data;
567}
568
569/**
570 * xmlListReverse:
571 * @l: a list
572 *
573 * Reverse the order of the elements in the list
574 */
575void
576xmlListReverse(xmlListPtr l) {
577 xmlLinkPtr lk;
578 xmlLinkPtr lkPrev = l->sentinel;
579
580 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
581 lkPrev->next = lkPrev->prev;
582 lkPrev->prev = lk;
583 lkPrev = lk;
584 }
585 /* Fix up the last node */
586 lkPrev->next = lkPrev->prev;
587 lkPrev->prev = lk;
588}
589
590/**
591 * xmlListSort:
592 * @l: a list
593 *
594 * Sort all the elements in the list
595 */
596void
597xmlListSort(xmlListPtr l)
598{
599 xmlListPtr lTemp;
600
601 if(xmlListEmpty(l))
602 return;
603
604 /* I think that the real answer is to implement quicksort, the
605 * alternative is to implement some list copying procedure which
606 * would be based on a list copy followed by a clear followed by
607 * an insert. This is slow...
608 */
609
610 if (NULL ==(lTemp = xmlListDup(l)))
611 return;
612 xmlListClear(l);
613 xmlListMerge(l, lTemp);
614 xmlListDelete(lTemp);
615 return;
616}
617
618/**
619 * xmlListWalk:
620 * @l: a list
621 * @walker: a processing function
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000622 * @user: a user parameter passed to the walker function
Owen Taylor3473f882001-02-23 17:55:21 +0000623 *
624 * Walk all the element of the first from first to last and
625 * apply the walker function to it
626 */
627void
628xmlListWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
629 xmlLinkPtr lk;
630
631 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
632 if((walker(lk->data, user)) == 0)
633 break;
634 }
635}
636
637/**
638 * xmlListReverseWalk:
639 * @l: a list
640 * @walker: a processing function
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000641 * @user: a user parameter passed to the walker function
Owen Taylor3473f882001-02-23 17:55:21 +0000642 *
643 * Walk all the element of the list in reverse order and
644 * apply the walker function to it
645 */
646void
647xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
648 xmlLinkPtr lk;
649
650 for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {
651 if((walker(lk->data, user)) == 0)
652 break;
653 }
654}
655
656/**
657 * xmlListMerge:
658 * @l1: the original list
659 * @l2: the new list
660 *
661 * include all the elements of the second list in the first one and
662 * clear the second list
663 */
664void
665xmlListMerge(xmlListPtr l1, xmlListPtr l2)
666{
667 xmlListCopy(l1, l2);
668 xmlListClear(l2);
669}
670
671/**
672 * xmlListDup:
673 * @old: the list
674 *
675 * Duplicate the list
676 *
677 * Returns a new copy of the list or NULL in case of error
678 */
679xmlListPtr
680xmlListDup(const xmlListPtr old)
681{
682 xmlListPtr cur;
683 /* Hmmm, how to best deal with allocation issues when copying
684 * lists. If there is a de-allocator, should responsibility lie with
685 * the new list or the old list. Surely not both. I'll arbitrarily
686 * set it to be the old list for the time being whilst I work out
687 * the answer
688 */
689 if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare)))
690 return (NULL);
691 if (0 != xmlListCopy(cur, old))
692 return NULL;
693 return cur;
694}
695
696/**
697 * xmlListCopy:
698 * @cur: the new list
699 * @old: the old list
700 *
701 * Move all the element from the old list in the new list
702 *
703 * Returns 0 in case of success 1 in case of error
704 */
705int
706xmlListCopy(xmlListPtr cur, const xmlListPtr old)
707{
708 /* Walk the old tree and insert the data into the new one */
709 xmlLinkPtr lk;
710
711 for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {
712 if (0 !=xmlListInsert(cur, lk->data)) {
713 xmlListDelete(cur);
714 return (1);
715 }
716 }
717 return (0);
718}
719/* xmlListUnique() */
720/* xmlListSwap */