Home | History | Annotate | Download | only in libxml2
      1 /*
      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 (at) uk.sun.com
     16  */
     17 
     18 #define IN_LIBXML
     19 #include "libxml.h"
     20 
     21 #include <stdlib.h>
     22 #include <string.h>
     23 #include <libxml/xmlmemory.h>
     24 #include <libxml/list.h>
     25 #include <libxml/globals.h>
     26 
     27 /*
     28  * Type definition are kept internal
     29  */
     30 
     31 struct _xmlLink
     32 {
     33     struct _xmlLink *next;
     34     struct _xmlLink *prev;
     35     void *data;
     36 };
     37 
     38 struct _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  */
     58 static void
     59 xmlLinkDeallocator(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  */
     78 static int
     79 xmlLinkCompare(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  */
     97 static xmlLinkPtr
     98 xmlListLowerSearch(xmlListPtr l, void *data)
     99 {
    100     xmlLinkPtr lk;
    101 
    102     if (l == NULL)
    103         return(NULL);
    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  */
    117 static xmlLinkPtr
    118 xmlListHigherSearch(xmlListPtr l, void *data)
    119 {
    120     xmlLinkPtr lk;
    121 
    122     if (l == NULL)
    123         return(NULL);
    124     for(lk = l->sentinel->prev;lk != l->sentinel && l->linkCompare(lk->data, data) >0 ;lk = lk->prev);
    125     return lk;
    126 }
    127 
    128 /**
    129  * xmlListSearch:
    130  * @l:  a list
    131  * @data:  a data
    132  *
    133  * Search data in the list
    134  *
    135  * Returns the link containing the data or NULL
    136  */
    137 static xmlLinkPtr
    138 xmlListLinkSearch(xmlListPtr l, void *data)
    139 {
    140     xmlLinkPtr lk;
    141     if (l == NULL)
    142         return(NULL);
    143     lk = xmlListLowerSearch(l, data);
    144     if (lk == l->sentinel)
    145         return NULL;
    146     else {
    147         if (l->linkCompare(lk->data, data) ==0)
    148             return lk;
    149         return NULL;
    150     }
    151 }
    152 
    153 /**
    154  * xmlListLinkReverseSearch:
    155  * @l:  a list
    156  * @data:  a data
    157  *
    158  * Search data in the list processing backward
    159  *
    160  * Returns the link containing the data or NULL
    161  */
    162 static xmlLinkPtr
    163 xmlListLinkReverseSearch(xmlListPtr l, void *data)
    164 {
    165     xmlLinkPtr lk;
    166     if (l == NULL)
    167         return(NULL);
    168     lk = xmlListHigherSearch(l, data);
    169     if (lk == l->sentinel)
    170         return NULL;
    171     else {
    172         if (l->linkCompare(lk->data, data) ==0)
    173             return lk;
    174         return NULL;
    175     }
    176 }
    177 
    178 /**
    179  * xmlListCreate:
    180  * @deallocator:  an optional deallocator function
    181  * @compare:  an optional comparison function
    182  *
    183  * Create a new list
    184  *
    185  * Returns the new list or NULL in case of error
    186  */
    187 xmlListPtr
    188 xmlListCreate(xmlListDeallocator deallocator, xmlListDataCompare compare)
    189 {
    190     xmlListPtr l;
    191     if (NULL == (l = (xmlListPtr )xmlMalloc( sizeof(xmlList)))) {
    192         xmlGenericError(xmlGenericErrorContext,
    193 		        "Cannot initialize memory for list");
    194         return (NULL);
    195     }
    196     /* Initialize the list to NULL */
    197     memset(l, 0, sizeof(xmlList));
    198 
    199     /* Add the sentinel */
    200     if (NULL ==(l->sentinel = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
    201         xmlGenericError(xmlGenericErrorContext,
    202 		        "Cannot initialize memory for sentinel");
    203 	xmlFree(l);
    204         return (NULL);
    205     }
    206     l->sentinel->next = l->sentinel;
    207     l->sentinel->prev = l->sentinel;
    208     l->sentinel->data = NULL;
    209 
    210     /* If there is a link deallocator, use it */
    211     if (deallocator != NULL)
    212         l->linkDeallocator = deallocator;
    213     /* If there is a link comparator, use it */
    214     if (compare != NULL)
    215         l->linkCompare = compare;
    216     else /* Use our own */
    217         l->linkCompare = xmlLinkCompare;
    218     return l;
    219 }
    220 
    221 /**
    222  * xmlListSearch:
    223  * @l:  a list
    224  * @data:  a search value
    225  *
    226  * Search the list for an existing value of @data
    227  *
    228  * Returns the value associated to @data or NULL in case of error
    229  */
    230 void *
    231 xmlListSearch(xmlListPtr l, void *data)
    232 {
    233     xmlLinkPtr lk;
    234     if (l == NULL)
    235         return(NULL);
    236     lk = xmlListLinkSearch(l, data);
    237     if (lk)
    238         return (lk->data);
    239     return NULL;
    240 }
    241 
    242 /**
    243  * xmlListReverseSearch:
    244  * @l:  a list
    245  * @data:  a search value
    246  *
    247  * Search the list in reverse order for an existing value of @data
    248  *
    249  * Returns the value associated to @data or NULL in case of error
    250  */
    251 void *
    252 xmlListReverseSearch(xmlListPtr l, void *data)
    253 {
    254     xmlLinkPtr lk;
    255     if (l == NULL)
    256         return(NULL);
    257     lk = xmlListLinkReverseSearch(l, data);
    258     if (lk)
    259         return (lk->data);
    260     return NULL;
    261 }
    262 
    263 /**
    264  * xmlListInsert:
    265  * @l:  a list
    266  * @data:  the data
    267  *
    268  * Insert data in the ordered list at the beginning for this value
    269  *
    270  * Returns 0 in case of success, 1 in case of failure
    271  */
    272 int
    273 xmlListInsert(xmlListPtr l, void *data)
    274 {
    275     xmlLinkPtr lkPlace, lkNew;
    276 
    277     if (l == NULL)
    278         return(1);
    279     lkPlace = xmlListLowerSearch(l, data);
    280     /* Add the new link */
    281     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
    282     if (lkNew == NULL) {
    283         xmlGenericError(xmlGenericErrorContext,
    284 		        "Cannot initialize memory for new link");
    285         return (1);
    286     }
    287     lkNew->data = data;
    288     lkPlace = lkPlace->prev;
    289     lkNew->next = lkPlace->next;
    290     (lkPlace->next)->prev = lkNew;
    291     lkPlace->next = lkNew;
    292     lkNew->prev = lkPlace;
    293     return 0;
    294 }
    295 
    296 /**
    297  * xmlListAppend:
    298  * @l:  a list
    299  * @data:  the data
    300  *
    301  * Insert data in the ordered list at the end for this value
    302  *
    303  * Returns 0 in case of success, 1 in case of failure
    304  */
    305 int xmlListAppend(xmlListPtr l, void *data)
    306 {
    307     xmlLinkPtr lkPlace, lkNew;
    308 
    309     if (l == NULL)
    310         return(1);
    311     lkPlace = xmlListHigherSearch(l, data);
    312     /* Add the new link */
    313     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
    314     if (lkNew == NULL) {
    315         xmlGenericError(xmlGenericErrorContext,
    316 		        "Cannot initialize memory for new link");
    317         return (1);
    318     }
    319     lkNew->data = data;
    320     lkNew->next = lkPlace->next;
    321     (lkPlace->next)->prev = lkNew;
    322     lkPlace->next = lkNew;
    323     lkNew->prev = lkPlace;
    324     return 0;
    325 }
    326 
    327 /**
    328  * xmlListDelete:
    329  * @l:  a list
    330  *
    331  * Deletes the list and its associated data
    332  */
    333 void xmlListDelete(xmlListPtr l)
    334 {
    335     if (l == NULL)
    336         return;
    337 
    338     xmlListClear(l);
    339     xmlFree(l->sentinel);
    340     xmlFree(l);
    341 }
    342 
    343 /**
    344  * xmlListRemoveFirst:
    345  * @l:  a list
    346  * @data:  list data
    347  *
    348  * Remove the first instance associated to data in the list
    349  *
    350  * Returns 1 if a deallocation occured, or 0 if not found
    351  */
    352 int
    353 xmlListRemoveFirst(xmlListPtr l, void *data)
    354 {
    355     xmlLinkPtr lk;
    356 
    357     if (l == NULL)
    358         return(0);
    359     /*Find the first instance of this data */
    360     lk = xmlListLinkSearch(l, data);
    361     if (lk != NULL) {
    362         xmlLinkDeallocator(l, lk);
    363         return 1;
    364     }
    365     return 0;
    366 }
    367 
    368 /**
    369  * xmlListRemoveLast:
    370  * @l:  a list
    371  * @data:  list data
    372  *
    373  * Remove the last instance associated to data in the list
    374  *
    375  * Returns 1 if a deallocation occured, or 0 if not found
    376  */
    377 int
    378 xmlListRemoveLast(xmlListPtr l, void *data)
    379 {
    380     xmlLinkPtr lk;
    381 
    382     if (l == NULL)
    383         return(0);
    384     /*Find the last instance of this data */
    385     lk = xmlListLinkReverseSearch(l, data);
    386     if (lk != NULL) {
    387 	xmlLinkDeallocator(l, lk);
    388         return 1;
    389     }
    390     return 0;
    391 }
    392 
    393 /**
    394  * xmlListRemoveAll:
    395  * @l:  a list
    396  * @data:  list data
    397  *
    398  * Remove the all instance associated to data in the list
    399  *
    400  * Returns the number of deallocation, or 0 if not found
    401  */
    402 int
    403 xmlListRemoveAll(xmlListPtr l, void *data)
    404 {
    405     int count=0;
    406 
    407     if (l == NULL)
    408         return(0);
    409 
    410     while(xmlListRemoveFirst(l, data))
    411         count++;
    412     return count;
    413 }
    414 
    415 /**
    416  * xmlListClear:
    417  * @l:  a list
    418  *
    419  * Remove the all data in the list
    420  */
    421 void
    422 xmlListClear(xmlListPtr l)
    423 {
    424     xmlLinkPtr  lk;
    425 
    426     if (l == NULL)
    427         return;
    428     lk = l->sentinel->next;
    429     while(lk != l->sentinel) {
    430         xmlLinkPtr next = lk->next;
    431 
    432         xmlLinkDeallocator(l, lk);
    433         lk = next;
    434     }
    435 }
    436 
    437 /**
    438  * xmlListEmpty:
    439  * @l:  a list
    440  *
    441  * Is the list empty ?
    442  *
    443  * Returns 1 if the list is empty, 0 if not empty and -1 in case of error
    444  */
    445 int
    446 xmlListEmpty(xmlListPtr l)
    447 {
    448     if (l == NULL)
    449         return(-1);
    450     return (l->sentinel->next == l->sentinel);
    451 }
    452 
    453 /**
    454  * xmlListFront:
    455  * @l:  a list
    456  *
    457  * Get the first element in the list
    458  *
    459  * Returns the first element in the list, or NULL
    460  */
    461 xmlLinkPtr
    462 xmlListFront(xmlListPtr l)
    463 {
    464     if (l == NULL)
    465         return(NULL);
    466     return (l->sentinel->next);
    467 }
    468 
    469 /**
    470  * xmlListEnd:
    471  * @l:  a list
    472  *
    473  * Get the last element in the list
    474  *
    475  * Returns the last element in the list, or NULL
    476  */
    477 xmlLinkPtr
    478 xmlListEnd(xmlListPtr l)
    479 {
    480     if (l == NULL)
    481         return(NULL);
    482     return (l->sentinel->prev);
    483 }
    484 
    485 /**
    486  * xmlListSize:
    487  * @l:  a list
    488  *
    489  * Get the number of elements in the list
    490  *
    491  * Returns the number of elements in the list or -1 in case of error
    492  */
    493 int
    494 xmlListSize(xmlListPtr l)
    495 {
    496     xmlLinkPtr lk;
    497     int count=0;
    498 
    499     if (l == NULL)
    500         return(-1);
    501     /* TODO: keep a counter in xmlList instead */
    502     for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next, count++);
    503     return count;
    504 }
    505 
    506 /**
    507  * xmlListPopFront:
    508  * @l:  a list
    509  *
    510  * Removes the first element in the list
    511  */
    512 void
    513 xmlListPopFront(xmlListPtr l)
    514 {
    515     if(!xmlListEmpty(l))
    516         xmlLinkDeallocator(l, l->sentinel->next);
    517 }
    518 
    519 /**
    520  * xmlListPopBack:
    521  * @l:  a list
    522  *
    523  * Removes the last element in the list
    524  */
    525 void
    526 xmlListPopBack(xmlListPtr l)
    527 {
    528     if(!xmlListEmpty(l))
    529         xmlLinkDeallocator(l, l->sentinel->prev);
    530 }
    531 
    532 /**
    533  * xmlListPushFront:
    534  * @l:  a list
    535  * @data:  new data
    536  *
    537  * add the new data at the beginning of the list
    538  *
    539  * Returns 1 if successful, 0 otherwise
    540  */
    541 int
    542 xmlListPushFront(xmlListPtr l, void *data)
    543 {
    544     xmlLinkPtr lkPlace, lkNew;
    545 
    546     if (l == NULL)
    547         return(0);
    548     lkPlace = l->sentinel;
    549     /* Add the new link */
    550     lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink));
    551     if (lkNew == NULL) {
    552         xmlGenericError(xmlGenericErrorContext,
    553 		        "Cannot initialize memory for new link");
    554         return (0);
    555     }
    556     lkNew->data = data;
    557     lkNew->next = lkPlace->next;
    558     (lkPlace->next)->prev = lkNew;
    559     lkPlace->next = lkNew;
    560     lkNew->prev = lkPlace;
    561     return 1;
    562 }
    563 
    564 /**
    565  * xmlListPushBack:
    566  * @l:  a list
    567  * @data:  new data
    568  *
    569  * add the new data at the end of the list
    570  *
    571  * Returns 1 if successful, 0 otherwise
    572  */
    573 int
    574 xmlListPushBack(xmlListPtr l, void *data)
    575 {
    576     xmlLinkPtr lkPlace, lkNew;
    577 
    578     if (l == NULL)
    579         return(0);
    580     lkPlace = l->sentinel->prev;
    581     /* Add the new link */
    582     if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) {
    583         xmlGenericError(xmlGenericErrorContext,
    584 		        "Cannot initialize memory for new link");
    585         return (0);
    586     }
    587     lkNew->data = data;
    588     lkNew->next = lkPlace->next;
    589     (lkPlace->next)->prev = lkNew;
    590     lkPlace->next = lkNew;
    591     lkNew->prev = lkPlace;
    592     return 1;
    593 }
    594 
    595 /**
    596  * xmlLinkGetData:
    597  * @lk:  a link
    598  *
    599  * See Returns.
    600  *
    601  * Returns a pointer to the data referenced from this link
    602  */
    603 void *
    604 xmlLinkGetData(xmlLinkPtr lk)
    605 {
    606     if (lk == NULL)
    607         return(NULL);
    608     return lk->data;
    609 }
    610 
    611 /**
    612  * xmlListReverse:
    613  * @l:  a list
    614  *
    615  * Reverse the order of the elements in the list
    616  */
    617 void
    618 xmlListReverse(xmlListPtr l)
    619 {
    620     xmlLinkPtr lk;
    621     xmlLinkPtr lkPrev;
    622 
    623     if (l == NULL)
    624         return;
    625     lkPrev = l->sentinel;
    626     for (lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
    627         lkPrev->next = lkPrev->prev;
    628         lkPrev->prev = lk;
    629         lkPrev = lk;
    630     }
    631     /* Fix up the last node */
    632     lkPrev->next = lkPrev->prev;
    633     lkPrev->prev = lk;
    634 }
    635 
    636 /**
    637  * xmlListSort:
    638  * @l:  a list
    639  *
    640  * Sort all the elements in the list
    641  */
    642 void
    643 xmlListSort(xmlListPtr l)
    644 {
    645     xmlListPtr lTemp;
    646 
    647     if (l == NULL)
    648         return;
    649     if(xmlListEmpty(l))
    650         return;
    651 
    652     /* I think that the real answer is to implement quicksort, the
    653      * alternative is to implement some list copying procedure which
    654      * would be based on a list copy followed by a clear followed by
    655      * an insert. This is slow...
    656      */
    657 
    658     if (NULL ==(lTemp = xmlListDup(l)))
    659         return;
    660     xmlListClear(l);
    661     xmlListMerge(l, lTemp);
    662     xmlListDelete(lTemp);
    663     return;
    664 }
    665 
    666 /**
    667  * xmlListWalk:
    668  * @l:  a list
    669  * @walker:  a processing function
    670  * @user:  a user parameter passed to the walker function
    671  *
    672  * Walk all the element of the first from first to last and
    673  * apply the walker function to it
    674  */
    675 void
    676 xmlListWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
    677     xmlLinkPtr lk;
    678 
    679     if ((l == NULL) || (walker == NULL))
    680         return;
    681     for(lk = l->sentinel->next; lk != l->sentinel; lk = lk->next) {
    682         if((walker(lk->data, user)) == 0)
    683                 break;
    684     }
    685 }
    686 
    687 /**
    688  * xmlListReverseWalk:
    689  * @l:  a list
    690  * @walker:  a processing function
    691  * @user:  a user parameter passed to the walker function
    692  *
    693  * Walk all the element of the list in reverse order and
    694  * apply the walker function to it
    695  */
    696 void
    697 xmlListReverseWalk(xmlListPtr l, xmlListWalker walker, const void *user) {
    698     xmlLinkPtr lk;
    699 
    700     if ((l == NULL) || (walker == NULL))
    701         return;
    702     for(lk = l->sentinel->prev; lk != l->sentinel; lk = lk->prev) {
    703         if((walker(lk->data, user)) == 0)
    704                 break;
    705     }
    706 }
    707 
    708 /**
    709  * xmlListMerge:
    710  * @l1:  the original list
    711  * @l2:  the new list
    712  *
    713  * include all the elements of the second list in the first one and
    714  * clear the second list
    715  */
    716 void
    717 xmlListMerge(xmlListPtr l1, xmlListPtr l2)
    718 {
    719     xmlListCopy(l1, l2);
    720     xmlListClear(l2);
    721 }
    722 
    723 /**
    724  * xmlListDup:
    725  * @old:  the list
    726  *
    727  * Duplicate the list
    728  *
    729  * Returns a new copy of the list or NULL in case of error
    730  */
    731 xmlListPtr
    732 xmlListDup(const xmlListPtr old)
    733 {
    734     xmlListPtr cur;
    735 
    736     if (old == NULL)
    737         return(NULL);
    738     /* Hmmm, how to best deal with allocation issues when copying
    739      * lists. If there is a de-allocator, should responsibility lie with
    740      * the new list or the old list. Surely not both. I'll arbitrarily
    741      * set it to be the old list for the time being whilst I work out
    742      * the answer
    743      */
    744     if (NULL ==(cur = xmlListCreate(NULL, old->linkCompare)))
    745         return (NULL);
    746     if (0 != xmlListCopy(cur, old))
    747         return NULL;
    748     return cur;
    749 }
    750 
    751 /**
    752  * xmlListCopy:
    753  * @cur:  the new list
    754  * @old:  the old list
    755  *
    756  * Move all the element from the old list in the new list
    757  *
    758  * Returns 0 in case of success 1 in case of error
    759  */
    760 int
    761 xmlListCopy(xmlListPtr cur, const xmlListPtr old)
    762 {
    763     /* Walk the old tree and insert the data into the new one */
    764     xmlLinkPtr lk;
    765 
    766     if ((old == NULL) || (cur == NULL))
    767         return(1);
    768     for(lk = old->sentinel->next; lk != old->sentinel; lk = lk->next) {
    769         if (0 !=xmlListInsert(cur, lk->data)) {
    770             xmlListDelete(cur);
    771             return (1);
    772         }
    773     }
    774     return (0);
    775 }
    776 /* xmlListUnique() */
    777 /* xmlListSwap */
    778 #define bottom_list
    779 #include "elfgcchack.h"
    780