blob: f8fb18e554f2da477bc89186f0daf9d1cd491b11 [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{
Daniel Veillarde43cc572004-11-03 11:50:29 +0000319 if (l == NULL)
320 return;
321
Owen Taylor3473f882001-02-23 17:55:21 +0000322 xmlListClear(l);
323 xmlFree(l->sentinel);
324 xmlFree(l);
325}
326
327/**
328 * xmlListRemoveFirst:
329 * @l: a list
330 * @data: list data
331 *
332 * Remove the first instance associated to data in the list
333 *
334 * Returns 1 if a deallocation occured, or 0 if not found
335 */
336int
337xmlListRemoveFirst(xmlListPtr l, void *data)
338{
339 xmlLinkPtr lk;
340
341 /*Find the first instance of this data */
342 lk = xmlListLinkSearch(l, data);
343 if (lk != NULL) {
344 xmlLinkDeallocator(l, lk);
345 return 1;
346 }
347 return 0;
348}
349
350/**
351 * xmlListRemoveLast:
352 * @l: a list
353 * @data: list data
354 *
355 * Remove the last instance associated to data in the list
356 *
357 * Returns 1 if a deallocation occured, or 0 if not found
358 */
359int
360xmlListRemoveLast(xmlListPtr l, void *data)
361{
362 xmlLinkPtr lk;
363
364 /*Find the last instance of this data */
365 lk = xmlListLinkReverseSearch(l, data);
366 if (lk != NULL) {
367 xmlLinkDeallocator(l, lk);
368 return 1;
369 }
370 return 0;
371}
372
373/**
374 * xmlListRemoveAll:
375 * @l: a list
376 * @data: list data
377 *
378 * Remove the all instance associated to data in the list
379 *
380 * Returns the number of deallocation, or 0 if not found
381 */
382int
383xmlListRemoveAll(xmlListPtr l, void *data)
384{
385 int count=0;
386
387
388 while(xmlListRemoveFirst(l, data))
389 count++;
390 return count;
391}
392
393/**
394 * xmlListClear:
395 * @l: a list
396 *
397 * Remove the all data in the list
398 */
399void
400xmlListClear(xmlListPtr l)
401{
402 xmlLinkPtr lk = l->sentinel->next;
403
404 while(lk != l->sentinel) {
405 xmlLinkPtr next = lk->next;
406
407 xmlLinkDeallocator(l, lk);
408 lk = next;
409 }
410}
411
412/**
413 * xmlListEmpty:
414 * @l: a list
415 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000416 * Is the list empty ?
417 *
Owen Taylor3473f882001-02-23 17:55:21 +0000418 * Returns 1 if the list is empty, 0 otherwise
419 */
420int
421xmlListEmpty(xmlListPtr l)
422{
423 return (l->sentinel->next == l->sentinel);
424}
425
426/**
427 * xmlListFront:
428 * @l: a list
429 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000430 * Get the first element in the list
431 *
Owen Taylor3473f882001-02-23 17:55:21 +0000432 * Returns the first element in the list, or NULL
433 */
434xmlLinkPtr
435xmlListFront(xmlListPtr l)
436{
437 return (l->sentinel->next);
438}
439
440/**
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000441 * xmlListEnd:
Owen Taylor3473f882001-02-23 17:55:21 +0000442 * @l: a list
443 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000444 * Get the last element in the list
445 *
Owen Taylor3473f882001-02-23 17:55:21 +0000446 * Returns the last element in the list, or NULL
447 */
448xmlLinkPtr
449xmlListEnd(xmlListPtr l)
450{
451 return (l->sentinel->prev);
452}
453
454/**
455 * xmlListSize:
456 * @l: a list
457 *
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000458 * Get the number of elements in the list
459 *
Owen Taylor3473f882001-02-23 17:55:21 +0000460 * Returns the number of elements in the list
461 */
462int
463xmlListSize(xmlListPtr l)
464{
465 xmlLinkPtr lk;
466 int count=0;
467
468 /* TODO: keep a counter in xmlList instead */
469 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);
470 return count;
471}
472
473/**
474 * xmlListPopFront:
475 * @l: a list
476 *
477 * Removes the first element in the list
478 */
479void
480xmlListPopFront(xmlListPtr l)
481{
482 if(!xmlListEmpty(l))
483 xmlLinkDeallocator(l, l->sentinel->next);
484}
485
486/**
487 * xmlListPopBack:
488 * @l: a list
489 *
490 * Removes the last element in the list
491 */
492void
493xmlListPopBack(xmlListPtr l)
494{
495 if(!xmlListEmpty(l))
496 xmlLinkDeallocator(l, l->sentinel->prev);
497}
498
499/**
500 * xmlListPushFront:
501 * @l: a list
502 * @data: new data
503 *
504 * add the new data at the beginning of the list
505 *
506 * Returns 1 if successful, 0 otherwise
507 */
508int
509xmlListPushFront(xmlListPtr l, void *data)
510{
511 xmlLinkPtr lkPlace, lkNew;
512
513 lkPlace = l->sentinel;
514 /* Add the new link */
515 lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
516 if (lkNew == NULL) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000517 xmlGenericError(xmlGenericErrorContext,
518 "Cannot initialize memory for new link");
Owen Taylor3473f882001-02-23 17:55:21 +0000519 return (0);
520 }
521 lkNew->data = data;
522 lkNew->next = lkPlace->next;
523 (lkPlace->next)->prev = lkNew;
524 lkPlace->next = lkNew;
525 lkNew->prev = lkPlace;
526 return 1;
527}
528
529/**
530 * xmlListPushBack:
531 * @l: a list
532 * @data: new data
533 *
534 * add the new data at the end of the list
535 *
536 * Returns 1 if successful, 0 otherwise
537 */
538int
539xmlListPushBack(xmlListPtr l, void *data)
540{
541 xmlLinkPtr lkPlace, lkNew;
542
543 lkPlace = l->sentinel->prev;
544 /* Add the new link */
545 if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
Daniel Veillard3487c8d2002-09-05 11:33:25 +0000546 xmlGenericError(xmlGenericErrorContext,
547 "Cannot initialize memory for new link");
Owen Taylor3473f882001-02-23 17:55:21 +0000548 return (0);
549 }
550 lkNew->data = data;
551 lkNew->next = lkPlace->next;
552 (lkPlace->next)->prev = lkNew;
553 lkPlace->next = lkNew;
554 lkNew->prev = lkPlace;
555 return 1;
556}
557
558/**
559 * xmlLinkGetData:
560 * @lk: a link
561 *
562 * See Returns.
563 *
564 * Returns a pointer to the data referenced from this link
565 */
566void *
567xmlLinkGetData(xmlLinkPtr lk)
568{
569 return lk->data;
570}
571
572/**
573 * xmlListReverse:
574 * @l: a list
575 *
576 * Reverse the order of the elements in the list
577 */
578void
579xmlListReverse(xmlListPtr l) {
580 xmlLinkPtr lk;
581 xmlLinkPtr lkPrev = l->sentinel;
582
583 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
584 lkPrev->next = lkPrev->prev;
585 lkPrev->prev = lk;
586 lkPrev = lk;
587 }
588 /* Fix up the last node */
589 lkPrev->next = lkPrev->prev;
590 lkPrev->prev = lk;
591}
592
593/**
594 * xmlListSort:
595 * @l: a list
596 *
597 * Sort all the elements in the list
598 */
599void
600xmlListSort(xmlListPtr l)
601{
602 xmlListPtr lTemp;
603
604 if(xmlListEmpty(l))
605 return;
606
607 /* I think that the real answer is to implement quicksort, the
608 * alternative is to implement some list copying procedure which
609 * would be based on a list copy followed by a clear followed by
610 * an insert. This is slow...
611 */
612
613 if (NULL ==(lTemp = xmlListDup(l)))
614 return;
615 xmlListClear(l);
616 xmlListMerge(l, lTemp);
617 xmlListDelete(lTemp);
618 return;
619}
620
621/**
622 * xmlListWalk:
623 * @l: a list
624 * @walker: a processing function
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000625 * @user: a user parameter passed to the walker function
Owen Taylor3473f882001-02-23 17:55:21 +0000626 *
627 * Walk all the element of the first from first to last and
628 * apply the walker function to it
629 */
630void
631xmlListWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
632 xmlLinkPtr lk;
633
634 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
635 if((walker(lk->data, user)) == 0)
636 break;
637 }
638}
639
640/**
641 * xmlListReverseWalk:
642 * @l: a list
643 * @walker: a processing function
Daniel Veillard5e2dace2001-07-18 19:30:27 +0000644 * @user: a user parameter passed to the walker function
Owen Taylor3473f882001-02-23 17:55:21 +0000645 *
646 * Walk all the element of the list in reverse order and
647 * apply the walker function to it
648 */
649void
650xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
651 xmlLinkPtr lk;
652
653 for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {
654 if((walker(lk->data, user)) == 0)
655 break;
656 }
657}
658
659/**
660 * xmlListMerge:
661 * @l1: the original list
662 * @l2: the new list
663 *
664 * include all the elements of the second list in the first one and
665 * clear the second list
666 */
667void
668xmlListMerge(xmlListPtr l1, xmlListPtr l2)
669{
670 xmlListCopy(l1, l2);
671 xmlListClear(l2);
672}
673
674/**
675 * xmlListDup:
676 * @old: the list
677 *
678 * Duplicate the list
679 *
680 * Returns a new copy of the list or NULL in case of error
681 */
682xmlListPtr
683xmlListDup(const xmlListPtr old)
684{
685 xmlListPtr cur;
686 /* Hmmm, how to best deal with allocation issues when copying
687 * lists. If there is a de-allocator, should responsibility lie with
688 * the new list or the old list. Surely not both. I'll arbitrarily
689 * set it to be the old list for the time being whilst I work out
690 * the answer
691 */
692 if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare)))
693 return (NULL);
694 if (0 != xmlListCopy(cur, old))
695 return NULL;
696 return cur;
697}
698
699/**
700 * xmlListCopy:
701 * @cur: the new list
702 * @old: the old list
703 *
704 * Move all the element from the old list in the new list
705 *
706 * Returns 0 in case of success 1 in case of error
707 */
708int
709xmlListCopy(xmlListPtr cur, const xmlListPtr old)
710{
711 /* Walk the old tree and insert the data into the new one */
712 xmlLinkPtr lk;
713
714 for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {
715 if (0 !=xmlListInsert(cur, lk->data)) {
716 xmlListDelete(cur);
717 return (1);
718 }
719 }
720 return (0);
721}
722/* xmlListUnique() */
723/* xmlListSwap */