/src/common/list.c
C | 838 lines | 632 code | 134 blank | 72 comment | 155 complexity | 773918d6c103aa9688c417098abf584e MD5 | raw file
1/***************************************************************************** 2 * $Id$ 3 ***************************************************************************** 4 * $LSDId: list.c,v 1.28 2003/05/20 23:53:22 dun Exp $ 5 ***************************************************************************** 6 * Copyright (C) 2001-2006 The Regents of the University of California. 7 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). 8 * Written by Chris Dunlap <cdunlap@llnl.gov>. 9 * 10 * This file is from LSD-Tools, the LLNL Software Development Toolbox. 11 * 12 * LSD-Tools is free software; you can redistribute it and/or modify it under 13 * the terms of the GNU General Public License as published by the Free 14 * Software Foundation; either version 2 of the License, or (at your option) 15 * any later version. 16 * 17 * LSD-Tools is distributed in the hope that it will be useful, but WITHOUT 18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 20 * more details. 21 * 22 * You should have received a copy of the GNU General Public License along 23 * with LSD-Tools; if not, write to the Free Software Foundation, Inc., 24 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 25 ***************************************************************************** 26 * Refer to "list.h" for documentation on public functions. 27 *****************************************************************************/ 28 29 30#ifdef HAVE_CONFIG_H 31# include "config.h" 32#endif /* HAVE_CONFIG_H */ 33 34#ifdef WITH_PTHREADS 35# include <pthread.h> 36#endif /* WITH_PTHREADS */ 37 38#include <assert.h> 39#include <errno.h> 40#include <stdlib.h> 41#include <string.h> 42#include "list.h" 43 44 45/********************* 46 * lsd_fatal_error * 47 *********************/ 48 49#ifdef WITH_LSD_FATAL_ERROR_FUNC 50# undef lsd_fatal_error 51 extern void lsd_fatal_error(char *file, int line, char *mesg); 52#else /* !WITH_LSD_FATAL_ERROR_FUNC */ 53# ifndef lsd_fatal_error 54# include <errno.h> 55# include <stdio.h> 56# include <string.h> 57# define lsd_fatal_error(file, line, mesg) \ 58 do { \ 59 fprintf(stderr, "ERROR: [%s:%d] %s: %s\n", \ 60 file, line, mesg, strerror(errno)); \ 61 } while (0) 62# endif /* !lsd_fatal_error */ 63#endif /* !WITH_LSD_FATAL_ERROR_FUNC */ 64 65 66/********************* 67 * lsd_nomem_error * 68 *********************/ 69 70#ifdef WITH_LSD_NOMEM_ERROR_FUNC 71# undef lsd_nomem_error 72 extern void * lsd_nomem_error(char *file, int line, char *mesg); 73#else /* !WITH_LSD_NOMEM_ERROR_FUNC */ 74# ifndef lsd_nomem_error 75# define lsd_nomem_error(file, line, mesg) (NULL) 76# endif /* !lsd_nomem_error */ 77#endif /* !WITH_LSD_NOMEM_ERROR_FUNC */ 78 79 80/*************** 81 * Constants * 82 ***************/ 83 84#define LIST_ALLOC 32 85#define LIST_MAGIC 0xDEADBEEF 86 87 88/**************** 89 * Data Types * 90 ****************/ 91 92struct listNode { 93 void *data; /* node's data */ 94 struct listNode *next; /* next node in list */ 95}; 96 97struct listIterator { 98 struct list *list; /* the list being iterated */ 99 struct listNode *pos; /* the next node to be iterated */ 100 struct listNode **prev; /* addr of 'next' ptr to prv It node */ 101 struct listIterator *iNext; /* iterator chain for list_destroy() */ 102#ifndef NDEBUG 103 unsigned int magic; /* sentinel for asserting validity */ 104#endif /* !NDEBUG */ 105}; 106 107struct list { 108 struct listNode *head; /* head of the list */ 109 struct listNode **tail; /* addr of last node's 'next' ptr */ 110 struct listIterator *iNext; /* iterator chain for list_destroy() */ 111 ListDelF fDel; /* function to delete node data */ 112 int count; /* number of nodes in list */ 113#ifdef WITH_PTHREADS 114 pthread_mutex_t mutex; /* mutex to protect access to list */ 115#endif /* WITH_PTHREADS */ 116#ifndef NDEBUG 117 unsigned int magic; /* sentinel for asserting validity */ 118#endif /* !NDEBUG */ 119}; 120 121typedef struct listNode * ListNode; 122 123 124/**************** 125 * Prototypes * 126 ****************/ 127 128static void * list_node_create (List l, ListNode *pp, void *x); 129static void * list_node_destroy (List l, ListNode *pp); 130static List list_alloc (void); 131static void list_free (List l); 132static ListNode list_node_alloc (void); 133static void list_node_free (ListNode p); 134static ListIterator list_iterator_alloc (void); 135static void list_iterator_free (ListIterator i); 136static void * list_alloc_aux (int size, void *pfreelist); 137static void list_free_aux (void *x, void *pfreelist); 138 139 140/*************** 141 * Variables * 142 ***************/ 143 144static List list_free_lists = NULL; 145static ListNode list_free_nodes = NULL; 146static ListIterator list_free_iterators = NULL; 147 148#ifdef WITH_PTHREADS 149static pthread_mutex_t list_free_lock = PTHREAD_MUTEX_INITIALIZER; 150#endif /* WITH_PTHREADS */ 151 152 153/************ 154 * Macros * 155 ************/ 156 157#ifdef WITH_PTHREADS 158 159# define list_mutex_init(mutex) \ 160 do { \ 161 int e = pthread_mutex_init(mutex, NULL); \ 162 if (e != 0) { \ 163 errno = e; \ 164 lsd_fatal_error(__FILE__, __LINE__, "list mutex init"); \ 165 abort(); \ 166 } \ 167 } while (0) 168 169# define list_mutex_lock(mutex) \ 170 do { \ 171 int e = pthread_mutex_lock(mutex); \ 172 if (e != 0) { \ 173 errno = e; \ 174 lsd_fatal_error(__FILE__, __LINE__, "list mutex lock"); \ 175 abort(); \ 176 } \ 177 } while (0) 178 179# define list_mutex_unlock(mutex) \ 180 do { \ 181 int e = pthread_mutex_unlock(mutex); \ 182 if (e != 0) { \ 183 errno = e; \ 184 lsd_fatal_error(__FILE__, __LINE__, "list mutex unlock"); \ 185 abort(); \ 186 } \ 187 } while (0) 188 189# define list_mutex_destroy(mutex) \ 190 do { \ 191 int e = pthread_mutex_destroy(mutex); \ 192 if (e != 0) { \ 193 errno = e; \ 194 lsd_fatal_error(__FILE__, __LINE__, "list mutex destroy"); \ 195 abort(); \ 196 } \ 197 } while (0) 198 199# ifndef NDEBUG 200 static int list_mutex_is_locked (pthread_mutex_t *mutex); 201# endif /* !NDEBUG */ 202 203#else /* !WITH_PTHREADS */ 204 205# define list_mutex_init(mutex) 206# define list_mutex_lock(mutex) 207# define list_mutex_unlock(mutex) 208# define list_mutex_destroy(mutex) 209# define list_mutex_is_locked(mutex) (1) 210 211#endif /* !WITH_PTHREADS */ 212 213 214/*************** 215 * Functions * 216 ***************/ 217 218List 219list_create (ListDelF f) 220{ 221 List l; 222 223 if (!(l = list_alloc())) 224 return(lsd_nomem_error(__FILE__, __LINE__, "list create")); 225 l->head = NULL; 226 l->tail = &l->head; 227 l->iNext = NULL; 228 l->fDel = f; 229 l->count = 0; 230 list_mutex_init(&l->mutex); 231 assert(l->magic = LIST_MAGIC); /* set magic via assert abuse */ 232 return(l); 233} 234 235 236void 237list_destroy (List l) 238{ 239 ListIterator i, iTmp; 240 ListNode p, pTmp; 241 242 assert(l != NULL); 243 list_mutex_lock(&l->mutex); 244 assert(l->magic == LIST_MAGIC); 245 i = l->iNext; 246 while (i) { 247 assert(i->magic == LIST_MAGIC); 248 iTmp = i->iNext; 249 assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ 250 list_iterator_free(i); 251 i = iTmp; 252 } 253 p = l->head; 254 while (p) { 255 pTmp = p->next; 256 if (p->data && l->fDel) 257 l->fDel(p->data); 258 list_node_free(p); 259 p = pTmp; 260 } 261 assert(l->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ 262 list_mutex_unlock(&l->mutex); 263 list_mutex_destroy(&l->mutex); 264 list_free(l); 265 return; 266} 267 268 269int 270list_is_empty (List l) 271{ 272 int n; 273 274 assert(l != NULL); 275 list_mutex_lock(&l->mutex); 276 assert(l->magic == LIST_MAGIC); 277 n = l->count; 278 list_mutex_unlock(&l->mutex); 279 return(n == 0); 280} 281 282 283int 284list_count (List l) 285{ 286 int n; 287 288 assert(l != NULL); 289 list_mutex_lock(&l->mutex); 290 assert(l->magic == LIST_MAGIC); 291 n = l->count; 292 list_mutex_unlock(&l->mutex); 293 return(n); 294} 295 296 297void * 298list_append (List l, void *x) 299{ 300 void *v; 301 302 assert(l != NULL); 303 assert(x != NULL); 304 list_mutex_lock(&l->mutex); 305 assert(l->magic == LIST_MAGIC); 306 v = list_node_create(l, l->tail, x); 307 list_mutex_unlock(&l->mutex); 308 return(v); 309} 310 311 312void * 313list_prepend (List l, void *x) 314{ 315 void *v; 316 317 assert(l != NULL); 318 assert(x != NULL); 319 list_mutex_lock(&l->mutex); 320 assert(l->magic == LIST_MAGIC); 321 v = list_node_create(l, &l->head, x); 322 list_mutex_unlock(&l->mutex); 323 return(v); 324} 325 326 327void * 328list_find_first (List l, ListFindF f, void *key) 329{ 330 ListNode p; 331 void *v = NULL; 332 333 assert(l != NULL); 334 assert(f != NULL); 335 assert(key != NULL); 336 list_mutex_lock(&l->mutex); 337 assert(l->magic == LIST_MAGIC); 338 for (p=l->head; p; p=p->next) { 339 if (f(p->data, key)) { 340 v = p->data; 341 break; 342 } 343 } 344 list_mutex_unlock(&l->mutex); 345 return(v); 346} 347 348 349int 350list_delete_all (List l, ListFindF f, void *key) 351{ 352 ListNode *pp; 353 void *v; 354 int n = 0; 355 356 assert(l != NULL); 357 assert(f != NULL); 358 assert(key != NULL); 359 list_mutex_lock(&l->mutex); 360 assert(l->magic == LIST_MAGIC); 361 pp = &l->head; 362 while (*pp) { 363 if (f((*pp)->data, key)) { 364 if ((v = list_node_destroy(l, pp))) { 365 if (l->fDel) 366 l->fDel(v); 367 n++; 368 } 369 } 370 else { 371 pp = &(*pp)->next; 372 } 373 } 374 list_mutex_unlock(&l->mutex); 375 return(n); 376} 377 378 379int 380list_for_each (List l, ListForF f, void *arg) 381{ 382 ListNode p; 383 int n = 0; 384 385 assert(l != NULL); 386 assert(f != NULL); 387 list_mutex_lock(&l->mutex); 388 assert(l->magic == LIST_MAGIC); 389 for (p=l->head; p; p=p->next) { 390 n++; 391 if (f(p->data, arg) < 0) { 392 n = -n; 393 break; 394 } 395 } 396 list_mutex_unlock(&l->mutex); 397 return(n); 398} 399 400 401void 402list_sort (List l, ListCmpF f) 403{ 404/* Note: Time complexity O(n^2). 405 */ 406 ListNode *pp, *ppPrev, *ppPos, pTmp; 407 ListIterator i; 408 409 assert(l != NULL); 410 assert(f != NULL); 411 list_mutex_lock(&l->mutex); 412 assert(l->magic == LIST_MAGIC); 413 if (l->count > 1) { 414 ppPrev = &l->head; 415 pp = &(*ppPrev)->next; 416 while (*pp) { 417 if (f((*pp)->data, (*ppPrev)->data) < 0) { 418 ppPos = &l->head; 419 while (f((*pp)->data, (*ppPos)->data) >= 0) 420 ppPos = &(*ppPos)->next; 421 pTmp = (*pp)->next; 422 (*pp)->next = *ppPos; 423 *ppPos = *pp; 424 *pp = pTmp; 425 if (ppPrev == ppPos) 426 ppPrev = &(*ppPrev)->next; 427 } 428 else { 429 ppPrev = pp; 430 pp = &(*pp)->next; 431 } 432 } 433 l->tail = pp; 434 435 for (i=l->iNext; i; i=i->iNext) { 436 assert(i->magic == LIST_MAGIC); 437 i->pos = i->list->head; 438 i->prev = &i->list->head; 439 } 440 } 441 list_mutex_unlock(&l->mutex); 442 return; 443} 444 445 446void * 447list_push (List l, void *x) 448{ 449 void *v; 450 451 assert(l != NULL); 452 assert(x != NULL); 453 list_mutex_lock(&l->mutex); 454 assert(l->magic == LIST_MAGIC); 455 v = list_node_create(l, &l->head, x); 456 list_mutex_unlock(&l->mutex); 457 return(v); 458} 459 460 461void * 462list_pop (List l) 463{ 464 void *v; 465 466 assert(l != NULL); 467 list_mutex_lock(&l->mutex); 468 assert(l->magic == LIST_MAGIC); 469 v = list_node_destroy(l, &l->head); 470 list_mutex_unlock(&l->mutex); 471 return(v); 472} 473 474 475void * 476list_peek (List l) 477{ 478 void *v; 479 480 assert(l != NULL); 481 list_mutex_lock(&l->mutex); 482 assert(l->magic == LIST_MAGIC); 483 v = (l->head) ? l->head->data : NULL; 484 list_mutex_unlock(&l->mutex); 485 return(v); 486} 487 488 489void * 490list_enqueue (List l, void *x) 491{ 492 void *v; 493 494 assert(l != NULL); 495 assert(x != NULL); 496 list_mutex_lock(&l->mutex); 497 assert(l->magic == LIST_MAGIC); 498 v = list_node_create(l, l->tail, x); 499 list_mutex_unlock(&l->mutex); 500 return(v); 501} 502 503 504void * 505list_dequeue (List l) 506{ 507 void *v; 508 509 assert(l != NULL); 510 list_mutex_lock(&l->mutex); 511 assert(l->magic == LIST_MAGIC); 512 v = list_node_destroy(l, &l->head); 513 list_mutex_unlock(&l->mutex); 514 return(v); 515} 516 517 518ListIterator 519list_iterator_create (List l) 520{ 521 ListIterator i; 522 523 assert(l != NULL); 524 if (!(i = list_iterator_alloc())) 525 return(lsd_nomem_error(__FILE__, __LINE__, "list iterator create")); 526 i->list = l; 527 list_mutex_lock(&l->mutex); 528 assert(l->magic == LIST_MAGIC); 529 i->pos = l->head; 530 i->prev = &l->head; 531 i->iNext = l->iNext; 532 l->iNext = i; 533 assert(i->magic = LIST_MAGIC); /* set magic via assert abuse */ 534 list_mutex_unlock(&l->mutex); 535 return(i); 536} 537 538 539void 540list_iterator_reset (ListIterator i) 541{ 542 assert(i != NULL); 543 assert(i->magic == LIST_MAGIC); 544 list_mutex_lock(&i->list->mutex); 545 assert(i->list->magic == LIST_MAGIC); 546 i->pos = i->list->head; 547 i->prev = &i->list->head; 548 list_mutex_unlock(&i->list->mutex); 549 return; 550} 551 552 553void 554list_iterator_destroy (ListIterator i) 555{ 556 ListIterator *pi; 557 558 assert(i != NULL); 559 assert(i->magic == LIST_MAGIC); 560 list_mutex_lock(&i->list->mutex); 561 assert(i->list->magic == LIST_MAGIC); 562 for (pi=&i->list->iNext; *pi; pi=&(*pi)->iNext) { 563 assert((*pi)->magic == LIST_MAGIC); 564 if (*pi == i) { 565 *pi = (*pi)->iNext; 566 break; 567 } 568 } 569 list_mutex_unlock(&i->list->mutex); 570 assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */ 571 list_iterator_free(i); 572 return; 573} 574 575 576void * 577list_next (ListIterator i) 578{ 579 ListNode p; 580 581 assert(i != NULL); 582 assert(i->magic == LIST_MAGIC); 583 list_mutex_lock(&i->list->mutex); 584 assert(i->list->magic == LIST_MAGIC); 585 if ((p = i->pos)) 586 i->pos = p->next; 587 if (*i->prev != p) 588 i->prev = &(*i->prev)->next; 589 list_mutex_unlock(&i->list->mutex); 590 return(p ? p->data : NULL); 591} 592 593 594void * 595list_insert (ListIterator i, void *x) 596{ 597 void *v; 598 599 assert(i != NULL); 600 assert(x != NULL); 601 assert(i->magic == LIST_MAGIC); 602 list_mutex_lock(&i->list->mutex); 603 assert(i->list->magic == LIST_MAGIC); 604 v = list_node_create(i->list, i->prev, x); 605 list_mutex_unlock(&i->list->mutex); 606 return(v); 607} 608 609 610void * 611list_find (ListIterator i, ListFindF f, void *key) 612{ 613 void *v; 614 615 assert(i != NULL); 616 assert(f != NULL); 617 assert(key != NULL); 618 assert(i->magic == LIST_MAGIC); 619 while ((v=list_next(i)) && !f(v,key)) {;} 620 return(v); 621} 622 623 624void * 625list_remove (ListIterator i) 626{ 627 void *v = NULL; 628 629 assert(i != NULL); 630 assert(i->magic == LIST_MAGIC); 631 list_mutex_lock(&i->list->mutex); 632 assert(i->list->magic == LIST_MAGIC); 633 if (*i->prev != i->pos) 634 v = list_node_destroy(i->list, i->prev); 635 list_mutex_unlock(&i->list->mutex); 636 return(v); 637} 638 639 640int 641list_delete (ListIterator i) 642{ 643 void *v; 644 645 assert(i != NULL); 646 assert(i->magic == LIST_MAGIC); 647 if ((v = list_remove(i))) { 648 if (i->list->fDel) 649 i->list->fDel(v); 650 return(1); 651 } 652 return(0); 653} 654 655 656static void * 657list_node_create (List l, ListNode *pp, void *x) 658{ 659/* Inserts data pointed to by [x] into list [l] after [pp], 660 * the address of the previous node's "next" ptr. 661 * Returns a ptr to data [x], or NULL if insertion fails. 662 * This routine assumes the list is already locked upon entry. 663 */ 664 ListNode p; 665 ListIterator i; 666 667 assert(l != NULL); 668 assert(l->magic == LIST_MAGIC); 669 assert(list_mutex_is_locked(&l->mutex)); 670 assert(pp != NULL); 671 assert(x != NULL); 672 if (!(p = list_node_alloc())) 673 return(lsd_nomem_error(__FILE__, __LINE__, "list node create")); 674 p->data = x; 675 if (!(p->next = *pp)) 676 l->tail = &p->next; 677 *pp = p; 678 l->count++; 679 for (i=l->iNext; i; i=i->iNext) { 680 assert(i->magic == LIST_MAGIC); 681 if (i->prev == pp) 682 i->prev = &p->next; 683 else if (i->pos == p->next) 684 i->pos = p; 685 assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next)); 686 } 687 return(x); 688} 689 690 691static void * 692list_node_destroy (List l, ListNode *pp) 693{ 694/* Removes the node pointed to by [*pp] from from list [l], 695 * where [pp] is the address of the previous node's "next" ptr. 696 * Returns the data ptr associated with list item being removed, 697 * or NULL if [*pp] points to the NULL element. 698 * This routine assumes the list is already locked upon entry. 699 */ 700 void *v; 701 ListNode p; 702 ListIterator i; 703 704 assert(l != NULL); 705 assert(l->magic == LIST_MAGIC); 706 assert(list_mutex_is_locked(&l->mutex)); 707 assert(pp != NULL); 708 if (!(p = *pp)) 709 return(NULL); 710 v = p->data; 711 if (!(*pp = p->next)) 712 l->tail = pp; 713 l->count--; 714 for (i=l->iNext; i; i=i->iNext) { 715 assert(i->magic == LIST_MAGIC); 716 if (i->pos == p) 717 i->pos = p->next, i->prev = pp; 718 else if (i->prev == &p->next) 719 i->prev = pp; 720 assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next)); 721 } 722 list_node_free(p); 723 return(v); 724} 725 726 727static List 728list_alloc (void) 729{ 730 return(list_alloc_aux(sizeof(struct list), &list_free_lists)); 731} 732 733 734static void 735list_free (List l) 736{ 737 list_free_aux(l, &list_free_lists); 738 return; 739} 740 741 742static ListNode 743list_node_alloc (void) 744{ 745 return(list_alloc_aux(sizeof(struct listNode), &list_free_nodes)); 746} 747 748 749static void 750list_node_free (ListNode p) 751{ 752 list_free_aux(p, &list_free_nodes); 753 return; 754} 755 756 757static ListIterator 758list_iterator_alloc (void) 759{ 760 return(list_alloc_aux(sizeof(struct listIterator), &list_free_iterators)); 761} 762 763 764static void 765list_iterator_free (ListIterator i) 766{ 767 list_free_aux(i, &list_free_iterators); 768 return; 769} 770 771 772static void * 773list_alloc_aux (int size, void *pfreelist) 774{ 775/* Allocates an object of [size] bytes from the freelist [*pfreelist]. 776 * Memory is added to the freelist in chunks of size LIST_ALLOC. 777 * Returns a ptr to the object, or NULL if the memory request fails. 778 */ 779 void **px; 780 void **pfree = pfreelist; 781 void **plast; 782 783 assert(sizeof(char) == 1); 784 assert(size >= sizeof(void *)); 785 assert(pfreelist != NULL); 786 assert(LIST_ALLOC > 0); 787 list_mutex_lock(&list_free_lock); 788 if (!*pfree) { 789 if ((*pfree = malloc(LIST_ALLOC * size))) { 790 px = *pfree; 791 plast = (void **) ((char *) *pfree + ((LIST_ALLOC - 1) * size)); 792 while (px < plast) 793 *px = (char *) px + size, px = *px; 794 *plast = NULL; 795 } 796 } 797 if ((px = *pfree)) 798 *pfree = *px; 799 else 800 errno = ENOMEM; 801 list_mutex_unlock(&list_free_lock); 802 return(px); 803} 804 805 806static void 807list_free_aux (void *x, void *pfreelist) 808{ 809/* Frees the object [x], returning it to the freelist [*pfreelist]. 810 */ 811 void **px = x; 812 void **pfree = pfreelist; 813 814 assert(x != NULL); 815 assert(pfreelist != NULL); 816 list_mutex_lock(&list_free_lock); 817 *px = *pfree; 818 *pfree = px; 819 list_mutex_unlock(&list_free_lock); 820 return; 821} 822 823 824#ifndef NDEBUG 825#ifdef WITH_PTHREADS 826static int 827list_mutex_is_locked (pthread_mutex_t *mutex) 828{ 829/* Returns true if the mutex is locked; o/w, returns false. 830 */ 831 int rc; 832 833 assert(mutex != NULL); 834 rc = pthread_mutex_trylock(mutex); 835 return(rc == EBUSY ? 1 : 0); 836} 837#endif /* WITH_PTHREADS */ 838#endif /* !NDEBUG */