2 * list.c: lists handling implementation
4 * Copyright (C) 2000 Gary Pennington and Daniel Veillard.
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.
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.
15 * Author: Gary.Pennington@uk.sun.com
23 #include <libxml/xmlmemory.h>
24 #include <libxml/list.h>
25 #include <libxml/globals.h>
28 * Type definition are kept internal
33 struct _xmlLink
*next
;
34 struct _xmlLink
*prev
;
41 void (*linkDeallocator
)(xmlLinkPtr
);
42 int (*linkCompare
)(const void *, const void*);
45 /************************************************************************
49 ************************************************************************/
56 * Unlink and deallocate @lk from list @l
59 xmlLinkDeallocator(xmlListPtr l
, xmlLinkPtr lk
)
61 (lk
->prev
)->next
= lk
->next
;
62 (lk
->next
)->prev
= lk
->prev
;
63 if(l
->linkDeallocator
)
64 l
->linkDeallocator(lk
);
73 * Compares two arbitrary data
75 * Returns -1, 0 or 1 depending on whether data1 is greater equal or smaller
79 xmlLinkCompare(const void *data0
, const void *data1
)
83 else if (data0
== data1
)
93 * Search data in the ordered list walking from the beginning
95 * Returns the link containing the data or NULL
98 xmlListLowerSearch(xmlListPtr l
, void *data
)
102 for(lk
= l
->sentinel
->next
;lk
!= l
->sentinel
&& l
->linkCompare(lk
->data
, data
) <0 ;lk
= lk
->next
);
107 * xmlListHigherSearch:
111 * Search data in the ordered list walking backward from the end
113 * Returns the link containing the data or NULL
116 xmlListHigherSearch(xmlListPtr l
, void *data
)
120 for(lk
= l
->sentinel
->prev
;lk
!= l
->sentinel
&& l
->linkCompare(lk
->data
, data
) >0 ;lk
= lk
->prev
);
129 * Search data in the list
131 * Returns the link containing the data or NULL
134 xmlListLinkSearch(xmlListPtr l
, void *data
)
137 lk
= xmlListLowerSearch(l
, data
);
138 if (lk
== l
->sentinel
)
141 if (l
->linkCompare(lk
->data
, data
) ==0)
148 * xmlListLinkReverseSearch:
152 * Search data in the list processing backward
154 * Returns the link containing the data or NULL
157 xmlListLinkReverseSearch(xmlListPtr l
, void *data
)
160 lk
= xmlListHigherSearch(l
, data
);
161 if (lk
== l
->sentinel
)
164 if (l
->linkCompare(lk
->data
, data
) ==0)
172 * @deallocator: an optional deallocator function
173 * @compare: an optional comparison function
177 * Returns the new list or NULL in case of error
180 xmlListCreate(xmlListDeallocator deallocator
, xmlListDataCompare compare
)
183 if (NULL
== (l
= (xmlListPtr
)xmlMalloc( sizeof(xmlList
)))) {
184 xmlGenericError(xmlGenericErrorContext
,
185 "Cannot initialize memory for list");
188 /* Initialize the list to NULL */
189 memset(l
, 0, sizeof(xmlList
));
191 /* Add the sentinel */
192 if (NULL
==(l
->sentinel
= (xmlLinkPtr
)xmlMalloc(sizeof(xmlLink
)))) {
193 xmlGenericError(xmlGenericErrorContext
,
194 "Cannot initialize memory for sentinel");
198 l
->sentinel
->next
= l
->sentinel
;
199 l
->sentinel
->prev
= l
->sentinel
;
200 l
->sentinel
->data
= NULL
;
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 */
207 l
->linkCompare
= compare
;
208 else /* Use our own */
209 l
->linkCompare
= xmlLinkCompare
;
216 * @data: a search value
218 * Search the list for an existing value of @data
220 * Returns the value associated to @data or NULL in case of error
223 xmlListSearch(xmlListPtr l
, void *data
)
226 lk
= xmlListLinkSearch(l
, data
);
233 * xmlListReverseSearch:
235 * @data: a search value
237 * Search the list in reverse order for an existing value of @data
239 * Returns the value associated to @data or NULL in case of error
242 xmlListReverseSearch(xmlListPtr l
, void *data
)
245 lk
= xmlListLinkReverseSearch(l
, data
);
256 * Insert data in the ordered list at the beginning for this value
258 * Returns 0 in case of success, 1 in case of failure
261 xmlListInsert(xmlListPtr l
, void *data
)
263 xmlLinkPtr lkPlace
, lkNew
;
265 lkPlace
= xmlListLowerSearch(l
, data
);
266 /* Add the new link */
267 lkNew
= (xmlLinkPtr
) xmlMalloc(sizeof(xmlLink
));
269 xmlGenericError(xmlGenericErrorContext
,
270 "Cannot initialize memory for new link");
274 lkPlace
= lkPlace
->prev
;
275 lkNew
->next
= lkPlace
->next
;
276 (lkPlace
->next
)->prev
= lkNew
;
277 lkPlace
->next
= lkNew
;
278 lkNew
->prev
= lkPlace
;
287 * Insert data in the ordered list at the end for this value
289 * Returns 0 in case of success, 1 in case of failure
291 int xmlListAppend(xmlListPtr l
, void *data
)
293 xmlLinkPtr lkPlace
, lkNew
;
295 lkPlace
= xmlListHigherSearch(l
, data
);
296 /* Add the new link */
297 lkNew
= (xmlLinkPtr
) xmlMalloc(sizeof(xmlLink
));
299 xmlGenericError(xmlGenericErrorContext
,
300 "Cannot initialize memory for new link");
304 lkNew
->next
= lkPlace
->next
;
305 (lkPlace
->next
)->prev
= lkNew
;
306 lkPlace
->next
= lkNew
;
307 lkNew
->prev
= lkPlace
;
315 * Deletes the list and its associated data
317 void xmlListDelete(xmlListPtr l
)
320 xmlFree(l
->sentinel
);
325 * xmlListRemoveFirst:
329 * Remove the first instance associated to data in the list
331 * Returns 1 if a deallocation occured, or 0 if not found
334 xmlListRemoveFirst(xmlListPtr l
, void *data
)
338 /*Find the first instance of this data */
339 lk
= xmlListLinkSearch(l
, data
);
341 xmlLinkDeallocator(l
, lk
);
352 * Remove the last instance associated to data in the list
354 * Returns 1 if a deallocation occured, or 0 if not found
357 xmlListRemoveLast(xmlListPtr l
, void *data
)
361 /*Find the last instance of this data */
362 lk
= xmlListLinkReverseSearch(l
, data
);
364 xmlLinkDeallocator(l
, lk
);
375 * Remove the all instance associated to data in the list
377 * Returns the number of deallocation, or 0 if not found
380 xmlListRemoveAll(xmlListPtr l
, void *data
)
385 while(xmlListRemoveFirst(l
, data
))
394 * Remove the all data in the list
397 xmlListClear(xmlListPtr l
)
399 xmlLinkPtr lk
= l
->sentinel
->next
;
401 while(lk
!= l
->sentinel
) {
402 xmlLinkPtr next
= lk
->next
;
404 xmlLinkDeallocator(l
, lk
);
413 * Is the list empty ?
415 * Returns 1 if the list is empty, 0 otherwise
418 xmlListEmpty(xmlListPtr l
)
420 return (l
->sentinel
->next
== l
->sentinel
);
427 * Get the first element in the list
429 * Returns the first element in the list, or NULL
432 xmlListFront(xmlListPtr l
)
434 return (l
->sentinel
->next
);
441 * Get the last element in the list
443 * Returns the last element in the list, or NULL
446 xmlListEnd(xmlListPtr l
)
448 return (l
->sentinel
->prev
);
455 * Get the number of elements in the list
457 * Returns the number of elements in the list
460 xmlListSize(xmlListPtr l
)
465 /* TODO: keep a counter in xmlList instead */
466 for(lk
= l
->sentinel
->next
; lk
!= l
->sentinel
; lk
= lk
->next
, count
++);
474 * Removes the first element in the list
477 xmlListPopFront(xmlListPtr l
)
480 xmlLinkDeallocator(l
, l
->sentinel
->next
);
487 * Removes the last element in the list
490 xmlListPopBack(xmlListPtr l
)
493 xmlLinkDeallocator(l
, l
->sentinel
->prev
);
501 * add the new data at the beginning of the list
503 * Returns 1 if successful, 0 otherwise
506 xmlListPushFront(xmlListPtr l
, void *data
)
508 xmlLinkPtr lkPlace
, lkNew
;
510 lkPlace
= l
->sentinel
;
511 /* Add the new link */
512 lkNew
= (xmlLinkPtr
) xmlMalloc(sizeof(xmlLink
));
514 xmlGenericError(xmlGenericErrorContext
,
515 "Cannot initialize memory for new link");
519 lkNew
->next
= lkPlace
->next
;
520 (lkPlace
->next
)->prev
= lkNew
;
521 lkPlace
->next
= lkNew
;
522 lkNew
->prev
= lkPlace
;
531 * add the new data at the end of the list
533 * Returns 1 if successful, 0 otherwise
536 xmlListPushBack(xmlListPtr l
, void *data
)
538 xmlLinkPtr lkPlace
, lkNew
;
540 lkPlace
= l
->sentinel
->prev
;
541 /* Add the new link */
542 if (NULL
==(lkNew
= (xmlLinkPtr
)xmlMalloc(sizeof(xmlLink
)))) {
543 xmlGenericError(xmlGenericErrorContext
,
544 "Cannot initialize memory for new link");
548 lkNew
->next
= lkPlace
->next
;
549 (lkPlace
->next
)->prev
= lkNew
;
550 lkPlace
->next
= lkNew
;
551 lkNew
->prev
= lkPlace
;
561 * Returns a pointer to the data referenced from this link
564 xmlLinkGetData(xmlLinkPtr lk
)
573 * Reverse the order of the elements in the list
576 xmlListReverse(xmlListPtr l
) {
578 xmlLinkPtr lkPrev
= l
->sentinel
;
580 for(lk
= l
->sentinel
->next
; lk
!= l
->sentinel
; lk
= lk
->next
) {
581 lkPrev
->next
= lkPrev
->prev
;
585 /* Fix up the last node */
586 lkPrev
->next
= lkPrev
->prev
;
594 * Sort all the elements in the list
597 xmlListSort(xmlListPtr l
)
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...
610 if (NULL
==(lTemp
= xmlListDup(l
)))
613 xmlListMerge(l
, lTemp
);
614 xmlListDelete(lTemp
);
621 * @walker: a processing function
622 * @user: a user parameter passed to the walker function
624 * Walk all the element of the first from first to last and
625 * apply the walker function to it
628 xmlListWalk(xmlListPtr l
, xmlListWalker walker
, const void *user
) {
631 for(lk
= l
->sentinel
->next
; lk
!= l
->sentinel
; lk
= lk
->next
) {
632 if((walker(lk
->data
, user
)) == 0)
638 * xmlListReverseWalk:
640 * @walker: a processing function
641 * @user: a user parameter passed to the walker function
643 * Walk all the element of the list in reverse order and
644 * apply the walker function to it
647 xmlListReverseWalk(xmlListPtr l
, xmlListWalker walker
, const void *user
) {
650 for(lk
= l
->sentinel
->prev
; lk
!= l
->sentinel
; lk
= lk
->prev
) {
651 if((walker(lk
->data
, user
)) == 0)
658 * @l1: the original list
661 * include all the elements of the second list in the first one and
662 * clear the second list
665 xmlListMerge(xmlListPtr l1
, xmlListPtr l2
)
677 * Returns a new copy of the list or NULL in case of error
680 xmlListDup(const xmlListPtr old
)
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
689 if (NULL
==(cur
= xmlListCreate(NULL
, old
->linkCompare
)))
691 if (0 != xmlListCopy(cur
, old
))
701 * Move all the element from the old list in the new list
703 * Returns 0 in case of success 1 in case of error
706 xmlListCopy(xmlListPtr cur
, const xmlListPtr old
)
708 /* Walk the old tree and insert the data into the new one */
711 for(lk
= old
->sentinel
->next
; lk
!= old
->sentinel
; lk
= lk
->next
) {
712 if (0 !=xmlListInsert(cur
, lk
->data
)) {
719 /* xmlListUnique() */