1 /* $NetBSD: lst.c,v 1.64 2020/09/14 19:14:19 rillig Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1989, 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Adam de Boor. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #include <stdint.h> 36 37 #include "make.h" 38 39 MAKE_RCSID("$NetBSD: lst.c,v 1.64 2020/09/14 19:14:19 rillig Exp $"); 40 41 struct ListNode { 42 struct ListNode *prev; /* previous element in list */ 43 struct ListNode *next; /* next in list */ 44 uint8_t useCount; /* Count of functions using the node. 45 * node may not be deleted until count 46 * goes to 0 */ 47 Boolean deleted; /* List node should be removed when done */ 48 union { 49 void *datum; /* datum associated with this element */ 50 const GNode *gnode; /* alias, just for debugging */ 51 const char *str; /* alias, just for debugging */ 52 }; 53 }; 54 55 typedef enum { 56 Head, Middle, Tail, Unknown 57 } Where; 58 59 struct List { 60 LstNode first; /* first node in list */ 61 LstNode last; /* last node in list */ 62 63 /* fields for sequential access */ 64 Boolean isOpen; /* true if list has been Lst_Open'ed */ 65 Where lastAccess; /* Where in the list the last access was */ 66 LstNode curr; /* current node, if open. NULL if 67 * *just* opened */ 68 LstNode prev; /* Previous node, if open. Used by Lst_Remove */ 69 }; 70 71 /* Allocate and initialize a list node. 72 * 73 * The fields 'prev' and 'next' must be initialized by the caller. 74 */ 75 static LstNode 76 LstNodeNew(void *datum) 77 { 78 LstNode node = bmake_malloc(sizeof *node); 79 node->useCount = 0; 80 node->deleted = FALSE; 81 node->datum = datum; 82 return node; 83 } 84 85 static Boolean 86 LstIsEmpty(Lst list) 87 { 88 return list->first == NULL; 89 } 90 91 /* Create and initialize a new, empty list. */ 92 Lst 93 Lst_Init(void) 94 { 95 Lst list = bmake_malloc(sizeof *list); 96 97 list->first = NULL; 98 list->last = NULL; 99 list->isOpen = FALSE; 100 list->lastAccess = Unknown; 101 102 return list; 103 } 104 105 /* Duplicate an entire list, usually by copying the datum pointers. 106 * If copyProc is given, that function is used to create the new datum from the 107 * old datum, usually by creating a copy of it. */ 108 Lst 109 Lst_Copy(Lst list, LstCopyProc copyProc) 110 { 111 Lst newList; 112 LstNode node; 113 114 assert(list != NULL); 115 116 newList = Lst_Init(); 117 118 for (node = list->first; node != NULL; node = node->next) { 119 void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum; 120 Lst_Append(newList, datum); 121 } 122 123 return newList; 124 } 125 126 /* Free a list and all its nodes. The list data itself are not freed though. */ 127 void 128 Lst_Free(Lst list) 129 { 130 LstNode node; 131 LstNode next; 132 133 assert(list != NULL); 134 135 for (node = list->first; node != NULL; node = next) { 136 next = node->next; 137 free(node); 138 } 139 140 free(list); 141 } 142 143 /* Destroy a list and free all its resources. The freeProc is called with the 144 * datum from each node in turn before the node is freed. */ 145 void 146 Lst_Destroy(Lst list, LstFreeProc freeProc) 147 { 148 LstNode node; 149 LstNode next; 150 151 assert(list != NULL); 152 assert(freeProc != NULL); 153 154 for (node = list->first; node != NULL; node = next) { 155 next = node->next; 156 freeProc(node->datum); 157 free(node); 158 } 159 160 free(list); 161 } 162 163 /* 164 * Functions to modify a list 165 */ 166 167 /* Insert a new node with the given piece of data before the given node in the 168 * given list. */ 169 void 170 Lst_InsertBefore(Lst list, LstNode node, void *datum) 171 { 172 LstNode newNode; 173 174 assert(list != NULL); 175 assert(!LstIsEmpty(list)); 176 assert(node != NULL); 177 assert(datum != NULL); 178 179 newNode = LstNodeNew(datum); 180 newNode->prev = node->prev; 181 newNode->next = node; 182 183 if (node->prev != NULL) { 184 node->prev->next = newNode; 185 } 186 node->prev = newNode; 187 188 if (node == list->first) { 189 list->first = newNode; 190 } 191 } 192 193 /* Add a piece of data at the start of the given list. */ 194 void 195 Lst_Prepend(Lst list, void *datum) 196 { 197 LstNode node; 198 199 assert(list != NULL); 200 assert(datum != NULL); 201 202 node = LstNodeNew(datum); 203 node->prev = NULL; 204 node->next = list->first; 205 206 if (list->first == NULL) { 207 list->first = node; 208 list->last = node; 209 } else { 210 list->first->prev = node; 211 list->first = node; 212 } 213 } 214 215 /* Add a piece of data at the end of the given list. */ 216 void 217 Lst_Append(Lst list, void *datum) 218 { 219 LstNode node; 220 221 assert(list != NULL); 222 assert(datum != NULL); 223 224 node = LstNodeNew(datum); 225 node->prev = list->last; 226 node->next = NULL; 227 228 if (list->last == NULL) { 229 list->first = node; 230 list->last = node; 231 } else { 232 list->last->next = node; 233 list->last = node; 234 } 235 } 236 237 /* Remove the given node from the given list. 238 * The datum stored in the node must be freed by the caller, if necessary. */ 239 void 240 Lst_Remove(Lst list, LstNode node) 241 { 242 assert(list != NULL); 243 assert(node != NULL); 244 245 /* 246 * unlink it from the list 247 */ 248 if (node->next != NULL) { 249 node->next->prev = node->prev; 250 } 251 if (node->prev != NULL) { 252 node->prev->next = node->next; 253 } 254 255 /* 256 * if either the first or last of the list point to this node, 257 * adjust them accordingly 258 */ 259 if (list->first == node) { 260 list->first = node->next; 261 } 262 if (list->last == node) { 263 list->last = node->prev; 264 } 265 266 /* 267 * Sequential access stuff. If the node we're removing is the current 268 * node in the list, reset the current node to the previous one. If the 269 * previous one was non-existent (prev == NULL), we set the 270 * end to be Unknown, since it is. 271 */ 272 if (list->isOpen && list->curr == node) { 273 list->curr = list->prev; 274 if (list->curr == NULL) { 275 list->lastAccess = Unknown; 276 } 277 } 278 279 /* 280 * note that the datum is unmolested. The caller must free it as 281 * necessary and as expected. 282 */ 283 if (node->useCount == 0) { 284 free(node); 285 } else { 286 node->deleted = TRUE; 287 } 288 } 289 290 /* Replace the datum in the given node with the new datum. */ 291 void 292 LstNode_Set(LstNode node, void *datum) 293 { 294 assert(node != NULL); 295 assert(datum != NULL); 296 297 node->datum = datum; 298 } 299 300 /* Replace the datum in the given node to NULL. */ 301 void 302 LstNode_SetNull(LstNode node) 303 { 304 assert(node != NULL); 305 306 node->datum = NULL; 307 } 308 309 310 /* 311 * Node-specific functions 312 */ 313 314 /* Return the first node from the given list, or NULL if the list is empty. */ 315 LstNode 316 Lst_First(Lst list) 317 { 318 assert(list != NULL); 319 320 return list->first; 321 } 322 323 /* Return the last node from the given list, or NULL if the list is empty. */ 324 LstNode 325 Lst_Last(Lst list) 326 { 327 assert(list != NULL); 328 329 return list->last; 330 } 331 332 /* Return the successor to the given node on its list, or NULL. */ 333 LstNode 334 LstNode_Next(LstNode node) 335 { 336 assert(node != NULL); 337 338 return node->next; 339 } 340 341 /* Return the predecessor to the given node on its list, or NULL. */ 342 LstNode 343 LstNode_Prev(LstNode node) 344 { 345 assert(node != NULL); 346 return node->prev; 347 } 348 349 /* Return the datum stored in the given node. */ 350 void * 351 LstNode_Datum(LstNode node) 352 { 353 assert(node != NULL); 354 return node->datum; 355 } 356 357 358 /* 359 * Functions for entire lists 360 */ 361 362 /* Return TRUE if the given list is empty. */ 363 Boolean 364 Lst_IsEmpty(Lst list) 365 { 366 assert(list != NULL); 367 368 return LstIsEmpty(list); 369 } 370 371 /* Return the first node from the list for which the match function returns 372 * TRUE, or NULL if none of the nodes matched. */ 373 LstNode 374 Lst_Find(Lst list, LstFindProc match, const void *matchArgs) 375 { 376 return Lst_FindFrom(list, Lst_First(list), match, matchArgs); 377 } 378 379 /* Return the first node from the list, starting at the given node, for which 380 * the match function returns TRUE, or NULL if none of the nodes matches. 381 * 382 * The start node may be NULL, in which case nothing is found. This allows 383 * for passing Lst_First or LstNode_Next as the start node. */ 384 LstNode 385 Lst_FindFrom(Lst list, LstNode node, LstFindProc match, const void *matchArgs) 386 { 387 LstNode tln; 388 389 assert(list != NULL); 390 assert(match != NULL); 391 392 for (tln = node; tln != NULL; tln = tln->next) { 393 if (match(tln->datum, matchArgs)) 394 return tln; 395 } 396 397 return NULL; 398 } 399 400 /* Return the first node that contains the given datum, or NULL. */ 401 LstNode 402 Lst_FindDatum(Lst list, const void *datum) 403 { 404 LstNode node; 405 406 assert(list != NULL); 407 assert(datum != NULL); 408 409 for (node = list->first; node != NULL; node = node->next) { 410 if (node->datum == datum) { 411 return node; 412 } 413 } 414 415 return NULL; 416 } 417 418 /* Apply the given function to each element of the given list. The function 419 * should return 0 if traversal should continue and non-zero if it should 420 * abort. */ 421 int 422 Lst_ForEach(Lst list, LstActionProc proc, void *procData) 423 { 424 if (LstIsEmpty(list)) 425 return 0; /* XXX: Document what this value means. */ 426 return Lst_ForEachFrom(list, Lst_First(list), proc, procData); 427 } 428 429 /* Apply the given function to each element of the given list, starting from 430 * the given node. The function should return 0 if traversal should continue, 431 * and non-zero if it should abort. */ 432 int 433 Lst_ForEachFrom(Lst list, LstNode node, 434 LstActionProc proc, void *procData) 435 { 436 LstNode tln = node; 437 LstNode next; 438 Boolean done; 439 int result; 440 441 assert(list != NULL); 442 assert(node != NULL); 443 assert(proc != NULL); 444 445 do { 446 /* 447 * Take care of having the current element deleted out from under 448 * us. 449 */ 450 451 next = tln->next; 452 453 /* 454 * We're done with the traversal if 455 * - the next node to examine doesn't exist and 456 * - nothing's been added after the current node (check this 457 * after proc() has been called). 458 */ 459 done = next == NULL; 460 461 tln->useCount++; 462 result = (*proc)(tln->datum, procData); 463 tln->useCount--; 464 465 /* 466 * Now check whether a node has been added. 467 * Note: this doesn't work if this node was deleted before 468 * the new node was added. 469 */ 470 if (next != tln->next) { 471 next = tln->next; 472 done = 0; 473 } 474 475 if (tln->deleted) { 476 free((char *)tln); 477 } 478 tln = next; 479 } while (!result && !LstIsEmpty(list) && !done); 480 481 return result; 482 } 483 484 /* Move all nodes from list2 to the end of list1. 485 * List2 is destroyed and freed. */ 486 void 487 Lst_MoveAll(Lst list1, Lst list2) 488 { 489 assert(list1 != NULL); 490 assert(list2 != NULL); 491 492 if (list2->first != NULL) { 493 list2->first->prev = list1->last; 494 if (list1->last != NULL) { 495 list1->last->next = list2->first; 496 } else { 497 list1->first = list2->first; 498 } 499 list1->last = list2->last; 500 } 501 free(list2); 502 } 503 504 /* Copy the element data from src to the start of dst. */ 505 void 506 Lst_PrependAll(Lst dst, Lst src) 507 { 508 LstNode node; 509 for (node = src->last; node != NULL; node = node->prev) 510 Lst_Prepend(dst, node->datum); 511 } 512 513 /* Copy the element data from src to the end of dst. */ 514 void 515 Lst_AppendAll(Lst dst, Lst src) 516 { 517 LstNode node; 518 for (node = src->first; node != NULL; node = node->next) 519 Lst_Append(dst, node->datum); 520 } 521 522 /* 523 * these functions are for dealing with a list as a table, of sorts. 524 * An idea of the "current element" is kept and used by all the functions 525 * between Lst_Open() and Lst_Close(). 526 * 527 * The sequential functions access the list in a slightly different way. 528 * CurPtr points to their idea of the current node in the list and they 529 * access the list based on it. 530 */ 531 532 /* Open a list for sequential access. A list can still be searched, etc., 533 * without confusing these functions. */ 534 void 535 Lst_Open(Lst list) 536 { 537 assert(list != NULL); 538 assert(!list->isOpen); 539 540 list->isOpen = TRUE; 541 list->lastAccess = LstIsEmpty(list) ? Head : Unknown; 542 list->curr = NULL; 543 } 544 545 /* Return the next node for the given list, or NULL if the end has been 546 * reached. */ 547 LstNode 548 Lst_Next(Lst list) 549 { 550 LstNode node; 551 552 assert(list != NULL); 553 assert(list->isOpen); 554 555 list->prev = list->curr; 556 557 if (list->curr == NULL) { 558 if (list->lastAccess == Unknown) { 559 /* 560 * If we're just starting out, lastAccess will be Unknown. 561 * Then we want to start this thing off in the right 562 * direction -- at the start with lastAccess being Middle. 563 */ 564 list->curr = node = list->first; 565 list->lastAccess = Middle; 566 } else { 567 node = NULL; 568 list->lastAccess = Tail; 569 } 570 } else { 571 node = list->curr->next; 572 list->curr = node; 573 574 if (node == list->first || node == NULL) { 575 /* 576 * If back at the front, then we've hit the end... 577 */ 578 list->lastAccess = Tail; 579 } else { 580 /* 581 * Reset to Middle if gone past first. 582 */ 583 list->lastAccess = Middle; 584 } 585 } 586 587 return node; 588 } 589 590 /* Close a list which was opened for sequential access. */ 591 void 592 Lst_Close(Lst list) 593 { 594 assert(list != NULL); 595 assert(list->isOpen); 596 597 list->isOpen = FALSE; 598 list->lastAccess = Unknown; 599 } 600 601 602 /* 603 * for using the list as a queue 604 */ 605 606 /* Add the datum to the tail of the given list. */ 607 void 608 Lst_Enqueue(Lst list, void *datum) 609 { 610 Lst_Append(list, datum); 611 } 612 613 /* Remove and return the datum at the head of the given list. */ 614 void * 615 Lst_Dequeue(Lst list) 616 { 617 void *datum; 618 619 assert(list != NULL); 620 assert(!LstIsEmpty(list)); 621 622 datum = list->first->datum; 623 Lst_Remove(list, list->first); 624 assert(datum != NULL); 625 return datum; 626 } 627 628 void 629 Stack_Init(Stack *stack) 630 { 631 stack->len = 0; 632 stack->cap = 10; 633 stack->items = bmake_malloc(stack->cap * sizeof stack->items[0]); 634 } 635 636 Boolean Stack_IsEmpty(Stack *stack) 637 { 638 return stack->len == 0; 639 } 640 641 void Stack_Push(Stack *stack, void *datum) 642 { 643 if (stack->len >= stack->cap) { 644 stack->cap *= 2; 645 stack->items = bmake_realloc(stack->items, 646 stack->cap * sizeof stack->items[0]); 647 } 648 stack->items[stack->len] = datum; 649 stack->len++; 650 } 651 652 void *Stack_Pop(Stack *stack) 653 { 654 void *datum; 655 656 assert(stack->len > 0); 657 stack->len--; 658 datum = stack->items[stack->len]; 659 #ifdef CLEANUP 660 stack->items[stack->len] = NULL; 661 #endif 662 return datum; 663 } 664 665 void Stack_Done(Stack *stack) 666 { 667 free(stack->items); 668 } 669