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