lst.c revision 1.66 1 /* $NetBSD: lst.c,v 1.66 2020/09/24 06:45:59 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.66 2020/09/24 06:45:59 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 static int Lst_ForEachFrom(List *, ListNode *, LstActionProc, void *);
419
420 /* Apply the given function to each element of the given list. The function
421 * should return 0 if traversal should continue and non-zero if it should
422 * abort. */
423 int
424 Lst_ForEach(List *list, LstActionProc proc, void *procData)
425 {
426 if (LstIsEmpty(list))
427 return 0; /* XXX: Document what this value means. */
428 return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
429 }
430
431 /* Apply the given function to each element of the given list, starting from
432 * the given node. The function should return 0 if traversal should continue,
433 * and non-zero if it should abort. */
434 int
435 Lst_ForEachFrom(List *list, ListNode *node,
436 LstActionProc proc, void *procData)
437 {
438 ListNode *tln = node;
439 ListNode *next;
440 Boolean done;
441 int result;
442
443 assert(list != NULL);
444 assert(node != NULL);
445 assert(proc != NULL);
446
447 do {
448 /*
449 * Take care of having the current element deleted out from under
450 * us.
451 */
452
453 next = tln->next;
454
455 /*
456 * We're done with the traversal if
457 * - the next node to examine doesn't exist and
458 * - nothing's been added after the current node (check this
459 * after proc() has been called).
460 */
461 done = next == NULL;
462
463 tln->useCount++;
464 result = (*proc)(tln->datum, procData);
465 tln->useCount--;
466
467 /*
468 * Now check whether a node has been added.
469 * Note: this doesn't work if this node was deleted before
470 * the new node was added.
471 */
472 if (next != tln->next) {
473 next = tln->next;
474 done = 0;
475 }
476
477 if (tln->deleted) {
478 free((char *)tln);
479 }
480 tln = next;
481 } while (!result && !LstIsEmpty(list) && !done);
482
483 return result;
484 }
485
486 /* Move all nodes from list2 to the end of list1.
487 * List2 is destroyed and freed. */
488 void
489 Lst_MoveAll(List *list1, List *list2)
490 {
491 assert(list1 != NULL);
492 assert(list2 != NULL);
493
494 if (list2->first != NULL) {
495 list2->first->prev = list1->last;
496 if (list1->last != NULL) {
497 list1->last->next = list2->first;
498 } else {
499 list1->first = list2->first;
500 }
501 list1->last = list2->last;
502 }
503 free(list2);
504 }
505
506 /* Copy the element data from src to the start of dst. */
507 void
508 Lst_PrependAll(List *dst, List *src)
509 {
510 ListNode *node;
511 for (node = src->last; node != NULL; node = node->prev)
512 Lst_Prepend(dst, node->datum);
513 }
514
515 /* Copy the element data from src to the end of dst. */
516 void
517 Lst_AppendAll(List *dst, List *src)
518 {
519 ListNode *node;
520 for (node = src->first; node != NULL; node = node->next)
521 Lst_Append(dst, node->datum);
522 }
523
524 /*
525 * these functions are for dealing with a list as a table, of sorts.
526 * An idea of the "current element" is kept and used by all the functions
527 * between Lst_Open() and Lst_Close().
528 *
529 * The sequential functions access the list in a slightly different way.
530 * CurPtr points to their idea of the current node in the list and they
531 * access the list based on it.
532 */
533
534 /* Open a list for sequential access. A list can still be searched, etc.,
535 * without confusing these functions. */
536 void
537 Lst_Open(List *list)
538 {
539 assert(list != NULL);
540 assert(!list->isOpen);
541
542 list->isOpen = TRUE;
543 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
544 list->curr = NULL;
545 }
546
547 /* Return the next node for the given list, or NULL if the end has been
548 * reached. */
549 ListNode *
550 Lst_Next(List *list)
551 {
552 ListNode *node;
553
554 assert(list != NULL);
555 assert(list->isOpen);
556
557 list->prev = list->curr;
558
559 if (list->curr == NULL) {
560 if (list->lastAccess == Unknown) {
561 /*
562 * If we're just starting out, lastAccess will be Unknown.
563 * Then we want to start this thing off in the right
564 * direction -- at the start with lastAccess being Middle.
565 */
566 list->curr = node = list->first;
567 list->lastAccess = Middle;
568 } else {
569 node = NULL;
570 list->lastAccess = Tail;
571 }
572 } else {
573 node = list->curr->next;
574 list->curr = node;
575
576 if (node == list->first || node == NULL) {
577 /*
578 * If back at the front, then we've hit the end...
579 */
580 list->lastAccess = Tail;
581 } else {
582 /*
583 * Reset to Middle if gone past first.
584 */
585 list->lastAccess = Middle;
586 }
587 }
588
589 return node;
590 }
591
592 /* Close a list which was opened for sequential access. */
593 void
594 Lst_Close(List *list)
595 {
596 assert(list != NULL);
597 assert(list->isOpen);
598
599 list->isOpen = FALSE;
600 list->lastAccess = Unknown;
601 }
602
603
604 /*
605 * for using the list as a queue
606 */
607
608 /* Add the datum to the tail of the given list. */
609 void
610 Lst_Enqueue(List *list, void *datum)
611 {
612 Lst_Append(list, datum);
613 }
614
615 /* Remove and return the datum at the head of the given list. */
616 void *
617 Lst_Dequeue(List *list)
618 {
619 void *datum;
620
621 assert(list != NULL);
622 assert(!LstIsEmpty(list));
623
624 datum = list->first->datum;
625 Lst_Remove(list, list->first);
626 assert(datum != NULL);
627 return datum;
628 }
629
630 void
631 Stack_Init(Stack *stack)
632 {
633 stack->len = 0;
634 stack->cap = 10;
635 stack->items = bmake_malloc(stack->cap * sizeof stack->items[0]);
636 }
637
638 Boolean Stack_IsEmpty(Stack *stack)
639 {
640 return stack->len == 0;
641 }
642
643 void Stack_Push(Stack *stack, void *datum)
644 {
645 if (stack->len >= stack->cap) {
646 stack->cap *= 2;
647 stack->items = bmake_realloc(stack->items,
648 stack->cap * sizeof stack->items[0]);
649 }
650 stack->items[stack->len] = datum;
651 stack->len++;
652 }
653
654 void *Stack_Pop(Stack *stack)
655 {
656 void *datum;
657
658 assert(stack->len > 0);
659 stack->len--;
660 datum = stack->items[stack->len];
661 #ifdef CLEANUP
662 stack->items[stack->len] = NULL;
663 #endif
664 return datum;
665 }
666
667 void Stack_Done(Stack *stack)
668 {
669 free(stack->items);
670 }
671