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() */