blob: 0e05789c355acd26815a9f06f6092ef255b02d22 [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/**
229 * xmlListLinkReverseSearch:
230 * @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 *
407 * Returns 1 if the list is empty, 0 otherwise
408 */
409int
410xmlListEmpty(xmlListPtr l)
411{
412 return (l->sentinel->next == l->sentinel);
413}
414
415/**
416 * xmlListFront:
417 * @l: a list
418 *
419 * Returns the first element in the list, or NULL
420 */
421xmlLinkPtr
422xmlListFront(xmlListPtr l)
423{
424 return (l->sentinel->next);
425}
426
427/**
428 * xmlListFront:
429 * @l: a list
430 *
431 * Returns the last element in the list, or NULL
432 */
433xmlLinkPtr
434xmlListEnd(xmlListPtr l)
435{
436 return (l->sentinel->prev);
437}
438
439/**
440 * xmlListSize:
441 * @l: a list
442 *
443 * Returns the number of elements in the list
444 */
445int
446xmlListSize(xmlListPtr l)
447{
448 xmlLinkPtr lk;
449 int count=0;
450
451 /* TODO: keep a counter in xmlList instead */
452 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);
453 return count;
454}
455
456/**
457 * xmlListPopFront:
458 * @l: a list
459 *
460 * Removes the first element in the list
461 */
462void
463xmlListPopFront(xmlListPtr l)
464{
465 if(!xmlListEmpty(l))
466 xmlLinkDeallocator(l, l->sentinel->next);
467}
468
469/**
470 * xmlListPopBack:
471 * @l: a list
472 *
473 * Removes the last element in the list
474 */
475void
476xmlListPopBack(xmlListPtr l)
477{
478 if(!xmlListEmpty(l))
479 xmlLinkDeallocator(l, l->sentinel->prev);
480}
481
482/**
483 * xmlListPushFront:
484 * @l: a list
485 * @data: new data
486 *
487 * add the new data at the beginning of the list
488 *
489 * Returns 1 if successful, 0 otherwise
490 */
491int
492xmlListPushFront(xmlListPtr l, void *data)
493{
494 xmlLinkPtr lkPlace, lkNew;
495
496 lkPlace = l->sentinel;
497 /* Add the new link */
498 lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
499 if (lkNew == NULL) {
500 perror("Cannot initialize memory for new link");
501 return (0);
502 }
503 lkNew->data = data;
504 lkNew->next = lkPlace->next;
505 (lkPlace->next)->prev = lkNew;
506 lkPlace->next = lkNew;
507 lkNew->prev = lkPlace;
508 return 1;
509}
510
511/**
512 * xmlListPushBack:
513 * @l: a list
514 * @data: new data
515 *
516 * add the new data at the end of the list
517 *
518 * Returns 1 if successful, 0 otherwise
519 */
520int
521xmlListPushBack(xmlListPtr l, void *data)
522{
523 xmlLinkPtr lkPlace, lkNew;
524
525 lkPlace = l->sentinel->prev;
526 /* Add the new link */
527 if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
528 perror("Cannot initialize memory for new link");
529 return (0);
530 }
531 lkNew->data = data;
532 lkNew->next = lkPlace->next;
533 (lkPlace->next)->prev = lkNew;
534 lkPlace->next = lkNew;
535 lkNew->prev = lkPlace;
536 return 1;
537}
538
539/**
540 * xmlLinkGetData:
541 * @lk: a link
542 *
543 * See Returns.
544 *
545 * Returns a pointer to the data referenced from this link
546 */
547void *
548xmlLinkGetData(xmlLinkPtr lk)
549{
550 return lk->data;
551}
552
553/**
554 * xmlListReverse:
555 * @l: a list
556 *
557 * Reverse the order of the elements in the list
558 */
559void
560xmlListReverse(xmlListPtr l) {
561 xmlLinkPtr lk;
562 xmlLinkPtr lkPrev = l->sentinel;
563
564 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
565 lkPrev->next = lkPrev->prev;
566 lkPrev->prev = lk;
567 lkPrev = lk;
568 }
569 /* Fix up the last node */
570 lkPrev->next = lkPrev->prev;
571 lkPrev->prev = lk;
572}
573
574/**
575 * xmlListSort:
576 * @l: a list
577 *
578 * Sort all the elements in the list
579 */
580void
581xmlListSort(xmlListPtr l)
582{
583 xmlListPtr lTemp;
584
585 if(xmlListEmpty(l))
586 return;
587
588 /* I think that the real answer is to implement quicksort, the
589 * alternative is to implement some list copying procedure which
590 * would be based on a list copy followed by a clear followed by
591 * an insert. This is slow...
592 */
593
594 if (NULL ==(lTemp = xmlListDup(l)))
595 return;
596 xmlListClear(l);
597 xmlListMerge(l, lTemp);
598 xmlListDelete(lTemp);
599 return;
600}
601
602/**
603 * xmlListWalk:
604 * @l: a list
605 * @walker: a processing function
606 *
607 * Walk all the element of the first from first to last and
608 * apply the walker function to it
609 */
610void
611xmlListWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
612 xmlLinkPtr lk;
613
614 for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
615 if((walker(lk->data, user)) == 0)
616 break;
617 }
618}
619
620/**
621 * xmlListReverseWalk:
622 * @l: a list
623 * @walker: a processing function
624 *
625 * Walk all the element of the list in reverse order and
626 * apply the walker function to it
627 */
628void
629xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
630 xmlLinkPtr lk;
631
632 for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {
633 if((walker(lk->data, user)) == 0)
634 break;
635 }
636}
637
638/**
639 * xmlListMerge:
640 * @l1: the original list
641 * @l2: the new list
642 *
643 * include all the elements of the second list in the first one and
644 * clear the second list
645 */
646void
647xmlListMerge(xmlListPtr l1, xmlListPtr l2)
648{
649 xmlListCopy(l1, l2);
650 xmlListClear(l2);
651}
652
653/**
654 * xmlListDup:
655 * @old: the list
656 *
657 * Duplicate the list
658 *
659 * Returns a new copy of the list or NULL in case of error
660 */
661xmlListPtr
662xmlListDup(const xmlListPtr old)
663{
664 xmlListPtr cur;
665 /* Hmmm, how to best deal with allocation issues when copying
666 * lists. If there is a de-allocator, should responsibility lie with
667 * the new list or the old list. Surely not both. I'll arbitrarily
668 * set it to be the old list for the time being whilst I work out
669 * the answer
670 */
671 if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare)))
672 return (NULL);
673 if (0 != xmlListCopy(cur, old))
674 return NULL;
675 return cur;
676}
677
678/**
679 * xmlListCopy:
680 * @cur: the new list
681 * @old: the old list
682 *
683 * Move all the element from the old list in the new list
684 *
685 * Returns 0 in case of success 1 in case of error
686 */
687int
688xmlListCopy(xmlListPtr cur, const xmlListPtr old)
689{
690 /* Walk the old tree and insert the data into the new one */
691 xmlLinkPtr lk;
692
693 for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {
694 if (0 !=xmlListInsert(cur, lk->data)) {
695 xmlListDelete(cur);
696 return (1);
697 }
698 }
699 return (0);
700}
701/* xmlListUnique() */
702/* xmlListSwap */