lst.c revision 1.79 1 /* $NetBSD: lst.c,v 1.79 2020/10/19 21:57:37 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 "make.h"
36
37 MAKE_RCSID("$NetBSD: lst.c,v 1.79 2020/10/19 21:57:37 rillig Exp $");
38
39 /* Allocate and initialize a list node.
40 *
41 * The fields 'prev' and 'next' must be initialized by the caller.
42 */
43 static ListNode *
44 LstNodeNew(void *datum)
45 {
46 ListNode *node = bmake_malloc(sizeof *node);
47 node->priv_useCount = 0;
48 node->priv_deleted = FALSE;
49 node->datum = datum;
50 return node;
51 }
52
53 static Boolean
54 LstIsEmpty(List *list)
55 {
56 return list->first == NULL;
57 }
58
59 /* Create and initialize a new, empty list. */
60 List *
61 Lst_New(void)
62 {
63 List *list = bmake_malloc(sizeof *list);
64
65 list->first = NULL;
66 list->last = NULL;
67 list->priv_isOpen = FALSE;
68 list->priv_lastAccess = Unknown;
69
70 return list;
71 }
72
73 /* Duplicate an entire list, usually by copying the datum pointers.
74 * If copyProc is given, that function is used to create the new datum from the
75 * old datum, usually by creating a copy of it. */
76 List *
77 Lst_Copy(List *list, LstCopyProc copyProc)
78 {
79 List *newList;
80 ListNode *node;
81
82 newList = Lst_New();
83
84 for (node = list->first; node != NULL; node = node->next) {
85 void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum;
86 Lst_Append(newList, datum);
87 }
88
89 return newList;
90 }
91
92 /* Free a list and all its nodes. The list data itself are not freed though. */
93 void
94 Lst_Free(List *list)
95 {
96 ListNode *node;
97 ListNode *next;
98
99 for (node = list->first; node != NULL; node = next) {
100 next = node->next;
101 free(node);
102 }
103
104 free(list);
105 }
106
107 /* Destroy a list and free all its resources. The freeProc is called with the
108 * datum from each node in turn before the node is freed. */
109 void
110 Lst_Destroy(List *list, LstFreeProc freeProc)
111 {
112 ListNode *node;
113 ListNode *next;
114
115 for (node = list->first; node != NULL; node = next) {
116 next = node->next;
117 freeProc(node->datum);
118 free(node);
119 }
120
121 free(list);
122 }
123
124 /*
125 * Functions to modify a list
126 */
127
128 /* Insert a new node with the given piece of data before the given node in the
129 * given list. */
130 void
131 Lst_InsertBefore(List *list, ListNode *node, void *datum)
132 {
133 ListNode *newNode;
134
135 assert(!LstIsEmpty(list));
136 assert(datum != NULL);
137
138 newNode = LstNodeNew(datum);
139 newNode->prev = node->prev;
140 newNode->next = node;
141
142 if (node->prev != NULL) {
143 node->prev->next = newNode;
144 }
145 node->prev = newNode;
146
147 if (node == list->first) {
148 list->first = newNode;
149 }
150 }
151
152 /* Add a piece of data at the start of the given list. */
153 void
154 Lst_Prepend(List *list, void *datum)
155 {
156 ListNode *node;
157
158 assert(datum != NULL);
159
160 node = LstNodeNew(datum);
161 node->prev = NULL;
162 node->next = list->first;
163
164 if (list->first == NULL) {
165 list->first = node;
166 list->last = node;
167 } else {
168 list->first->prev = node;
169 list->first = node;
170 }
171 }
172
173 /* Add a piece of data at the end of the given list. */
174 void
175 Lst_Append(List *list, void *datum)
176 {
177 ListNode *node;
178
179 assert(datum != NULL);
180
181 node = LstNodeNew(datum);
182 node->prev = list->last;
183 node->next = NULL;
184
185 if (list->last == NULL) {
186 list->first = node;
187 list->last = node;
188 } else {
189 list->last->next = node;
190 list->last = node;
191 }
192 }
193
194 /* Remove the given node from the given list.
195 * The datum stored in the node must be freed by the caller, if necessary. */
196 void
197 Lst_Remove(List *list, ListNode *node)
198 {
199 /*
200 * unlink it from the list
201 */
202 if (node->next != NULL) {
203 node->next->prev = node->prev;
204 }
205 if (node->prev != NULL) {
206 node->prev->next = node->next;
207 }
208
209 /*
210 * if either the first or last of the list point to this node,
211 * adjust them accordingly
212 */
213 if (list->first == node) {
214 list->first = node->next;
215 }
216 if (list->last == node) {
217 list->last = node->prev;
218 }
219
220 /*
221 * Sequential access stuff. If the node we're removing is the current
222 * node in the list, reset the current node to the previous one. If the
223 * previous one was non-existent (prev == NULL), we set the
224 * end to be Unknown, since it is.
225 */
226 if (list->priv_isOpen && list->priv_curr == node) {
227 list->priv_curr = list->priv_prev;
228 if (list->priv_curr == NULL) {
229 list->priv_lastAccess = Unknown;
230 }
231 }
232
233 /*
234 * note that the datum is unmolested. The caller must free it as
235 * necessary and as expected.
236 */
237 if (node->priv_useCount == 0) {
238 free(node);
239 } else {
240 node->priv_deleted = TRUE;
241 }
242 }
243
244 /* Replace the datum in the given node with the new datum. */
245 void
246 LstNode_Set(ListNode *node, void *datum)
247 {
248 assert(datum != NULL);
249
250 node->datum = datum;
251 }
252
253 /* Replace the datum in the given node to NULL.
254 * Having NULL values in a list is unusual though. */
255 void
256 LstNode_SetNull(ListNode *node)
257 {
258 node->datum = NULL;
259 }
260
261
262 /*
263 * Functions for entire lists
264 */
265
266 /* Return the first node from the list for which the match function returns
267 * TRUE, or NULL if none of the nodes matched. */
268 ListNode *
269 Lst_Find(List *list, LstFindProc match, const void *matchArgs)
270 {
271 return Lst_FindFrom(list, list->first, match, matchArgs);
272 }
273
274 /* Return the first node from the list, starting at the given node, for which
275 * the match function returns TRUE, or NULL if none of the nodes matches.
276 *
277 * The start node may be NULL, in which case nothing is found. */
278 ListNode *
279 Lst_FindFrom(List *list, ListNode *node, LstFindProc match, const void *matchArgs)
280 {
281 ListNode *tln;
282
283 assert(list != NULL);
284 assert(match != NULL);
285
286 for (tln = node; tln != NULL; tln = tln->next) {
287 if (match(tln->datum, matchArgs))
288 return tln;
289 }
290
291 return NULL;
292 }
293
294 /* Return the first node that contains the given datum, or NULL. */
295 ListNode *
296 Lst_FindDatum(List *list, const void *datum)
297 {
298 ListNode *node;
299
300 assert(datum != NULL);
301
302 for (node = list->first; node != NULL; node = node->next) {
303 if (node->datum == datum) {
304 return node;
305 }
306 }
307
308 return NULL;
309 }
310
311 /* Apply the given function to each element of the given list. The function
312 * should return 0 if traversal should continue and non-zero if it should
313 * abort. */
314 int
315 Lst_ForEachUntil(List *list, LstActionUntilProc proc, void *procData)
316 {
317 ListNode *tln = list->first;
318 int result = 0;
319
320 while (tln != NULL) {
321 /*
322 * Take care of having the current element deleted out from under
323 * us.
324 */
325 ListNode *next = tln->next;
326
327 /*
328 * We're done with the traversal if
329 * - the next node to examine doesn't exist and
330 * - nothing's been added after the current node (check this
331 * after proc() has been called).
332 */
333 Boolean done = next == NULL;
334
335 tln->priv_useCount++;
336 result = proc(tln->datum, procData);
337 tln->priv_useCount--;
338
339 /*
340 * Now check whether a node has been added.
341 * Note: this doesn't work if this node was deleted before
342 * the new node was added.
343 */
344 if (next != tln->next) {
345 next = tln->next;
346 done = 0;
347 }
348
349 if (tln->priv_deleted) {
350 free((char *)tln);
351 }
352 tln = next;
353 if (result || LstIsEmpty(list) || done)
354 break;
355 }
356
357 return result;
358 }
359
360 /* Move all nodes from list2 to the end of list1.
361 * List2 is destroyed and freed. */
362 void
363 Lst_MoveAll(List *list1, List *list2)
364 {
365 if (list2->first != NULL) {
366 list2->first->prev = list1->last;
367 if (list1->last != NULL) {
368 list1->last->next = list2->first;
369 } else {
370 list1->first = list2->first;
371 }
372 list1->last = list2->last;
373 }
374 free(list2);
375 }
376
377 /* Copy the element data from src to the start of dst. */
378 void
379 Lst_PrependAll(List *dst, List *src)
380 {
381 ListNode *node;
382 for (node = src->last; node != NULL; node = node->prev)
383 Lst_Prepend(dst, node->datum);
384 }
385
386 /* Copy the element data from src to the end of dst. */
387 void
388 Lst_AppendAll(List *dst, List *src)
389 {
390 ListNode *node;
391 for (node = src->first; node != NULL; node = node->next)
392 Lst_Append(dst, node->datum);
393 }
394
395 /*
396 * these functions are for dealing with a list as a table, of sorts.
397 * An idea of the "current element" is kept and used by all the functions
398 * between Lst_Open() and Lst_Close().
399 *
400 * The sequential functions access the list in a slightly different way.
401 * CurPtr points to their idea of the current node in the list and they
402 * access the list based on it.
403 */
404
405 /* Open a list for sequential access. A list can still be searched, etc.,
406 * without confusing these functions. */
407 void
408 Lst_Open(List *list)
409 {
410 assert(!list->priv_isOpen);
411
412 list->priv_isOpen = TRUE;
413 list->priv_lastAccess = LstIsEmpty(list) ? Head : Unknown;
414 list->priv_curr = NULL;
415 }
416
417 /* Return the next node for the given list, or NULL if the end has been
418 * reached. */
419 ListNode *
420 Lst_Next(List *list)
421 {
422 ListNode *node;
423
424 assert(list->priv_isOpen);
425
426 list->priv_prev = list->priv_curr;
427
428 if (list->priv_curr == NULL) {
429 if (list->priv_lastAccess == Unknown) {
430 /*
431 * If we're just starting out, lastAccess will be Unknown.
432 * Then we want to start this thing off in the right
433 * direction -- at the start with lastAccess being Middle.
434 */
435 list->priv_curr = node = list->first;
436 list->priv_lastAccess = Middle;
437 } else {
438 node = NULL;
439 list->priv_lastAccess = Tail;
440 }
441 } else {
442 node = list->priv_curr->next;
443 list->priv_curr = node;
444
445 if (node == list->first || node == NULL) {
446 /*
447 * If back at the front, then we've hit the end...
448 */
449 list->priv_lastAccess = Tail;
450 } else {
451 /*
452 * Reset to Middle if gone past first.
453 */
454 list->priv_lastAccess = Middle;
455 }
456 }
457
458 return node;
459 }
460
461 /* Close a list which was opened for sequential access. */
462 void
463 Lst_Close(List *list)
464 {
465 assert(list->priv_isOpen);
466
467 list->priv_isOpen = FALSE;
468 list->priv_lastAccess = Unknown;
469 }
470
471
472 /*
473 * for using the list as a queue
474 */
475
476 /* Add the datum to the tail of the given list. */
477 void
478 Lst_Enqueue(List *list, void *datum)
479 {
480 Lst_Append(list, datum);
481 }
482
483 /* Remove and return the datum at the head of the given list. */
484 void *
485 Lst_Dequeue(List *list)
486 {
487 void *datum = list->first->datum;
488 Lst_Remove(list, list->first);
489 assert(datum != NULL); /* since NULL would mean end of the list */
490 return datum;
491 }
492
493 void
494 Vector_Init(Vector *v)
495 {
496 v->len = 0;
497 v->cap = 10;
498 v->items = bmake_malloc(v->cap * sizeof v->items[0]);
499 }
500
501 Boolean Vector_IsEmpty(Vector *v)
502 {
503 return v->len == 0;
504 }
505
506 void Vector_Push(Vector *v, void *datum)
507 {
508 if (v->len >= v->cap) {
509 v->cap *= 2;
510 v->items = bmake_realloc(v->items,
511 v->cap * sizeof v->items[0]);
512 }
513 v->items[v->len] = datum;
514 v->len++;
515 }
516
517 void *Vector_Pop(Vector *v)
518 {
519 void *datum;
520
521 assert(v->len > 0);
522 v->len--;
523 datum = v->items[v->len];
524 #ifdef CLEANUP
525 v->items[v->len] = NULL;
526 #endif
527 return datum;
528 }
529
530 void Vector_Done(Vector *v)
531 {
532 free(v->items);
533 }
534