xref: /netbsd-src/external/bsd/ntp/dist/include/ntp_lists.h (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
1 /*	$NetBSD: ntp_lists.h,v 1.8 2024/10/01 20:59:51 christos Exp $	*/
2 
3 /*
4  * ntp_lists.h - linked lists common code
5  *
6  * SLIST: singly-linked lists
7  * ==========================
8  *
9  * These macros implement a simple singly-linked list template.  Both
10  * the listhead and per-entry next fields are declared as pointers to
11  * the list entry struct type.  Initialization to NULL is typically
12  * implicit (for globals and statics) or handled by zeroing of the
13  * containing structure.
14  *
15  * The name of the next link field is passed as an argument to allow
16  * membership in several lists at once using multiple next link fields.
17  *
18  * When possible, placing the link field first in the entry structure
19  * allows slightly smaller code to be generated on some platforms.
20  *
21  * LINK_SLIST(listhead, pentry, nextlink)
22  *	add entry at head
23  *
24  * LINK_TAIL_SLIST(listhead, pentry, nextlink, entrytype)
25  *	add entry at tail.  This is O(n), if this is a common
26  *	operation the FIFO template may be more appropriate.
27  *
28  * LINK_SORT_SLIST(listhead, pentry, beforecur, nextlink, entrytype)
29  *	add entry in sorted order.  beforecur is an expression comparing
30  *	pentry with the current list entry.  The current entry can be
31  *	referenced within beforecur as L_S_S_CUR(), which is short for
32  *	LINK_SORT_SLIST_CUR().  beforecur is nonzero if pentry sorts
33  *	before L_S_S_CUR().
34  *
35  * UNLINK_HEAD_SLIST(punlinked, listhead, nextlink)
36  *	unlink first entry and point punlinked to it, or set punlinked
37  *	to NULL if the list is empty.
38  *
39  * UNLINK_SLIST(punlinked, listhead, ptounlink, nextlink, entrytype)
40  *	unlink entry pointed to by ptounlink.  punlinked is set to NULL
41  *	if the entry is not found on the list, otherwise it is set to
42  *	ptounlink.
43  *
44  * UNLINK_EXPR_SLIST(punlinked, listhead, expr, nextlink, entrytype)
45  *	unlink entry where expression expr is nonzero.  expr can refer
46  *	to the entry being tested using UNLINK_EXPR_SLIST_CURRENT(),
47  *	alias U_E_S_CUR().  See the implementation of UNLINK_SLIST()
48  *	below for an example. U_E_S_CUR() is NULL iff the list is empty.
49  *	punlinked is pointed to the removed entry or NULL if none
50  *	satisfy expr.
51  *
52  * FIFO: singly-linked lists plus tail pointer
53  * ===========================================
54  *
55  * This is the same as FreeBSD's sys/queue.h STAILQ -- a singly-linked
56  * list implementation with tail-pointer maintenance, so that adding
57  * at the tail for first-in, first-out access is O(1).
58  *
59  * DECL_FIFO_ANCHOR(entrytype)
60  *	provides the type specification portion of the declaration for
61  *	a variable to refer to a FIFO queue (similar to listhead).  The
62  *	anchor contains the head and indirect tail pointers.  Example:
63  *
64  *		#include "ntp_lists.h"
65  *
66  *		typedef struct myentry_tag myentry;
67  *		struct myentry_tag {
68  *			myentry *next_link;
69  *			...
70  *		};
71  *
72  *		DECL_FIFO_ANCHOR(myentry) my_fifo;
73  *
74  *		void somefunc(myentry *pentry)
75  *		{
76  *			LINK_FIFO(my_fifo, pentry, next_link);
77  *		}
78  *
79  *	If DECL_FIFO_ANCHOR is used with stack or heap storage, it
80  *	should be initialized to NULL pointers using a = { NULL };
81  *	initializer or memset.
82  *
83  * HEAD_FIFO(anchor)
84  * TAIL_FIFO(anchor)
85  *	Pointer to first/last entry, NULL if FIFO is empty.
86  *
87  * LINK_FIFO(anchor, pentry, nextlink)
88  *	add entry at tail.
89  *
90  * UNLINK_FIFO(punlinked, anchor, nextlink)
91  *	unlink head entry and point punlinked to it, or set punlinked
92  *	to NULL if the list is empty.
93  *
94  * CONCAT_FIFO(q1, q2, nextlink)
95  *	empty fifoq q2 moving its nodes to q1 following q1's existing
96  *	nodes.
97  *
98  * DLIST: doubly-linked lists
99  * ==========================
100  *
101  * Elements on DLISTs always have non-NULL forward and back links,
102  * because both link chains are circular.  The beginning/end is marked
103  * by the listhead, which is the same type as elements for simplicity.
104  * An empty list's listhead has both links set to its own address.
105  *
106  *
107  */
108 #ifndef NTP_LISTS_H
109 #define NTP_LISTS_H
110 
111 #include "ntp_types.h"		/* TRUE and FALSE */
112 #include "ntp_assert.h"
113 
114 #ifdef DEBUG
115 # define NTP_DEBUG_LISTS_H
116 #endif
117 
118 /*
119  * If list debugging is not enabled, save a little time by not clearing
120  * an entry's link pointer when it is unlinked, as the stale pointer
121  * is harmless as long as it is ignored when the entry is not in a
122  * list.
123  */
124 #ifndef NTP_DEBUG_LISTS_H
125 #define MAYBE_Z_LISTS(p)	do { } while (FALSE)
126 #else
127 #define MAYBE_Z_LISTS(p)	(p) = NULL
128 #endif
129 
130 #define LINK_SLIST(listhead, pentry, nextlink)			\
131 do {								\
132 	(pentry)->nextlink = (listhead);			\
133 	(listhead) = (pentry);					\
134 } while (FALSE)
135 
136 #define LINK_TAIL_SLIST(listhead, pentry, nextlink, entrytype)	\
137 do {								\
138 	entrytype **pptail;					\
139 								\
140 	pptail = &(listhead);					\
141 	while (*pptail != NULL)					\
142 		pptail = &((*pptail)->nextlink);		\
143 								\
144 	(pentry)->nextlink = NULL;				\
145 	*pptail = (pentry);					\
146 } while (FALSE)
147 
148 #define LINK_SORT_SLIST_CURRENT()	(*ppentry)
149 #define	L_S_S_CUR()			LINK_SORT_SLIST_CURRENT()
150 
151 #define LINK_SORT_SLIST(listhead, pentry, beforecur, nextlink,	\
152 			entrytype)				\
153 do {								\
154 	entrytype **ppentry;					\
155 								\
156 	ppentry = &(listhead);					\
157 	while (TRUE) {						\
158 		if (beforecur) {				\
159 			(pentry)->nextlink = *ppentry;		\
160 			*ppentry = (pentry);			\
161 			break;					\
162 		}						\
163 		ppentry = &((*ppentry)->nextlink);		\
164 		if (NULL == *ppentry) {				\
165 			(pentry)->nextlink = NULL;		\
166 			*ppentry = (pentry);			\
167 			break;					\
168 		}						\
169 	}							\
170 } while (FALSE)
171 
172 #define UNLINK_HEAD_SLIST(punlinked, listhead, nextlink)	\
173 do {								\
174 	(punlinked) = (listhead);				\
175 	if (NULL != (punlinked)) {				\
176 		(listhead) = (punlinked)->nextlink;		\
177 		MAYBE_Z_LISTS((punlinked)->nextlink);		\
178 	}							\
179 } while (FALSE)
180 
181 #define UNLINK_EXPR_SLIST_CURRENT()	(*ppentry)
182 #define	U_E_S_CUR()			UNLINK_EXPR_SLIST_CURRENT()
183 
184 #define UNLINK_EXPR_SLIST(punlinked, listhead, expr, nextlink,	\
185 			  entrytype)				\
186 if (NULL != (listhead)) {					\
187 	entrytype **ppentry;					\
188 								\
189 	ppentry = &(listhead);					\
190 								\
191 	while (!(expr))						\
192 		if (*ppentry != NULL &&				\
193 		    (*ppentry)->nextlink != NULL) {		\
194 			ppentry = &((*ppentry)->nextlink);	\
195 		} else {					\
196 			ppentry = NULL;				\
197 			break;					\
198 		}						\
199 								\
200 	if (ppentry != NULL) {					\
201 		(punlinked) = *ppentry;				\
202 		*ppentry = (punlinked)->nextlink;		\
203 		MAYBE_Z_LISTS((punlinked)->nextlink);		\
204 	} else {						\
205 		(punlinked) = NULL;				\
206 	}							\
207 } else do {							\
208 	(punlinked) = NULL;					\
209 } while (FALSE)
210 
211 #define UNLINK_SLIST(punlinked, listhead, ptounlink, nextlink,	\
212 		     entrytype)					\
213 	UNLINK_EXPR_SLIST(punlinked, listhead, (ptounlink) ==	\
214 	    U_E_S_CUR(), nextlink, entrytype)
215 
216 #define CHECK_SLIST(listhead, nextlink, entrytype)		\
217 do {								\
218 	entrytype *pentry;					\
219 								\
220 	for (pentry = (listhead);				\
221 	     pentry != NULL;					\
222 	     pentry = pentry->nextlink) {			\
223 		INSIST(pentry != pentry->nextlink);		\
224 		INSIST((listhead) != pentry->nextlink);		\
225 	}							\
226 } while (FALSE)
227 
228 /*
229  * FIFO
230  */
231 
232 #define DECL_FIFO_ANCHOR(entrytype)				\
233 struct {							\
234 	entrytype *	phead;	/* NULL if list empty */	\
235 	entrytype **	pptail;	/* NULL if list empty */	\
236 }
237 
238 #define HEAD_FIFO(anchor)	((anchor).phead)
239 #define TAIL_FIFO(anchor)	((NULL == (anchor).pptail)	\
240 					? NULL			\
241 					: *((anchor).pptail))
242 
243 /*
244  * For DEBUG builds only, verify both or neither of the anchor pointers
245  * are NULL with each operation.
246  */
247 #if !defined(NTP_DEBUG_LISTS_H)
248 #define	CHECK_FIFO_CONSISTENCY(anchor)	do { } while (FALSE)
249 #else
250 #define	CHECK_FIFO_CONSISTENCY(anchor)				\
251 	check_gen_fifo_consistency(&(anchor))
252 void	check_gen_fifo_consistency(void *fifo);
253 #endif
254 
255 /*
256  * generic FIFO element used to access any FIFO where each element
257  * begins with the link pointer
258  */
259 typedef struct gen_node_tag gen_node;
260 struct gen_node_tag {
261 	gen_node *	link;
262 };
263 
264 /* generic FIFO */
265 typedef DECL_FIFO_ANCHOR(gen_node) gen_fifo;
266 
267 
268 #define LINK_FIFO(anchor, pentry, nextlink)			\
269 do {								\
270 	CHECK_FIFO_CONSISTENCY(anchor);				\
271 								\
272 	(pentry)->nextlink = NULL;				\
273 	if (NULL != (anchor).pptail) {				\
274 		(*((anchor).pptail))->nextlink = (pentry);	\
275 		(anchor).pptail =				\
276 		    &(*((anchor).pptail))->nextlink;		\
277 	} else {						\
278 		(anchor).phead = (pentry);			\
279 		(anchor).pptail = &(anchor).phead;		\
280 	}							\
281 								\
282 	CHECK_FIFO_CONSISTENCY(anchor);				\
283 } while (FALSE)
284 
285 #define UNLINK_FIFO(punlinked, anchor, nextlink)		\
286 do {								\
287 	CHECK_FIFO_CONSISTENCY(anchor);				\
288 								\
289 	(punlinked) = (anchor).phead;				\
290 	if (NULL != (punlinked)) {				\
291 		(anchor).phead = (punlinked)->nextlink;		\
292 		if (NULL == (anchor).phead)			\
293 			(anchor).pptail = NULL;			\
294 		else if ((anchor).pptail ==			\
295 			 &(punlinked)->nextlink)		\
296 			(anchor).pptail = &(anchor).phead;	\
297 		MAYBE_Z_LISTS((punlinked)->nextlink);		\
298 		CHECK_FIFO_CONSISTENCY(anchor);			\
299 	}							\
300 } while (FALSE)
301 
302 #define UNLINK_MID_FIFO(punlinked, anchor, tounlink, nextlink,	\
303 			entrytype)				\
304 do {								\
305 	entrytype **ppentry;					\
306 								\
307 	CHECK_FIFO_CONSISTENCY(anchor);				\
308 								\
309 	ppentry = &(anchor).phead;				\
310 								\
311 	while ((tounlink) != *ppentry)				\
312 		if ((*ppentry)->nextlink != NULL) {		\
313 			ppentry = &((*ppentry)->nextlink);	\
314 		} else {					\
315 			ppentry = NULL;				\
316 			break;					\
317 		}						\
318 								\
319 	if (ppentry != NULL) {					\
320 		(punlinked) = *ppentry;				\
321 		*ppentry = (punlinked)->nextlink;		\
322 		if (NULL == *ppentry)				\
323 			(anchor).pptail = NULL;			\
324 		else if ((anchor).pptail ==			\
325 			 &(punlinked)->nextlink)		\
326 			(anchor).pptail = &(anchor).phead;	\
327 		MAYBE_Z_LISTS((punlinked)->nextlink);		\
328 		CHECK_FIFO_CONSISTENCY(anchor);			\
329 	} else {						\
330 		(punlinked) = NULL;				\
331 	}							\
332 } while (FALSE)
333 
334 #define CONCAT_FIFO(f1, f2, nextlink)				\
335 do {								\
336 	CHECK_FIFO_CONSISTENCY(f1);				\
337 	CHECK_FIFO_CONSISTENCY(f2);				\
338 								\
339 	if ((f2).pptail != NULL) {				\
340 		if ((f1).pptail != NULL) {			\
341 			(*(f1).pptail)->nextlink = (f2).phead;	\
342 			if ((f2).pptail == &(f2).phead)		\
343 				(f1).pptail =			\
344 				    &(*(f1).pptail)->nextlink;	\
345 			else					\
346 				(f1).pptail = (f2).pptail;	\
347 			CHECK_FIFO_CONSISTENCY(f1);		\
348 		} else	{					\
349 			(f1) = (f2);				\
350 		}						\
351 		MAYBE_Z_LISTS((f2).phead);			\
352 		MAYBE_Z_LISTS((f2).pptail);			\
353 	}							\
354 } while (FALSE)
355 
356 /*
357  * DLIST
358  */
359 #define DECL_DLIST_LINK(entrytype, link)			\
360 struct {							\
361 	entrytype *	b;					\
362 	entrytype *	f;					\
363 } link
364 
365 #define INIT_DLIST(listhead, link)				\
366 do {								\
367 	(listhead).link.f = &(listhead);			\
368 	(listhead).link.b = &(listhead);			\
369 } while (FALSE)
370 
371 #define HEAD_DLIST(listhead, link)				\
372 	(							\
373 		(&(listhead) != (listhead).link.f)		\
374 		    ? (listhead).link.f				\
375 		    : NULL					\
376 	)
377 
378 #define TAIL_DLIST(listhead, link)				\
379 	(							\
380 		(&(listhead) != (listhead).link.b)		\
381 		    ? (listhead).link.b				\
382 		    : NULL					\
383 	)
384 
385 #define NEXT_DLIST(listhead, entry, link)			\
386 	(							\
387 		(&(listhead) != (entry)->link.f)		\
388 		    ? (entry)->link.f				\
389 		    : NULL					\
390 	)
391 
392 #define PREV_DLIST(listhead, entry, link)			\
393 	(							\
394 		(&(listhead) != (entry)->link.b)		\
395 		    ? (entry)->link.b				\
396 		    : NULL					\
397 	)
398 
399 #define LINK_DLIST(listhead, pentry, link)			\
400 do {								\
401 	(pentry)->link.f = (listhead).link.f;			\
402 	(pentry)->link.b = &(listhead);				\
403 	(listhead).link.f->link.b = (pentry);			\
404 	(listhead).link.f = (pentry);				\
405 } while (FALSE)
406 
407 #define LINK_TAIL_DLIST(listhead, pentry, link)			\
408 do {								\
409 	(pentry)->link.b = (listhead).link.b;			\
410 	(pentry)->link.f = &(listhead);				\
411 	(listhead).link.b->link.f = (pentry);			\
412 	(listhead).link.b = (pentry);				\
413 } while (FALSE)
414 
415 #define UNLINK_DLIST(ptounlink, link)				\
416 do {								\
417 	(ptounlink)->link.b->link.f = (ptounlink)->link.f;	\
418 	(ptounlink)->link.f->link.b = (ptounlink)->link.b;	\
419 	MAYBE_Z_LISTS((ptounlink)->link.b);			\
420 	MAYBE_Z_LISTS((ptounlink)->link.f);			\
421 } while (FALSE)
422 
423 #define ITER_DLIST_BEGIN(listhead, iter, link, entrytype)	\
424 {								\
425 	entrytype *i_dl_nextiter;				\
426 								\
427 	for ((iter) = (listhead).link.f;			\
428 	     (iter) != &(listhead)				\
429 	     && ((i_dl_nextiter = (iter)->link.f), TRUE);	\
430 	     (iter) = i_dl_nextiter) {
431 #define ITER_DLIST_END()					\
432 	}							\
433 }
434 
435 #define REV_ITER_DLIST_BEGIN(listhead, iter, link, entrytype)	\
436 {								\
437 	entrytype *i_dl_nextiter;				\
438 								\
439 	for ((iter) = (listhead).link.b;			\
440 	     (iter) != &(listhead)				\
441 	     && ((i_dl_nextiter = (iter)->link.b), TRUE);	\
442 	     (iter) = i_dl_nextiter) {
443 #define REV_ITER_DLIST_END()					\
444 	}							\
445 }
446 
447 #endif	/* NTP_LISTS_H */
448