lst.c revision 1.68 1 /* $NetBSD: lst.c,v 1.68 2020/09/24 07:23:26 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.68 2020/09/24 07:23:26 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 ListNode *first; /* first node in list */
61 ListNode *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 ListNode *curr; /* current node, if open. NULL if
67 * *just* opened */
68 ListNode *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 ListNode *
76 LstNodeNew(void *datum)
77 {
78 ListNode *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(List *list)
87 {
88 return list->first == NULL;
89 }
90
91 /* Create and initialize a new, empty list. */
92 List *
93 Lst_Init(void)
94 {
95 List *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 List *
109 Lst_Copy(List *list, LstCopyProc copyProc)
110 {
111 List *newList;
112 ListNode *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(List *list)
129 {
130 ListNode *node;
131 ListNode *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(List *list, LstFreeProc freeProc)
147 {
148 ListNode *node;
149 ListNode *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(List *list, ListNode *node, void *datum)
171 {
172 ListNode *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(List *list, void *datum)
196 {
197 ListNode *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(List *list, void *datum)
218 {
219 ListNode *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(List *list, ListNode *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(ListNode *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(ListNode *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 ListNode *
316 Lst_First(List *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 ListNode *
325 Lst_Last(List *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 ListNode *
334 LstNode_Next(ListNode *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 ListNode *
343 LstNode_Prev(ListNode *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(ListNode *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(List *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 ListNode *
374 Lst_Find(List *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 ListNode *
385 Lst_FindFrom(List *list, ListNode *node, LstFindProc match, const void *matchArgs)
386 {
387 ListNode *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 ListNode *
402 Lst_FindDatum(List *list, const void *datum)
403 {
404 ListNode *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_ForEachUntil(List *list, LstActionUntilProc proc, void *procData)
423 {
424 ListNode *tln = list->first;
425 int result = 0;
426
427 while (tln != NULL) {
428 /*
429 * Take care of having the current element deleted out from under
430 * us.
431 */
432 ListNode *next = tln->next;
433
434 /*
435 * We're done with the traversal if
436 * - the next node to examine doesn't exist and
437 * - nothing's been added after the current node (check this
438 * after proc() has been called).
439 */
440 Boolean done = next == NULL;
441
442 tln->useCount++;
443 result = (*proc)(tln->datum, procData);
444 tln->useCount--;
445
446 /*
447 * Now check whether a node has been added.
448 * Note: this doesn't work if this node was deleted before
449 * the new node was added.
450 */
451 if (next != tln->next) {
452 next = tln->next;
453 done = 0;
454 }
455
456 if (tln->deleted) {
457 free((char *)tln);
458 }
459 tln = next;
460 if (result || LstIsEmpty(list) || done)
461 break;
462 }
463
464 return result;
465 }
466
467 /* Move all nodes from list2 to the end of list1.
468 * List2 is destroyed and freed. */
469 void
470 Lst_MoveAll(List *list1, List *list2)
471 {
472 assert(list1 != NULL);
473 assert(list2 != NULL);
474
475 if (list2->first != NULL) {
476 list2->first->prev = list1->last;
477 if (list1->last != NULL) {
478 list1->last->next = list2->first;
479 } else {
480 list1->first = list2->first;
481 }
482 list1->last = list2->last;
483 }
484 free(list2);
485 }
486
487 /* Copy the element data from src to the start of dst. */
488 void
489 Lst_PrependAll(List *dst, List *src)
490 {
491 ListNode *node;
492 for (node = src->last; node != NULL; node = node->prev)
493 Lst_Prepend(dst, node->datum);
494 }
495
496 /* Copy the element data from src to the end of dst. */
497 void
498 Lst_AppendAll(List *dst, List *src)
499 {
500 ListNode *node;
501 for (node = src->first; node != NULL; node = node->next)
502 Lst_Append(dst, node->datum);
503 }
504
505 /*
506 * these functions are for dealing with a list as a table, of sorts.
507 * An idea of the "current element" is kept and used by all the functions
508 * between Lst_Open() and Lst_Close().
509 *
510 * The sequential functions access the list in a slightly different way.
511 * CurPtr points to their idea of the current node in the list and they
512 * access the list based on it.
513 */
514
515 /* Open a list for sequential access. A list can still be searched, etc.,
516 * without confusing these functions. */
517 void
518 Lst_Open(List *list)
519 {
520 assert(list != NULL);
521 assert(!list->isOpen);
522
523 list->isOpen = TRUE;
524 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
525 list->curr = NULL;
526 }
527
528 /* Return the next node for the given list, or NULL if the end has been
529 * reached. */
530 ListNode *
531 Lst_Next(List *list)
532 {
533 ListNode *node;
534
535 assert(list != NULL);
536 assert(list->isOpen);
537
538 list->prev = list->curr;
539
540 if (list->curr == NULL) {
541 if (list->lastAccess == Unknown) {
542 /*
543 * If we're just starting out, lastAccess will be Unknown.
544 * Then we want to start this thing off in the right
545 * direction -- at the start with lastAccess being Middle.
546 */
547 list->curr = node = list->first;
548 list->lastAccess = Middle;
549 } else {
550 node = NULL;
551 list->lastAccess = Tail;
552 }
553 } else {
554 node = list->curr->next;
555 list->curr = node;
556
557 if (node == list->first || node == NULL) {
558 /*
559 * If back at the front, then we've hit the end...
560 */
561 list->lastAccess = Tail;
562 } else {
563 /*
564 * Reset to Middle if gone past first.
565 */
566 list->lastAccess = Middle;
567 }
568 }
569
570 return node;
571 }
572
573 /* Close a list which was opened for sequential access. */
574 void
575 Lst_Close(List *list)
576 {
577 assert(list != NULL);
578 assert(list->isOpen);
579
580 list->isOpen = FALSE;
581 list->lastAccess = Unknown;
582 }
583
584
585 /*
586 * for using the list as a queue
587 */
588
589 /* Add the datum to the tail of the given list. */
590 void
591 Lst_Enqueue(List *list, void *datum)
592 {
593 Lst_Append(list, datum);
594 }
595
596 /* Remove and return the datum at the head of the given list. */
597 void *
598 Lst_Dequeue(List *list)
599 {
600 void *datum;
601
602 assert(list != NULL);
603 assert(!LstIsEmpty(list));
604
605 datum = list->first->datum;
606 Lst_Remove(list, list->first);
607 assert(datum != NULL);
608 return datum;
609 }
610
611 void
612 Stack_Init(Stack *stack)
613 {
614 stack->len = 0;
615 stack->cap = 10;
616 stack->items = bmake_malloc(stack->cap * sizeof stack->items[0]);
617 }
618
619 Boolean Stack_IsEmpty(Stack *stack)
620 {
621 return stack->len == 0;
622 }
623
624 void Stack_Push(Stack *stack, void *datum)
625 {
626 if (stack->len >= stack->cap) {
627 stack->cap *= 2;
628 stack->items = bmake_realloc(stack->items,
629 stack->cap * sizeof stack->items[0]);
630 }
631 stack->items[stack->len] = datum;
632 stack->len++;
633 }
634
635 void *Stack_Pop(Stack *stack)
636 {
637 void *datum;
638
639 assert(stack->len > 0);
640 stack->len--;
641 datum = stack->items[stack->len];
642 #ifdef CLEANUP
643 stack->items[stack->len] = NULL;
644 #endif
645 return datum;
646 }
647
648 void Stack_Done(Stack *stack)
649 {
650 free(stack->items);
651 }
652