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@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 occurred, 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 occurred, 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, 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, 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