lst.c revision 1.18 1 /* $NetBSD: lst.c,v 1.18 2020/08/21 14:33:32 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 <assert.h>
36
37 #include "lst.h"
38 #include "make_malloc.h"
39
40 #ifndef MAKE_NATIVE
41 static char rcsid[] = "$NetBSD: lst.c,v 1.18 2020/08/21 14:33:32 rillig Exp $";
42 #else
43 #include <sys/cdefs.h>
44 #ifndef lint
45 __RCSID("$NetBSD: lst.c,v 1.18 2020/08/21 14:33:32 rillig Exp $");
46 #endif /* not lint */
47 #endif
48
49 struct ListNode {
50 struct ListNode *prev; /* previous element in list */
51 struct ListNode *next; /* next in list */
52 uint8_t useCount; /* Count of functions using the node.
53 * node may not be deleted until count
54 * goes to 0 */
55 Boolean deleted; /* List node should be removed when done */
56 void *datum; /* datum associated with this element */
57 };
58
59 typedef enum {
60 Head, Middle, Tail, Unknown
61 } Where;
62
63 struct List {
64 LstNode first; /* first node in list */
65 LstNode last; /* last node in list */
66 /*
67 * fields for sequential access
68 */
69 Where lastAccess; /* Where in the list the last access was */
70 Boolean isOpen; /* true if list has been Lst_Open'ed */
71 LstNode curr; /* current node, if open. NULL if
72 * *just* opened */
73 LstNode prev; /* Previous node, if open. Used by
74 * Lst_Remove */
75 };
76
77 /* Return TRUE if the list is valid. */
78 static Boolean
79 LstValid(Lst list)
80 {
81 return list != NULL;
82 }
83
84 /* Return TRUE if the list node is valid. */
85 static Boolean
86 LstNodeValid(LstNode node)
87 {
88 return node != NULL;
89 }
90
91 static LstNode
92 LstNodeNew(void *datum)
93 {
94 LstNode node = bmake_malloc(sizeof *node);
95 /* prev will be initialized by the calling code. */
96 /* next will be initialized by the calling code. */
97 node->useCount = 0;
98 node->deleted = FALSE;
99 node->datum = datum;
100 return node;
101 }
102
103 /* Return TRUE if the list is empty. */
104 static Boolean
105 LstIsEmpty(Lst list)
106 {
107 return list->first == NULL;
108 }
109
110 /* Create and initialize a new, empty list. */
111 Lst
112 Lst_Init(void)
113 {
114 Lst list = bmake_malloc(sizeof *list);
115
116 list->first = NULL;
117 list->last = NULL;
118 list->isOpen = FALSE;
119 list->lastAccess = Unknown;
120
121 return list;
122 }
123
124 /* Duplicate an entire list, usually by copying the datum pointers.
125 * If copyProc is given, that function is used to create the new datum from the
126 * old datum, usually by creating a copy of it.
127 * Return the new list, or NULL on failure. */
128 Lst
129 Lst_Duplicate(Lst list, DuplicateProc *copyProc)
130 {
131 Lst newList;
132 LstNode node;
133
134 if (!LstValid(list)) {
135 return NULL;
136 }
137
138 newList = Lst_Init();
139
140 node = list->first;
141 while (node != NULL) {
142 if (copyProc != NULL) {
143 if (Lst_AtEnd(newList, copyProc(node->datum)) == FAILURE) {
144 return NULL;
145 }
146 } else if (Lst_AtEnd(newList, node->datum) == FAILURE) {
147 return NULL;
148 }
149
150 node = node->next;
151 }
152
153 return newList;
154 }
155
156 /* Destroy a list and free all its resources. If the freeProc is given, it is
157 * called with the datum from each node in turn before the node is freed. */
158 void
159 Lst_Destroy(Lst list, FreeProc *freeProc)
160 {
161 LstNode node;
162 LstNode next = NULL;
163
164 if (list == NULL)
165 return;
166
167 /* To ease scanning */
168 if (list->last != NULL)
169 list->last->next = NULL;
170 else {
171 free(list);
172 return;
173 }
174
175 if (freeProc) {
176 for (node = list->first; node != NULL; node = next) {
177 next = node->next;
178 freeProc(node->datum);
179 free(node);
180 }
181 } else {
182 for (node = list->first; node != NULL; node = next) {
183 next = node->next;
184 free(node);
185 }
186 }
187
188 free(list);
189 }
190
191 /*
192 * Functions to modify a list
193 */
194
195 /* Insert a new node with the given piece of data before the given node in the
196 * given list. */
197 ReturnStatus
198 Lst_InsertBefore(Lst list, LstNode node, void *datum)
199 {
200 LstNode newNode;
201
202 /*
203 * check validity of arguments
204 */
205 if (LstValid(list) && (LstIsEmpty(list) && node == NULL))
206 goto ok;
207
208 if (!LstValid(list) || LstIsEmpty(list) || !LstNodeValid(node)) {
209 return FAILURE;
210 }
211
212 ok:
213 newNode = LstNodeNew(datum);
214
215 if (node == NULL) {
216 newNode->prev = newNode->next = NULL;
217 list->first = list->last = newNode;
218 } else {
219 newNode->prev = node->prev;
220 newNode->next = node;
221
222 if (newNode->prev != NULL) {
223 newNode->prev->next = newNode;
224 }
225 node->prev = newNode;
226
227 if (node == list->first) {
228 list->first = newNode;
229 }
230 }
231
232 return SUCCESS;
233 }
234
235 /* Insert a new node with the given piece of data after the given node in the
236 * given list. */
237 ReturnStatus
238 Lst_InsertAfter(Lst list, LstNode node, void *datum)
239 {
240 LstNode newNode;
241
242 if (LstValid(list) && (node == NULL && LstIsEmpty(list))) {
243 goto ok;
244 }
245
246 if (!LstValid(list) || LstIsEmpty(list) || !LstNodeValid(node)) {
247 return FAILURE;
248 }
249 ok:
250
251 newNode = LstNodeNew(datum);
252
253 if (node == NULL) {
254 newNode->next = newNode->prev = NULL;
255 list->first = list->last = newNode;
256 } else {
257 newNode->prev = node;
258 newNode->next = node->next;
259
260 node->next = newNode;
261 if (newNode->next != NULL) {
262 newNode->next->prev = newNode;
263 }
264
265 if (node == list->last) {
266 list->last = newNode;
267 }
268 }
269
270 return SUCCESS;
271 }
272
273 /* Add a piece of data at the front of the given list. */
274 ReturnStatus
275 Lst_AtFront(Lst list, void *datum)
276 {
277 LstNode front = Lst_First(list);
278 return Lst_InsertBefore(list, front, datum);
279 }
280
281 /* Add a piece of data at the end of the given list. */
282 ReturnStatus
283 Lst_AtEnd(Lst list, void *datum)
284 {
285 LstNode end = Lst_Last(list);
286 return Lst_InsertAfter(list, end, datum);
287 }
288
289 /* Remove the given node from the given list.
290 * The datum stored in the node must be freed by the caller, if necessary. */
291 void
292 Lst_RemoveS(Lst list, LstNode node)
293 {
294 assert(LstValid(list));
295 assert(LstNodeValid(node));
296
297 /*
298 * unlink it from the list
299 */
300 if (node->next != NULL) {
301 node->next->prev = node->prev;
302 }
303 if (node->prev != NULL) {
304 node->prev->next = node->next;
305 }
306
307 /*
308 * if either the first or last of the list point to this node,
309 * adjust them accordingly
310 */
311 if (list->first == node) {
312 list->first = node->next;
313 }
314 if (list->last == node) {
315 list->last = node->prev;
316 }
317
318 /*
319 * Sequential access stuff. If the node we're removing is the current
320 * node in the list, reset the current node to the previous one. If the
321 * previous one was non-existent (prev == NULL), we set the
322 * end to be Unknown, since it is.
323 */
324 if (list->isOpen && list->curr == node) {
325 list->curr = list->prev;
326 if (list->curr == NULL) {
327 list->lastAccess = Unknown;
328 }
329 }
330
331 /*
332 * note that the datum is unmolested. The caller must free it as
333 * necessary and as expected.
334 */
335 if (node->useCount == 0) {
336 free(node);
337 } else {
338 node->deleted = TRUE;
339 }
340 }
341
342 /* Replace the datum in the given node with the new datum. */
343 void
344 Lst_ReplaceS(LstNode node, void *datum)
345 {
346 node->datum = datum;
347 }
348
349
350 /*
351 * Node-specific functions
352 */
353
354 /* Return the first node from the given list, or NULL if the list is empty or
355 * invalid. */
356 LstNode
357 Lst_First(Lst list)
358 {
359 if (!LstValid(list) || LstIsEmpty(list)) {
360 return NULL;
361 } else {
362 return list->first;
363 }
364 }
365
366 /* Return the last node from the given list, or NULL if the list is empty or
367 * invalid. */
368 LstNode
369 Lst_Last(Lst list)
370 {
371 if (!LstValid(list) || LstIsEmpty(list)) {
372 return NULL;
373 } else {
374 return list->last;
375 }
376 }
377
378 /* Return the successor to the given node on its list, or NULL. */
379 LstNode
380 Lst_Succ(LstNode node)
381 {
382 if (node == NULL) {
383 return NULL;
384 } else {
385 return node->next;
386 }
387 }
388
389 /* Return the predecessor to the given node on its list, or NULL. */
390 LstNode
391 Lst_Prev(LstNode node)
392 {
393 if (node == NULL) {
394 return NULL;
395 } else {
396 return node->prev;
397 }
398 }
399
400 /* Return the datum stored in the given node, or NULL if the node is invalid. */
401 void *
402 Lst_Datum(LstNode node)
403 {
404 if (node != NULL) {
405 return node->datum;
406 } else {
407 return NULL;
408 }
409 }
410
411
412 /*
413 * Functions for entire lists
414 */
415
416 /* Return TRUE if the given list is empty or invalid. */
417 Boolean
418 Lst_IsEmpty(Lst list)
419 {
420 return !LstValid(list) || LstIsEmpty(list);
421 }
422
423 /* Return the first node from the given list for which the given comparison
424 * function returns 0, or NULL if none of the nodes matches. */
425 LstNode
426 Lst_Find(Lst list, const void *cmpData, int (*cmp)(const void *, const void *))
427 {
428 return Lst_FindFrom(list, Lst_First(list), cmpData, cmp);
429 }
430
431 /* Return the first node from the given list, starting at the given node, for
432 * which the given comparison function returns 0, or NULL if none of the nodes
433 * matches. */
434 LstNode
435 Lst_FindFrom(Lst list, LstNode node, const void *cmpData,
436 int (*cmp)(const void *, const void *))
437 {
438 LstNode tln;
439
440 if (!LstValid(list) || LstIsEmpty(list) || !LstNodeValid(node)) {
441 return NULL;
442 }
443
444 tln = node;
445
446 do {
447 if ((*cmp)(tln->datum, cmpData) == 0)
448 return tln;
449 tln = tln->next;
450 } while (tln != node && tln != NULL);
451
452 return NULL;
453 }
454
455 /* Return the first node that contains the given datum, or NULL. */
456 LstNode
457 Lst_Member(Lst list, void *datum)
458 {
459 LstNode node;
460
461 if (list == NULL) {
462 return NULL;
463 }
464 node = list->first;
465 if (node == NULL) {
466 return NULL;
467 }
468
469 do {
470 if (node->datum == datum) {
471 return node;
472 }
473 node = node->next;
474 } while (node != NULL && node != list->first);
475
476 return NULL;
477 }
478
479 /* Apply the given function to each element of the given list. The function
480 * should return 0 if traversal should continue and non-zero if it should
481 * abort. */
482 int
483 Lst_ForEach(Lst list, int (*proc)(void *, void *), void *procData)
484 {
485 return Lst_ForEachFrom(list, Lst_First(list), proc, procData);
486 }
487
488 /* Apply the given function to each element of the given list, starting from
489 * the given node. The function should return 0 if traversal should continue,
490 * and non-zero if it should abort. */
491 int
492 Lst_ForEachFrom(Lst list, LstNode node,
493 int (*proc)(void *, void *), void *procData)
494 {
495 LstNode tln = node;
496 LstNode next;
497 Boolean done;
498 int result;
499
500 if (!LstValid(list) || LstIsEmpty(list)) {
501 return 0;
502 }
503
504 do {
505 /*
506 * Take care of having the current element deleted out from under
507 * us.
508 */
509
510 next = tln->next;
511
512 /*
513 * We're done with the traversal if
514 * - the next node to examine is the first in the queue or
515 * doesn't exist and
516 * - nothing's been added after the current node (check this
517 * after proc() has been called).
518 */
519 done = (next == NULL || next == list->first);
520
521 tln->useCount++;
522 result = (*proc)(tln->datum, procData);
523 tln->useCount--;
524
525 /*
526 * Now check whether a node has been added.
527 * Note: this doesn't work if this node was deleted before
528 * the new node was added.
529 */
530 if (next != tln->next) {
531 next = tln->next;
532 done = 0;
533 }
534
535 if (tln->deleted) {
536 free((char *)tln);
537 }
538 tln = next;
539 } while (!result && !LstIsEmpty(list) && !done);
540
541 return result;
542 }
543
544 /* Concatenate two lists. New nodes are created to hold the data elements,
545 * if specified, but the data themselves are not copied. If the data
546 * should be duplicated to avoid confusion with another list, the Lst_Duplicate
547 * function should be called first. If LST_CONCLINK is specified, the second
548 * list is destroyed since its pointers have been corrupted and the list is no
549 * longer usable.
550 *
551 * Input:
552 * list1 The list to which list2 is to be appended
553 * list2 The list to append to list1
554 * flags LST_CONCNEW if the list nodes should be duplicated
555 * LST_CONCLINK if the list nodes should just be relinked
556 */
557 ReturnStatus
558 Lst_Concat(Lst list1, Lst list2, int flags)
559 {
560 LstNode node; /* original node */
561 LstNode newNode;
562 LstNode last; /* the last element in the list.
563 * Keeps bookkeeping until the end */
564
565 if (!LstValid(list1) || !LstValid(list2)) {
566 return FAILURE;
567 }
568
569 if (flags == LST_CONCLINK) {
570 if (list2->first != NULL) {
571 /*
572 * So long as the second list isn't empty, we just link the
573 * first element of the second list to the last element of the
574 * first list. If the first list isn't empty, we then link the
575 * last element of the list to the first element of the second list
576 * The last element of the second list, if it exists, then becomes
577 * the last element of the first list.
578 */
579 list2->first->prev = list1->last;
580 if (list1->last != NULL) {
581 list1->last->next = list2->first;
582 } else {
583 list1->first = list2->first;
584 }
585 list1->last = list2->last;
586 }
587 free(list2);
588 } else if (list2->first != NULL) {
589 /*
590 * We set the 'next' of the last element of list 2 to be nil to make
591 * the loop less difficult. The loop simply goes through the entire
592 * second list creating new LstNodes and filling in the 'next', and
593 * 'prev' to fit into list1 and its datum field from the
594 * datum field of the corresponding element in list2. The 'last' node
595 * follows the last of the new nodes along until the entire list2 has
596 * been appended. Only then does the bookkeeping catch up with the
597 * changes. During the first iteration of the loop, if 'last' is nil,
598 * the first list must have been empty so the newly-created node is
599 * made the first node of the list.
600 */
601 list2->last->next = NULL;
602 for (last = list1->last, node = list2->first;
603 node != NULL;
604 node = node->next)
605 {
606 newNode = LstNodeNew(node->datum);
607 if (last != NULL) {
608 last->next = newNode;
609 } else {
610 list1->first = newNode;
611 }
612 newNode->prev = last;
613 last = newNode;
614 }
615
616 /*
617 * Finish bookkeeping. The last new element becomes the last element
618 * of list one.
619 */
620 list1->last = last;
621 last->next = NULL;
622 }
623
624 return SUCCESS;
625 }
626
627
628 /*
629 * these functions are for dealing with a list as a table, of sorts.
630 * An idea of the "current element" is kept and used by all the functions
631 * between Lst_Open() and Lst_Close().
632 *
633 * The sequential functions access the list in a slightly different way.
634 * CurPtr points to their idea of the current node in the list and they
635 * access the list based on it.
636 */
637
638 /* Open a list for sequential access. A list can still be searched, etc.,
639 * without confusing these functions. */
640 ReturnStatus
641 Lst_Open(Lst list)
642 {
643 if (!LstValid(list)) {
644 return FAILURE;
645 }
646 list->isOpen = TRUE;
647 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
648 list->curr = NULL;
649
650 return SUCCESS;
651 }
652
653 /* Open a list for sequential access. A list can still be searched, etc.,
654 * without confusing these functions. */
655 void
656 Lst_OpenS(Lst list)
657 {
658 assert(LstValid(list));
659
660 list->isOpen = TRUE;
661 list->lastAccess = LstIsEmpty(list) ? Head : Unknown;
662 list->curr = NULL;
663 }
664
665 /* Return the next node for the given list, or NULL if the end has been
666 * reached. */
667 LstNode
668 Lst_NextS(Lst list)
669 {
670 LstNode node;
671
672 assert(LstValid(list));
673 assert(list->isOpen);
674
675 list->prev = list->curr;
676
677 if (list->curr == NULL) {
678 if (list->lastAccess == Unknown) {
679 /*
680 * If we're just starting out, lastAccess will be Unknown.
681 * Then we want to start this thing off in the right
682 * direction -- at the start with lastAccess being Middle.
683 */
684 list->curr = node = list->first;
685 list->lastAccess = Middle;
686 } else {
687 node = NULL;
688 list->lastAccess = Tail;
689 }
690 } else {
691 node = list->curr->next;
692 list->curr = node;
693
694 if (node == list->first || node == NULL) {
695 /*
696 * If back at the front, then we've hit the end...
697 */
698 list->lastAccess = Tail;
699 } else {
700 /*
701 * Reset to Middle if gone past first.
702 */
703 list->lastAccess = Middle;
704 }
705 }
706
707 return node;
708 }
709
710 /* Close a list which was opened for sequential access. */
711 void
712 Lst_CloseS(Lst list)
713 {
714 assert(LstValid(list));
715 assert(list->isOpen);
716
717 list->isOpen = FALSE;
718 list->lastAccess = Unknown;
719 }
720
721
722 /*
723 * for using the list as a queue
724 */
725
726 /* Add the datum to the tail of the given list. */
727 ReturnStatus
728 Lst_EnQueue(Lst list, void *datum)
729 {
730 if (!LstValid(list)) {
731 return FAILURE;
732 }
733
734 return Lst_InsertAfter(list, Lst_Last(list), datum);
735 }
736
737 /* Remove and return the datum at the head of the given list, or NULL if the
738 * list is empty. */
739 void *
740 Lst_DeQueue(Lst list)
741 {
742 LstNode head;
743 void *datum;
744
745 head = Lst_First(list);
746 if (head == NULL) {
747 return NULL;
748 }
749
750 datum = head->datum;
751 Lst_RemoveS(list, head);
752 return datum;
753 }
754