Home | History | Annotate | Line # | Download | only in isc
      1 /*	$NetBSD: list.h,v 1.10 2025/01/26 16:25:41 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 #pragma once
     17 
     18 #include <isc/assertions.h>
     19 
     20 #define ISC_LINK_TOMBSTONE(type) ((type *)-1)
     21 
     22 #define ISC_LIST_INITIALIZER  \
     23 	{                     \
     24 		.head = NULL, \
     25 		.tail = NULL, \
     26 	}
     27 #define ISC_LINK_INITIALIZER_TYPE(type)           \
     28 	{                                         \
     29 		.prev = ISC_LINK_TOMBSTONE(type), \
     30 		.next = ISC_LINK_TOMBSTONE(type), \
     31 	}
     32 #define ISC_LINK_INITIALIZER ISC_LINK_INITIALIZER_TYPE(void)
     33 
     34 #ifdef ISC_LIST_CHECKINIT
     35 #define ISC_LINK_INSIST(x) ISC_INSIST(x)
     36 #else /* ifdef ISC_LIST_CHECKINIT */
     37 #define ISC_LINK_INSIST(x)
     38 #endif /* ifdef ISC_LIST_CHECKINIT */
     39 
     40 #define ISC_LIST(type)             \
     41 	struct {                   \
     42 		type *head, *tail; \
     43 	}
     44 #define ISC_LIST_INIT(list)         \
     45 	do {                        \
     46 		(list).head = NULL; \
     47 		(list).tail = NULL; \
     48 	} while (0)
     49 
     50 #define ISC_LINK(type)             \
     51 	struct {                   \
     52 		type *prev, *next; \
     53 	}
     54 #define ISC_LINK_INIT_TYPE(elt, link, type)                  \
     55 	do {                                                 \
     56 		(elt)->link.prev = ISC_LINK_TOMBSTONE(type); \
     57 		(elt)->link.next = ISC_LINK_TOMBSTONE(type); \
     58 	} while (0)
     59 #define ISC_LINK_INIT(elt, link) ISC_LINK_INIT_TYPE(elt, link, void)
     60 #define ISC_LINK_LINKED_TYPE(elt, link, type) \
     61 	((type *)((elt)->link.prev) != ISC_LINK_TOMBSTONE(type))
     62 #define ISC_LINK_LINKED(elt, link) ISC_LINK_LINKED_TYPE(elt, link, void)
     63 
     64 #define ISC_LIST_HEAD(list)  ((list).head)
     65 #define ISC_LIST_TAIL(list)  ((list).tail)
     66 #define ISC_LIST_EMPTY(list) ((list).head == NULL)
     67 
     68 #define __ISC_LIST_PREPENDUNSAFE(list, elt, link)       \
     69 	do {                                            \
     70 		if ((list).head != NULL) {              \
     71 			(list).head->link.prev = (elt); \
     72 		} else {                                \
     73 			(list).tail = (elt);            \
     74 		}                                       \
     75 		(elt)->link.prev = NULL;                \
     76 		(elt)->link.next = (list).head;         \
     77 		(list).head = (elt);                    \
     78 	} while (0)
     79 
     80 #define ISC_LIST_PREPEND(list, elt, link)                     \
     81 	do {                                                  \
     82 		ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \
     83 		__ISC_LIST_PREPENDUNSAFE(list, elt, link);    \
     84 	} while (0)
     85 
     86 #define ISC_LIST_INITANDPREPEND(list, elt, link) \
     87 	__ISC_LIST_PREPENDUNSAFE(list, elt, link)
     88 
     89 #define __ISC_LIST_APPENDUNSAFE(list, elt, link)        \
     90 	do {                                            \
     91 		if ((list).tail != NULL) {              \
     92 			(list).tail->link.next = (elt); \
     93 		} else {                                \
     94 			(list).head = (elt);            \
     95 		}                                       \
     96 		(elt)->link.prev = (list).tail;         \
     97 		(elt)->link.next = NULL;                \
     98 		(list).tail = (elt);                    \
     99 	} while (0)
    100 
    101 #define ISC_LIST_APPEND(list, elt, link)                      \
    102 	do {                                                  \
    103 		ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \
    104 		__ISC_LIST_APPENDUNSAFE(list, elt, link);     \
    105 	} while (0)
    106 
    107 #define ISC_LIST_INITANDAPPEND(list, elt, link) \
    108 	__ISC_LIST_APPENDUNSAFE(list, elt, link)
    109 
    110 #define __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type)             \
    111 	do {                                                            \
    112 		if ((elt)->link.next != NULL) {                         \
    113 			(elt)->link.next->link.prev = (elt)->link.prev; \
    114 		} else {                                                \
    115 			ISC_INSIST((list).tail == (elt));               \
    116 			(list).tail = (elt)->link.prev;                 \
    117 		}                                                       \
    118 		if ((elt)->link.prev != NULL) {                         \
    119 			(elt)->link.prev->link.next = (elt)->link.next; \
    120 		} else {                                                \
    121 			ISC_INSIST((list).head == (elt));               \
    122 			(list).head = (elt)->link.next;                 \
    123 		}                                                       \
    124 		(elt)->link.prev = ISC_LINK_TOMBSTONE(type);            \
    125 		(elt)->link.next = ISC_LINK_TOMBSTONE(type);            \
    126 		ISC_INSIST((list).head != (elt));                       \
    127 		ISC_INSIST((list).tail != (elt));                       \
    128 	} while (0)
    129 
    130 #define __ISC_LIST_UNLINKUNSAFE(list, elt, link) \
    131 	__ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, void)
    132 
    133 #define ISC_LIST_UNLINK_TYPE(list, elt, link, type)                  \
    134 	do {                                                         \
    135 		ISC_LINK_INSIST(ISC_LINK_LINKED(elt, link));         \
    136 		__ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type); \
    137 	} while (0)
    138 #define ISC_LIST_UNLINK(list, elt, link) \
    139 	ISC_LIST_UNLINK_TYPE(list, elt, link, void)
    140 
    141 #define ISC_LIST_PREV(elt, link) ((elt)->link.prev)
    142 #define ISC_LIST_NEXT(elt, link) ((elt)->link.next)
    143 
    144 #define __ISC_LIST_INSERTBEFOREUNSAFE(list, before, elt, link)  \
    145 	do {                                                    \
    146 		if ((before)->link.prev == NULL) {              \
    147 			ISC_LIST_PREPEND(list, elt, link);      \
    148 		} else {                                        \
    149 			(elt)->link.prev = (before)->link.prev; \
    150 			(before)->link.prev = (elt);            \
    151 			(elt)->link.prev->link.next = (elt);    \
    152 			(elt)->link.next = (before);            \
    153 		}                                               \
    154 	} while (0)
    155 
    156 #define ISC_LIST_INSERTBEFORE(list, before, elt, link)                  \
    157 	do {                                                            \
    158 		ISC_LINK_INSIST(ISC_LINK_LINKED(before, link));         \
    159 		ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link));           \
    160 		__ISC_LIST_INSERTBEFOREUNSAFE(list, before, elt, link); \
    161 	} while (0)
    162 
    163 #define __ISC_LIST_INSERTAFTERUNSAFE(list, after, elt, link)   \
    164 	do {                                                   \
    165 		if ((after)->link.next == NULL) {              \
    166 			ISC_LIST_APPEND(list, elt, link);      \
    167 		} else {                                       \
    168 			(elt)->link.next = (after)->link.next; \
    169 			(after)->link.next = (elt);            \
    170 			(elt)->link.next->link.prev = (elt);   \
    171 			(elt)->link.prev = (after);            \
    172 		}                                              \
    173 	} while (0)
    174 
    175 #define ISC_LIST_INSERTAFTER(list, after, elt, link)                  \
    176 	do {                                                          \
    177 		ISC_LINK_INSIST(ISC_LINK_LINKED(after, link));        \
    178 		ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link));         \
    179 		__ISC_LIST_INSERTAFTERUNSAFE(list, after, elt, link); \
    180 	} while (0)
    181 
    182 #define ISC_LIST_APPENDLIST(list1, list2, link)                 \
    183 	do {                                                    \
    184 		if (ISC_LIST_EMPTY(list1)) {                    \
    185 			(list1) = (list2);                      \
    186 		} else if (!ISC_LIST_EMPTY(list2)) {            \
    187 			(list1).tail->link.next = (list2).head; \
    188 			(list2).head->link.prev = (list1).tail; \
    189 			(list1).tail = (list2).tail;            \
    190 		}                                               \
    191 		(list2).head = NULL;                            \
    192 		(list2).tail = NULL;                            \
    193 	} while (0)
    194 
    195 #define ISC_LIST_PREPENDLIST(list1, list2, link)                \
    196 	do {                                                    \
    197 		if (ISC_LIST_EMPTY(list1)) {                    \
    198 			(list1) = (list2);                      \
    199 		} else if (!ISC_LIST_EMPTY(list2)) {            \
    200 			(list2).tail->link.next = (list1).head; \
    201 			(list1).head->link.prev = (list2).tail; \
    202 			(list1).head = (list2).head;            \
    203 		}                                               \
    204 		(list2).head = NULL;                            \
    205 		(list2).tail = NULL;                            \
    206 	} while (0)
    207 
    208 #define ISC_LIST_ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link)
    209 #define __ISC_LIST_ENQUEUEUNSAFE(list, elt, link) \
    210 	__ISC_LIST_APPENDUNSAFE(list, elt, link)
    211 #define ISC_LIST_DEQUEUE(list, elt, link) \
    212 	ISC_LIST_UNLINK_TYPE(list, elt, link, void)
    213 #define ISC_LIST_DEQUEUE_TYPE(list, elt, link, type) \
    214 	ISC_LIST_UNLINK_TYPE(list, elt, link, type)
    215 #define __ISC_LIST_DEQUEUEUNSAFE(list, elt, link) \
    216 	__ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, void)
    217 #define __ISC_LIST_DEQUEUEUNSAFE_TYPE(list, elt, link, type) \
    218 	__ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type)
    219 
    220 #define ISC_LIST_MOVEUNSAFE(dest, src)    \
    221 	{                                 \
    222 		(dest).head = (src).head; \
    223 		(dest).tail = (src).tail; \
    224 		(src).head = NULL;        \
    225 		(src).tail = NULL;        \
    226 	}
    227 
    228 #define ISC_LIST_MOVE(dest, src)                \
    229 	{                                       \
    230 		INSIST(ISC_LIST_EMPTY(dest));   \
    231 		ISC_LIST_MOVEUNSAFE(dest, src); \
    232 	}
    233 
    234 /* clang-format off */
    235 #define ISC_LIST_FOREACH(list, elt, link)	\
    236 	for (elt = ISC_LIST_HEAD(list);		\
    237 	     elt != NULL;			\
    238 	     elt = ISC_LIST_NEXT(elt, link))
    239 /* clang-format on */
    240 
    241 /* clang-format off */
    242 #define ISC_LIST_FOREACH_SAFE(list, elt, link, next)						\
    243 	for (elt = ISC_LIST_HEAD(list), next = (elt != NULL) ? ISC_LIST_NEXT(elt, link) : NULL;	\
    244 	     elt != NULL;									\
    245 	     elt = next, next = (elt != NULL) ? ISC_LIST_NEXT(elt, link) : NULL)
    246 /* clang-format on */
    247 
    248 /* clang-format off */
    249 #define ISC_LIST_FOREACH_REV(list, elt, link)	\
    250 	for (elt = ISC_LIST_TAIL(list);		\
    251 	     elt != NULL;			\
    252 	     elt = ISC_LIST_PREV(elt, link))
    253 /* clang-format on */
    254 
    255 /* clang-format off */
    256 #define ISC_LIST_FOREACH_REV_SAFE(list, elt, link, prev)						\
    257 	for (elt = ISC_LIST_TAIL(list), prev = (elt != NULL) ? ISC_LIST_PREV(elt, link) : NULL;	\
    258 	     elt != NULL;									\
    259 	     elt = prev, prev = (elt != NULL) ? ISC_LIST_PREV(elt, link) : NULL)
    260 /* clang-format on */
    261