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