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