BRL-CAD
list.h
Go to the documentation of this file.
1 /* L I S T . H
2  * BRL-CAD
3  *
4  * Copyright (c) 2004-2024 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 
21 #ifndef BU_LIST_H
22 #define BU_LIST_H
23 
24 #include "common.h"
25 
26 #include "bu/defines.h"
27 #include "bu/assert.h"
28 #include "bu/magic.h"
29 
30 __BEGIN_DECLS
31 
32 /* TODO: Plan for replacement of bu_list with a more memory coherent
33  * vector/array storage, ideally lockfree and threadsafe but not
34  * strictly necessary. Some options to consider and benchmark:
35  *
36  * http://libcds.sourceforge.net
37  * https://code.google.com/archive/p/ccl/
38  * http://sglib.sourceforge.net
39  * tbb::concurrent_vector
40  * std::vector et. al
41  * https://github.com/xant/libhl
42  */
43 
44 /*----------------------------------------------------------------------*/
45 /** @addtogroup bu_list
46  *
47  * @brief Support routines for doubly-linked lists.
48  *
49  * These macros assume that all user-provided structures will have a
50  * "struct bu_list" as their first element (often named "l" [ell]).
51  * Thus, a pointer to the bu_list struct is a "pun" for the
52  * user-provided structure as well, and the pointers can be converted
53  * back and forth safely with type casts.
54  *
55  * Furthermore, the head of the linked list could be a full instance
56  * of the user-provided structure (although the storage-conscious
57  * programmer could make the head just an bu_list structure, with
58  * careful type casting). This results in a doubly-linked circular
59  * list, with the head having the same shape as all the list members.
60  * The application is free to make use of this symmetry and store data
61  * values in the head, or the extra storage in the head can be
62  * ignored.
63  *
64  * Where a macro expects an argument "p", it should be a pointer to a
65  * user-provided structure.
66  *
67  * Where a macro expects an argument "headp", it should be a pointer
68  * to a "struct bu_list" located in the list head, e.g., &(head.l).
69  *
70  * Where a macro expects an argument "old", "new", or "cur", it should
71  * be a pointer to the "struct bu_list" located either in a
72  * user-provided structure, e.g. &((p)->l), or for the case of "old"
73  * it may also be in the list head.
74  *
75  @code
76  --- BEGIN EXAMPLE ---
77 
78  // make bu_list the first element in your structure
79  struct my_structure {
80  struct bu_list l;
81  int my_data;
82  };
83 
84  // your actual list
85  struct my_structure *my_list = NULL;
86 
87  // allocate and initialize your list head
88  BU_GET(my_list, struct my_structure);
89  BU_LIST_INIT(&(my_list->l));
90  my_list->my_data = -1;
91 
92  // add a new element to your list
93  struct my_structure *new_entry;
94  BU_GET(new_entry, struct my_structure);
95  new_entry->my_data = rand();
96  BU_LIST_PUSH(&(my_list->l), &(new_entry->l));
97 
98  // iterate over your list, remove all items
99  struct my_structure *entry;
100  while (BU_LIST_WHILE(entry, my_structure, &(my_list->l))) {
101  bu_log("Entry value is %d\n", entry->my_data);
102  BU_LIST_DEQUEUE(&(entry->l));
103  BU_PUT(entry, struct my_structure);
104  }
105  BU_PUT(my_list, struct my_structure);
106 
107  --- END EXAMPLE ---
108  @endcode
109  *
110  * Dequeueing the head of a list is a valid and well defined operation
111  * which should be performed with caution. Unless a pointer to some
112  * other element of the list is retained by the application, the rest
113  * of the linked list can no longer be referred to.
114  *
115  * The "magic" field of the list header _must_ be set to the constant
116  * BU_LIST_HEAD_MAGIC, but the "magic" field of all list members
117  * should be established by user code, to identify the type of
118  * structure that the bu_list structure is embedded in. It is
119  * permissible for one list to contain an arbitrarily mixed set of
120  * user "magic" numbers, as long as the head is properly marked.
121  *
122  * There is a dual set of terminology used in some of the macros:
123  *
124  * FIRST/ LAST - from the point of view of the list head
125  * NEXT / PREV - from the point of view of a list member
126  * forw / back - the actual pointer names
127  */
128 /** @{*/
129 /** @file bu/list.h */
130 
131 struct bu_list {
132  uint32_t magic; /**< @brief Magic # for mem id/check */
133  struct bu_list *forw; /**< @brief "forward", "next" */
134  struct bu_list *back; /**< @brief "back", "last" */
135 };
136 typedef struct bu_list bu_list_t;
137 #define BU_LIST_NULL ((struct bu_list *)0)
138 
139 /**
140  * macro for setting the magic number of a non-head list node
141  */
142 #define BU_LIST_MAGIC_SET(_l, _magic) {(_l)->magic = (_magic);}
143 
144 /**
145  * macro for testing whether a list node's magic number is equal to a
146  * specific magic number
147  */
148 #define BU_LIST_MAGIC_EQUAL(_l, _magic) ((_l)->magic == (_magic))
149 
150 /**
151  * there is no reliable way to assert the integrity of an arbitrary
152  * bu_list struct since the magic can be anything, therefore there is
153  * no BU_CK_LIST(). we can, however, check for a valid head node.
154  */
155 #define BU_CK_LIST_HEAD(_p) BU_CKMAG((_p), BU_LIST_HEAD_MAGIC, "bu_list")
156 
157 /**
158  * initializes a bu_list struct as a circular list without allocating
159  * any memory. call BU_LIST_MAGIC_SET() to change the list type.
160  */
161 #define BU_LIST_INIT(_headp) { \
162  BU_ASSERT((void *)(_headp) != (void *)NULL); \
163  (_headp)->forw = (_headp)->back = (_headp); \
164  (_headp)->magic = BU_LIST_HEAD_MAGIC; /* used by circ. macros */ }
165 
166 /**
167  * initializes a bu_list struct node with a particular non-head node
168  * magic number without allocating any memory.
169  */
170 #define BU_LIST_INIT_MAGIC(_headp, _magic) { \
171  BU_LIST_INIT((_headp)); \
172  BU_LIST_MAGIC_SET((_headp), (_magic)); \
173  }
174 
175 /**
176  * macro suitable for declaration statement zero-initialization of a
177  * bu_list struct, but not suitably for validation with
178  * BU_CK_LIST_HEAD() as the list pointers are NULL. does not allocate
179  * memory.
180  */
181 #define BU_LIST_INIT_ZERO { 0, BU_LIST_NULL, BU_LIST_NULL }
182 
183 /**
184  * returns truthfully whether a bu_list has been initialized via
185  * BU_LIST_INIT(). lists initialized with BU_LIST_INIT_ZERO or
186  * zero-allocated will not return true as their forward/backward
187  * pointers reference nothing.
188  */
189 #define BU_LIST_IS_INITIALIZED(_headp) (((struct bu_list *)(_headp) != BU_LIST_NULL) && LIKELY((_headp)->forw != BU_LIST_NULL))
190 
191 
192 /**
193  * Insert "new" item in front of "old" item. Often, "old" is the
194  * head. To put the new item at the tail of the list, insert before
195  * the head, e.g. * BU_LIST_INSERT(&(head.l), &((p)->l));
196  */
197 #define BU_LIST_INSERT(old, new) { \
198  BU_ASSERT((void *)(old) != (void *)NULL); \
199  BU_ASSERT((void *)(new) != (void *)NULL); \
200  (new)->back = (old)->back; \
201  (old)->back = (new); \
202  (new)->forw = (old); \
203  BU_ASSERT((void *)((new)->back) != (void *)NULL); \
204  (new)->back->forw = (new); }
205 
206 /**
207  * Append "new" item after "old" item. Often, "old" is the head. To
208  * put the new item at the head of the list, append after the head,
209  * e.g. * BU_LIST_APPEND(&(head.l), &((p)->l));
210  */
211 #define BU_LIST_APPEND(old, new) { \
212  BU_ASSERT((void *)(old) != (void *)NULL); \
213  BU_ASSERT((void *)(new) != (void *)NULL); \
214  (new)->forw = (old)->forw; \
215  (new)->back = (old); \
216  (old)->forw = (new); \
217  BU_ASSERT((void *)((new)->forw) != (void *)NULL); \
218  (new)->forw->back = (new); }
219 
220 /**
221  * Dequeue "cur" item from anywhere in doubly-linked list
222  */
223 #define BU_LIST_DEQUEUE(cur) { \
224  BU_ASSERT((void *)(cur) != (void *)NULL); \
225  if (LIKELY((cur)->forw != NULL)) (cur)->forw->back = (cur)->back; \
226  if (LIKELY((cur)->back != NULL)) (cur)->back->forw = (cur)->forw; \
227  (cur)->forw = (cur)->back = BU_LIST_NULL; /* sanity */ }
228 
229 /**
230  * Dequeue "cur" but do not fix its links
231  */
232 #define BU_LIST_DQ(cur) {\
233  BU_ASSERT((void *)(cur) != (void *)NULL); \
234  if (LIKELY((cur)->forw != NULL)) (cur)->forw->back = (cur)->back; \
235  if (LIKELY((cur)->back != NULL)) (cur)->back->forw = (cur)->forw; }
236 
237 #define BU_LIST_DQ_T(cur, type) (\
238  (cur)->forw->back = (cur)->back, \
239  (cur)->back->forw = (cur)->forw, \
240  (type *)(cur))
241 
242 /**
243  * This version of BU_LIST_DEQUEUE uses the comma operator in order to
244  * return a typecast version of the dequeued pointer
245  */
246 #define BU_LIST_DEQUEUE_T(cur, type) (\
247  (cur)->forw->back = (cur)->back, \
248  (cur)->back->forw = (cur)->forw, \
249  (cur)->forw = (cur)->back = BU_LIST_NULL, \
250  (type *)(cur))
251 
252 
253 /**
254  * The Stack Discipline
255  *
256  * BU_LIST_PUSH places p at the tail of headp. BU_LIST_POP sets p to
257  * last element in headp's list (else NULL) and, if p is non-null,
258  * dequeues it.
259  */
260 #define BU_LIST_PUSH(headp, p) \
261  BU_LIST_APPEND(headp, (struct bu_list *)(p))
262 
263 #define BU_LIST_POP(structure, headp, p) \
264  { \
265  if (BU_LIST_NON_EMPTY(headp)) { \
266  (p) = ((struct structure *)((headp)->forw)); \
267  BU_LIST_DEQUEUE((struct bu_list *)(p)); \
268  } else { \
269  (p) = (struct structure *) 0; \
270  } \
271  }
272 
273 #define BU_LIST_POP_T(headp, type) \
274  (type *)bu_list_pop(headp)
275 
276 /**
277  * "Bulk transfer" all elements from the list headed by src_headp onto
278  * the list headed by dest_headp, without examining every element in the
279  * list. src_headp is left with a valid but empty list.
280  *
281  * BU_LIST_INSERT_LIST places src_headp elements at head of dest_headp list,
282  * BU_LIST_APPEND_LIST places src_headp elements at end of dest_headp list.
283  */
284 #define BU_LIST_INSERT_LIST(dest_headp, src_headp) \
285  if (LIKELY(BU_LIST_NON_EMPTY(src_headp))) { \
286  struct bu_list *_first = (src_headp)->forw; \
287  struct bu_list *_last = (src_headp)->back; \
288  (dest_headp)->forw->back = _last; \
289  _last->forw = (dest_headp)->forw; \
290  (dest_headp)->forw = _first; \
291  _first->back = (dest_headp); \
292  (src_headp)->forw = (src_headp)->back = (src_headp); \
293  }
294 
295 #define BU_LIST_APPEND_LIST(dest_headp, src_headp) \
296  if (LIKELY(BU_LIST_NON_EMPTY(src_headp))) {\
297  struct bu_list *_first = (src_headp)->forw; \
298  struct bu_list *_last = (src_headp)->back; \
299  _first->back = (dest_headp)->back; \
300  (dest_headp)->back->forw = _first; \
301  (dest_headp)->back = _last; \
302  _last->forw = (dest_headp); \
303  (src_headp)->forw = (src_headp)->back = (src_headp); \
304  }
305 
306 /**
307  * Test if a doubly linked list is empty, given head pointer
308  */
309 #define BU_LIST_IS_EMPTY(headp) ((headp)->forw == (headp))
310 #define BU_LIST_NON_EMPTY(headp) ((headp)->forw != (headp))
311 #define BU_LIST_NON_EMPTY_P(p, structure, headp) \
312  (((p)=(struct structure *)((headp)->forw)) != (struct structure *)(headp))
313 #define BU_LIST_IS_CLEAR(headp) ((headp)->magic == 0 && \
314  (headp)->forw == BU_LIST_NULL && \
315  (headp)->back == BU_LIST_NULL)
316 
317 /* Return re-cast pointer to first element on list.
318  * No checking is performed to see if list is empty.
319  */
320 #define BU_LIST_LAST(structure, headp) \
321  ((struct structure *)((headp)->back))
322 #define BU_LIST_BACK(structure, headp) \
323  ((struct structure *)((headp)->back))
324 #define BU_LIST_PREV(structure, headp) \
325  ((struct structure *)((headp)->back))
326 #define BU_LIST_FIRST(structure, headp) \
327  ((struct structure *)((headp)->forw))
328 #define BU_LIST_FORW(structure, headp) \
329  ((struct structure *)((headp)->forw))
330 #define BU_LIST_NEXT(structure, headp) \
331  ((struct structure *)((headp)->forw))
332 
333 /**
334  * Boolean test to see if current list element is the head
335  */
336 #define BU_LIST_IS_HEAD(p, headp) \
337  (((struct bu_list *)(p)) == (struct bu_list *)(headp))
338 #define BU_LIST_NOT_HEAD(p, headp) \
339  (!BU_LIST_IS_HEAD(p, headp))
340 
341 /**
342  * Boolean test to see if previous list element is the head
343  */
344 #define BU_LIST_PREV_IS_HEAD(p, headp)\
345  (((struct bu_list *)(p))->back == (struct bu_list *)(headp))
346 #define BU_LIST_PREV_NOT_HEAD(p, headp)\
347  (!BU_LIST_PREV_IS_HEAD(p, headp))
348 
349 /**
350  * Boolean test to see if the next list element is the head
351  */
352 #define BU_LIST_NEXT_IS_HEAD(p, headp) \
353  (((struct bu_list *)(p))->forw == (struct bu_list *)(headp))
354 #define BU_LIST_NEXT_NOT_HEAD(p, headp) \
355  (!BU_LIST_NEXT_IS_HEAD(p, headp))
356 
357 #define BU_LIST_EACH(headp, p, type) \
358  for ((p)=(type *)BU_LIST_FIRST(bu_list, headp); \
359  (p) && BU_LIST_NOT_HEAD(p, headp); \
360  (p)=(type *)BU_LIST_PNEXT(bu_list, p)) \
361 
362 #define BU_LIST_REVEACH(headp, p, type) \
363  for ((p)=(type *)BU_LIST_LAST(bu_list, headp); \
364  (p) && BU_LIST_NOT_HEAD(p, headp); \
365  (p)=(type *)BU_LIST_PREV(bu_list, ((struct bu_list *)(p)))) \
366 
367 #define BU_LIST_TAIL(headp, start, p, type) \
368  for ((p)=(type *)start; \
369  (p) && BU_LIST_NOT_HEAD(p, headp); \
370  (p)=(type *)BU_LIST_PNEXT(bu_list, (p)))
371 
372 /**
373  * Intended as innards for a for loop to visit all nodes on list, e.g.:
374  *
375  * for (BU_LIST_FOR(p, structure, headp)) {
376  * work_on(p);
377  * }
378  */
379 #define BU_LIST_FOR(p, structure, headp) \
380  (p)=BU_LIST_FIRST(structure, headp); \
381  (p) && BU_LIST_NOT_HEAD(p, headp); \
382  (p)=BU_LIST_PNEXT(structure, p)
383 
384 #define BU_LIST_FOR_BACKWARDS(p, structure, headp) \
385  (p)=BU_LIST_LAST(structure, headp); \
386  (p) && BU_LIST_NOT_HEAD(p, headp); \
387  (p)=BU_LIST_PLAST(structure, p)
388 
389 /**
390  * Process all the list members except headp and the actual head. Useful
391  * when starting somewhere besides the head.
392  */
393 #define BU_LIST_FOR_CIRC(p, structure, headp) \
394  (p)=BU_LIST_PNEXT_CIRC(structure, headp); \
395  (p) && BU_LIST_NOT_HEAD(p, headp); \
396  (p)=BU_LIST_PNEXT_CIRC(structure, p)
397 
398 /**
399  * Intended as innards for a for loop to visit elements of two lists
400  * in tandem, e.g.:
401  *
402  * for (BU_LIST_FOR2(p1, p2, structure, headp1, headp2)) {
403  * process(p1, p2);
404  * }
405  */
406 #define BU_LIST_FOR2(p1, p2, structure, headp1, headp2) \
407  (p1)=BU_LIST_FIRST(structure, headp1), \
408  (p2)=BU_LIST_FIRST(structure, headp2); \
409  (p1) && BU_LIST_NOT_HEAD((struct bu_list *)(p1), (headp1)) && \
410  (p2) && BU_LIST_NOT_HEAD((struct bu_list *)(p2), (headp2)); \
411  (p1)=BU_LIST_NEXT(structure, (struct bu_list *)(p1)), \
412  (p2)=BU_LIST_NEXT(structure, (struct bu_list *)(p2))
413 
414 /**
415  * Innards for a while loop that constantly picks off the first
416  * element. Useful mostly for a loop that will dequeue every list
417  * element, e.g.:
418  *
419  * while (BU_LIST_WHILE(p, structure, headp)) {
420  *@n BU_LIST_DEQUEUE(&(p->l));
421  *@n free((char *)p);
422  *@n }
423  */
424 #define BU_LIST_WHILE(p, structure, headp) \
425  (((p)=(struct structure *)((headp)->forw)) != (struct structure *)(headp))
426 
427 /**
428  * Return the magic number of the first (or last) item on a list
429  */
430 #define BU_LIST_FIRST_MAGIC(headp) ((headp)->forw->magic)
431 #define BU_LIST_LAST_MAGIC(headp) ((headp)->back->magic)
432 
433 /**
434  * Return pointer to next (or previous) element, which may be the head
435  */
436 #define BU_LIST_PNEXT(structure, p) \
437  ((struct structure *)(((struct bu_list *)(p))->forw))
438 #define BU_LIST_PLAST(structure, p) \
439  ((struct structure *)(((struct bu_list *)(p))->back))
440 
441 /**
442  * Return pointer two links away, which may include the head
443  */
444 #define BU_LIST_PNEXT_PNEXT(structure, p) \
445  ((struct structure *)(((struct bu_list *)(p))->forw->forw))
446 #define BU_LIST_PNEXT_PLAST(structure, p) \
447  ((struct structure *)(((struct bu_list *)(p))->forw->back))
448 #define BU_LIST_PLAST_PNEXT(structure, p) \
449  ((struct structure *)(((struct bu_list *)(p))->back->forw))
450 #define BU_LIST_PLAST_PLAST(structure, p) \
451  ((struct structure *)(((struct bu_list *)(p))->back->back))
452 
453 /**
454  * Return pointer to circular next element; i.e., ignoring the list head
455  */
456 #define BU_LIST_PNEXT_CIRC(structure, p) \
457  ((BU_LIST_FIRST_MAGIC((struct bu_list *)(p)) == BU_LIST_HEAD_MAGIC) ? \
458  BU_LIST_PNEXT_PNEXT(structure, (struct bu_list *)(p)) : \
459  BU_LIST_PNEXT(structure, p))
460 
461 /**
462  * Return pointer to circular last element; i.e., ignoring the list head
463  */
464 #define BU_LIST_PPREV_CIRC(structure, p) \
465  ((BU_LIST_LAST_MAGIC((struct bu_list *)(p)) == BU_LIST_HEAD_MAGIC) ? \
466  BU_LIST_PLAST_PLAST(structure, (struct bu_list *)(p)) : \
467  BU_LIST_PLAST(structure, p))
468 
469 /**
470  * Support for membership on multiple linked lists.
471  *
472  * When a structure of type '_type' contains more than one bu_list
473  * structure within it (such as the NMG edgeuse), this macro can be
474  * used to convert a pointer '_ptr2' to a "midway" bu_list structure
475  * (an element called '_name2' in structure '_type') back into a
476  * pointer to the overall enclosing structure. Examples:
477  *
478  * eu = BU_LIST_MAIN_PTR(edgeuse, midway, l2);
479  *
480  * eu1 = BU_LIST_MAIN_PTR(edgeuse, BU_LIST_FIRST(bu_list, &eg1->eu_hd2), l2);
481  *
482  * Files using BU_LIST_MAIN_PTR will need to include stddef.h
483  */
484 #define BU_LIST_MAIN_PTR(_type, _ptr2, _name2) \
485  ((struct _type *)(((char *)(_ptr2)) - (bu_offsetof(struct _type, _name2) + bu_offsetof(struct bu_list, magic))))
486 
487 /**
488  * Creates and initializes a bu_list head structure
489  */
490 BU_EXPORT extern struct bu_list *bu_list_new(void);
491 
492 /**
493  * Returns the results of BU_LIST_POP
494  */
495 BU_EXPORT extern struct bu_list *bu_list_pop(struct bu_list *headp);
496 
497 /**
498  * Returns the number of elements on a bu_list brand linked list.
499  */
500 BU_EXPORT extern int bu_list_len(const struct bu_list *headp);
501 
502 /**
503  * Reverses the order of elements in a bu_list linked list.
504  */
505 BU_EXPORT extern void bu_list_reverse(struct bu_list *headp);
506 
507 /**
508  * Given a list of structures allocated with bu_malloc() or
509  * bu_calloc() enrolled on a bu_list head, walk the list and free the
510  * structures. This routine can only be used when the structures have
511  * no interior pointers.
512  */
513 BU_EXPORT extern void bu_list_free(struct bu_list *headp);
514 
515 /**
516  * Simple parallel-safe routine for appending a data structure to the
517  * end of a bu_list doubly-linked list.
518  *
519  * @par Issues:
520  * Only one semaphore shared by all list heads.
521  * @n No portable way to notify waiting thread(s) that are sleeping
522  */
523 BU_EXPORT extern void bu_list_parallel_append(struct bu_list *headp,
524  struct bu_list *itemp);
525 
526 /**
527  * Simple parallel-safe routine for dequeueing one data structure from
528  * the head of a bu_list doubly-linked list.
529  * If the list is empty, wait until some other thread puts something on
530  * the list.
531  *
532  * @par Issues:
533  * No portable way to not spin and burn CPU time while waiting
534  * @n for something to show up on the list.
535  */
536 BU_EXPORT extern struct bu_list *bu_list_parallel_dequeue(struct bu_list *headp);
537 
538 /**
539  * Generic bu_list doubly-linked list checker.
540  */
541 BU_EXPORT extern void bu_ck_list(const struct bu_list *headp,
542  const char *str);
543 
544 /**
545  * bu_list doubly-linked list checker which checks the magic number for
546  * all elements in the linked list
547  */
548 BU_EXPORT extern void bu_ck_list_magic(const struct bu_list *headp,
549  const char *str,
550  const uint32_t magic);
551 
552 
553 /** @} */
554 
555 __END_DECLS
556 
557 #endif /* BU_LIST_H */
558 
559 /*
560  * Local Variables:
561  * mode: C
562  * tab-width: 8
563  * indent-tabs-mode: t
564  * c-file-style: "stroustrup"
565  * End:
566  * ex: shiftwidth=4 tabstop=8
567  */
Header file for the BRL-CAD common definitions.
void bu_ck_list(const struct bu_list *headp, const char *str)
void bu_ck_list_magic(const struct bu_list *headp, const char *str, const uint32_t magic)
void bu_list_reverse(struct bu_list *headp)
struct bu_list * bu_list_parallel_dequeue(struct bu_list *headp)
int bu_list_len(const struct bu_list *headp)
struct bu_list * bu_list_pop(struct bu_list *headp)
void bu_list_free(struct bu_list *headp)
void bu_list_parallel_append(struct bu_list *headp, struct bu_list *itemp)
struct bu_list * bu_list_new(void)
Global registry of recognized magic numbers.
Definition: list.h:132
uint32_t magic
Magic # for mem id/check.
Definition: list.h:133
struct bu_list * forw
"forward", "next"
Definition: list.h:134
struct bu_list * back
"back", "last"
Definition: list.h:135