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