lst.c revision 1.82 1 /* $NetBSD: lst.c,v 1.82 2020/10/22 21:27:24 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.82 2020/10/22 21:27:24 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
68 return list;
69 }
70
71 /* Duplicate an entire list, usually by copying the datum pointers.
72 * If copyProc is given, that function is used to create the new datum from the
73 * old datum, usually by creating a copy of it. */
74 List *
75 Lst_Copy(List *list, LstCopyProc copyProc)
76 {
77 List *newList;
78 ListNode *node;
79
80 newList = Lst_New();
81
82 for (node = list->first; node != NULL; node = node->next) {
83 void *datum = copyProc != NULL ? copyProc(node->datum) : node->datum;
84 Lst_Append(newList, datum);
85 }
86
87 return newList;
88 }
89
90 /* Free a list and all its nodes. The list data itself are not freed though. */
91 void
92 Lst_Free(List *list)
93 {
94 ListNode *node;
95 ListNode *next;
96
97 for (node = list->first; node != NULL; node = next) {
98 next = node->next;
99 free(node);
100 }
101
102 free(list);
103 }
104
105 /* Destroy a list and free all its resources. The freeProc is called with the
106 * datum from each node in turn before the node is freed. */
107 void
108 Lst_Destroy(List *list, LstFreeProc freeProc)
109 {
110 ListNode *node;
111 ListNode *next;
112
113 for (node = list->first; node != NULL; node = next) {
114 next = node->next;
115 freeProc(node->datum);
116 free(node);
117 }
118
119 free(list);
120 }
121
122 /*
123 * Functions to modify a list
124 */
125
126 /* Insert a new node with the given piece of data before the given node in the
127 * given list. */
128 void
129 Lst_InsertBefore(List *list, ListNode *node, void *datum)
130 {
131 ListNode *newNode;
132
133 assert(!LstIsEmpty(list));
134 assert(datum != NULL);
135
136 newNode = LstNodeNew(datum);
137 newNode->prev = node->prev;
138 newNode->next = node;
139
140 if (node->prev != NULL) {
141 node->prev->next = newNode;
142 }
143 node->prev = newNode;
144
145 if (node == list->first) {
146 list->first = newNode;
147 }
148 }
149
150 /* Add a piece of data at the start of the given list. */
151 void
152 Lst_Prepend(List *list, void *datum)
153 {
154 ListNode *node;
155
156 assert(datum != NULL);
157
158 node = LstNodeNew(datum);
159 node->prev = NULL;
160 node->next = list->first;
161
162 if (list->first == NULL) {
163 list->first = node;
164 list->last = node;
165 } else {
166 list->first->prev = node;
167 list->first = node;
168 }
169 }
170
171 /* Add a piece of data at the end of the given list. */
172 void
173 Lst_Append(List *list, void *datum)
174 {
175 ListNode *node;
176
177 assert(datum != NULL);
178
179 node = LstNodeNew(datum);
180 node->prev = list->last;
181 node->next = NULL;
182
183 if (list->last == NULL) {
184 list->first = node;
185 list->last = node;
186 } else {
187 list->last->next = node;
188 list->last = node;
189 }
190 }
191
192 /* Remove the given node from the given list.
193 * The datum stored in the node must be freed by the caller, if necessary. */
194 void
195 Lst_Remove(List *list, ListNode *node)
196 {
197 /*
198 * unlink it from the list
199 */
200 if (node->next != NULL) {
201 node->next->prev = node->prev;
202 }
203 if (node->prev != NULL) {
204 node->prev->next = node->next;
205 }
206
207 /*
208 * if either the first or last of the list point to this node,
209 * adjust them accordingly
210 */
211 if (list->first == node) {
212 list->first = node->next;
213 }
214 if (list->last == node) {
215 list->last = node->prev;
216 }
217
218 /*
219 * note that the datum is unmolested. The caller must free it as
220 * necessary and as expected.
221 */
222 if (node->priv_useCount == 0) {
223 free(node);
224 } else {
225 node->priv_deleted = TRUE;
226 }
227 }
228
229 /* Replace the datum in the given node with the new datum. */
230 void
231 LstNode_Set(ListNode *node, void *datum)
232 {
233 assert(datum != NULL);
234
235 node->datum = datum;
236 }
237
238 /* Replace the datum in the given node to NULL.
239 * Having NULL values in a list is unusual though. */
240 void
241 LstNode_SetNull(ListNode *node)
242 {
243 node->datum = NULL;
244 }
245
246
247 /*
248 * Functions for entire lists
249 */
250
251 /* Return the first node from the list for which the match function returns
252 * TRUE, or NULL if none of the nodes matched. */
253 ListNode *
254 Lst_Find(List *list, LstFindProc match, const void *matchArgs)
255 {
256 return Lst_FindFrom(list, list->first, match, matchArgs);
257 }
258
259 /* Return the first node from the list, starting at the given node, for which
260 * the match function returns TRUE, or NULL if none of the nodes matches.
261 *
262 * The start node may be NULL, in which case nothing is found. */
263 ListNode *
264 Lst_FindFrom(List *list, ListNode *node, LstFindProc match, const void *matchArgs)
265 {
266 ListNode *tln;
267
268 assert(list != NULL);
269 assert(match != NULL);
270
271 for (tln = node; tln != NULL; tln = tln->next) {
272 if (match(tln->datum, matchArgs))
273 return tln;
274 }
275
276 return NULL;
277 }
278
279 /* Return the first node that contains the given datum, or NULL. */
280 ListNode *
281 Lst_FindDatum(List *list, const void *datum)
282 {
283 ListNode *node;
284
285 assert(datum != NULL);
286
287 for (node = list->first; node != NULL; node = node->next) {
288 if (node->datum == datum) {
289 return node;
290 }
291 }
292
293 return NULL;
294 }
295
296 /* Apply the given function to each element of the given list, until the
297 * function returns non-zero. During this iteration, the list must not be
298 * modified structurally. */
299 int
300 Lst_ForEachUntil(List *list, LstActionUntilProc proc, void *procData)
301 {
302 ListNode *node;
303 int result = 0;
304
305 for (node = list->first; node != NULL; node = node->next) {
306 result = proc(node->datum, procData);
307 if (result != 0)
308 break;
309 }
310 return result;
311 }
312
313 /* Apply the given function to each element of the given list. The function
314 * should return 0 if traversal should continue and non-zero if it should
315 * abort. */
316 int
317 Lst_ForEachUntilConcurrent(List *list, LstActionUntilProc proc, void *procData)
318 {
319 ListNode *tln = list->first;
320 int result = 0;
321
322 while (tln != NULL) {
323 /*
324 * Take care of having the current element deleted out from under
325 * us.
326 */
327 ListNode *next = tln->next;
328
329 /*
330 * We're done with the traversal if
331 * - the next node to examine doesn't exist and
332 * - nothing's been added after the current node (check this
333 * after proc() has been called).
334 */
335 Boolean done = next == NULL;
336
337 tln->priv_useCount++;
338 result = proc(tln->datum, procData);
339 tln->priv_useCount--;
340
341 /*
342 * Now check whether a node has been added.
343 * Note: this doesn't work if this node was deleted before
344 * the new node was added.
345 */
346 if (next != tln->next) {
347 next = tln->next;
348 done = FALSE;
349 }
350
351 if (tln->priv_deleted)
352 free(tln);
353 tln = next;
354 if (result || LstIsEmpty(list) || done)
355 break;
356 }
357
358 return result;
359 }
360
361 /* Move all nodes from list2 to the end of list1.
362 * List2 is destroyed and freed. */
363 void
364 Lst_MoveAll(List *list1, List *list2)
365 {
366 if (list2->first != NULL) {
367 list2->first->prev = list1->last;
368 if (list1->last != NULL) {
369 list1->last->next = list2->first;
370 } else {
371 list1->first = list2->first;
372 }
373 list1->last = list2->last;
374 }
375 free(list2);
376 }
377
378 /* Copy the element data from src to the start of dst. */
379 void
380 Lst_PrependAll(List *dst, List *src)
381 {
382 ListNode *node;
383 for (node = src->last; node != NULL; node = node->prev)
384 Lst_Prepend(dst, node->datum);
385 }
386
387 /* Copy the element data from src to the end of dst. */
388 void
389 Lst_AppendAll(List *dst, List *src)
390 {
391 ListNode *node;
392 for (node = src->first; node != NULL; node = node->next)
393 Lst_Append(dst, node->datum);
394 }
395
396 /*
397 * for using the list as a queue
398 */
399
400 /* Add the datum to the tail of the given list. */
401 void
402 Lst_Enqueue(List *list, void *datum)
403 {
404 Lst_Append(list, datum);
405 }
406
407 /* Remove and return the datum at the head of the given list. */
408 void *
409 Lst_Dequeue(List *list)
410 {
411 void *datum = list->first->datum;
412 Lst_Remove(list, list->first);
413 assert(datum != NULL); /* since NULL would mean end of the list */
414 return datum;
415 }
416
417 void
418 Vector_Init(Vector *v)
419 {
420 v->len = 0;
421 v->cap = 10;
422 v->items = bmake_malloc(v->cap * sizeof v->items[0]);
423 }
424
425 Boolean Vector_IsEmpty(Vector *v)
426 {
427 return v->len == 0;
428 }
429
430 void Vector_Push(Vector *v, void *datum)
431 {
432 if (v->len >= v->cap) {
433 v->cap *= 2;
434 v->items = bmake_realloc(v->items,
435 v->cap * sizeof v->items[0]);
436 }
437 v->items[v->len] = datum;
438 v->len++;
439 }
440
441 void *Vector_Pop(Vector *v)
442 {
443 void *datum;
444
445 assert(v->len > 0);
446 v->len--;
447 datum = v->items[v->len];
448 #ifdef CLEANUP
449 v->items[v->len] = NULL;
450 #endif
451 return datum;
452 }
453
454 void Vector_Done(Vector *v)
455 {
456 free(v->items);
457 }
458