summaryrefslogtreecommitdiff
path: root/list.h
blob: ddaf33ca1bb0e09dc5c737de07abfe1ffbb01ae7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
/*
 * Some simple implementation of lists similar to the one used in the kernel.
 *
 * Copyright (c) 2016-2020 The strace developers.
 * All rights reserved.
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 */

#ifndef STRACE_LIST_H
# define STRACE_LIST_H

/*
 * struct list_item and related macros and functions provide an interface
 * for manipulating and iterating linked lists.  In order to have list
 * associated with its payload, struct list_item has to be embedded into
 * a structure type representing payload, and (optionally) an additional
 * struct list_item should be added somewhere as a starting point for list
 * iteration (creating a list with a designated head). A situation where
 * no designated head exists, and each embedded struct list_head is considered
 * a head (i.e. starting point for list iteration), is also possible.
 *
 * List head has to be initialised with list_init() call. Statically allocated
 * list heads can also be defined with an EMPTY_LIST() macro.
 *
 * In order to get a pointer to list item from a struct list_item, list_elem
 * macro is used.
 *
 * When a designated head is used, list_head() and list_tail() can be used
 * for getting pointer to the first and the last list item, respectively.
 *
 * list_next() and list_prev() macros can be used for obtaining pointers
 * to the next and the previous items in the list, respectively.  Note that
 * they do not perform additional checks for the validity of these pointers,
 * so they have to be guarded with respective list_head/list_tail checks in case
 * of lists with designated heads (where the list's head is not embedded within
 * a list item.
 *
 * list_{insert,append,remove,remove_tail,remove_head,replace} provide some
 * basic means of list manipulation.
 *
 * list_foreach() and list_foreach_safe() are wrapper macros for simplifying
 * iteration over a list, with the latter having an additional argument
 * for storing temporary pointer, thus allowing list manipulations during
 * its iteration.
 *
 * A simple example:
 *
 *     struct my_struct {
 *             int a;
 *             struct list_item l1;
 *             struct list_item l2;
 *     };
 *
 *     EMPTY_LIST(list_1);              <--- Defining a designated head for list
 *
 *     struct my_struct *item =
 *             calloc(1, sizeof(*item));
 *     list_init(&item->l2);            <--- Initialising structure field that
 *                                           is used for lists without designated
 *                                           head.
 *     list_insert(&list_1, &item->l1); <--- Inserting an item into the list
 *
 *     item = calloc(1, sizeof(*item));
 *     list_init(&item->l2);
 *
 *     list_append(&(list_head(list_1, struct my_struct, l1)->l2), &item->l2);
 *
 *     struct my_struct *cur = item;    <--- Iteration over a headless list
 *     do {
 *             printf("%d\n", cur->a);
 *     } while ((cur = list_next(&cur, l2)) != item);
 *
 *     struct my_struct *i;
 *     list_foreach(i, list_1, l1) {    <--- Iteration over list_1 without list
 *             printf("%d\n", i->a);         modification
 *     }
 *
 *     struct my_struct *tmp;           <--- Iteration with modification
 *     list_foreach_safe(i, list_1, l1, tmp) {
 *             list_remove(&i->l1);
 *             free(i);
 *     }
 *
 * See also:
 *     "Linux kernel design patterns - part 2", section "Linked Lists"
 *     https://lwn.net/Articles/336255/
 */

# include "macros.h"

struct list_item {
	struct list_item *prev;
	struct list_item *next;
};

/**
 * Define an empty list head.
 *
 * @param l_ List head variable name.
 */
# define EMPTY_LIST(l_) struct list_item l_ = { &l_, &l_ }

/** Initialise an empty list. */
static inline void
list_init(struct list_item *l)
{
	l->prev = l;
	l->next = l;
}

/** Check whether list is empty. */
static inline bool
list_is_empty(const struct list_item *l)
{
	return ((l->next == l) && (l->prev == l))
		/*
		 * XXX This could be the case when struct list_item hasn't been
		 *     initialised at all; we should probably also call some
		 *     errror_func_msg() in that case, as it looks like sloppy
		 *     programming.
		 */
		|| (!l->next && !l->prev);
}

/**
 * Convert a pointer to a struct list_item to a pointer to a list item.
 *
 * @param var   Pointer to struct list_item.
 * @param type  Type of the list's item.
 * @param field Name of the field that holds the respective struct list_item.
 */
# define list_elem(var, type, field) containerof((var), type, field)

/**
 * Get the first element in a list.
 *
 * @param head  Pointer to the list's head.
 * @param type  Type of the list's item.
 * @param field Name of the field that holds the respective struct list_item.
 */
# define list_head(head, type, field) \
	(list_is_empty(head) ? NULL : list_elem((head)->next, type, field))
/**
 * Get the last element in a list.
 *
 * @param head  Pointer to the list's head.
 * @param type  Type of the list's item.
 * @param field Name of the field that holds the respective struct list_item.
 */
# define list_tail(head, type, field) \
	(list_is_empty(head) ? NULL : list_elem((head)->prev, type, field))

/**
 * Get the next element in a list.
 *
 * @param var   Pointer to a list item.
 * @param field Name of the field that holds the respective struct list_item.
 */
# define list_next(var, field) \
	list_elem((var)->field.next, typeof(*(var)), field)
/**
 * Get the previous element in a list.
 *
 * @param var   Pointer to a list item.
 * @param field Name of the field that holds the respective struct list_item.
 */
# define list_prev(var, field) \
	list_elem((var)->field.prev, typeof(*(var)), field)

/**
 * Insert an item into a list. The item is placed as the next list item
 * to the head.
 */
static inline void
list_insert(struct list_item *head, struct list_item *item)
{
	item->next = head->next;
	item->prev = head;
	head->next->prev = item;
	head->next = item;
}

/**
 * Insert an item into a list. The item is placed as the previous list item
 * to the head.
 */
static inline void
list_append(struct list_item *head, struct list_item *item)
{
	item->next = head;
	item->prev = head->prev;
	head->prev->next = item;
	head->prev = item;
}

/**
 * Remove an item from a list.
 *
 * @param item Pointer to struct list_item of the item to be removed.
 * @return     Whether the action has been performed.
 */
static inline bool
list_remove(struct list_item *item)
{
	if (!item->next || !item->prev || list_is_empty(item))
		return false;

	item->prev->next = item->next;
	item->next->prev = item->prev;
	item->next = item->prev = item;

	return true;
}

/**
 * Remove the last element of a list.
 *
 * @param head Pointer to the list's head.
 * @return     Pointer to struct list_item removed from the list;
 *             or NULL, if the list is empty.
 */
static inline struct list_item *
list_remove_tail(struct list_item *head)
{
	struct list_item *t = list_is_empty(head) ? NULL : head->prev;

	if (t)
		list_remove(t);

	return t;
}

/**
 * Remove the first element of a list.
 *
 * @param head Pointer to the list's head.
 * @return     Pointer to struct list_item removed from the list;
 *             or NULL, if the list is empty.
 */
static inline struct list_item *
list_remove_head(struct list_item *head)
{
	struct list_item *h = list_is_empty(head) ? NULL : head->next;

	if (h)
		list_remove(h);

	return h;
}

/**
 * Replace an old struct list_item in a list with the new one.
 *
 * @param old Pointer to struct list_item of the item to be replaced.
 * @param new Pointer to struct list_item of the item to be replaced with.
 * @return    Whether the replacement has been performed.
 */
static inline bool
list_replace(struct list_item *old, struct list_item *new)
{
	if (!old->next || !old->prev || list_is_empty(old))
		return false;

	new->next = old->next;
	new->prev = old->prev;
	old->prev->next = new;
	old->next->prev = new;
	old->next = old->prev = old;

	return true;
}

/**
 * List iteration wrapper for non-destructive operations.
 *
 * @param var_   Variable holding pointer to a current list item.
 * @param head_  Pointer to the list's head.
 * @param field_ Name of the field containing the respective struct list_item
 *               inside list items.
 */
# define list_foreach(var_, head_, field_) \
	for (var_ = list_elem((head_)->next, typeof(*var_), field_); \
	    &(var_->field_) != (head_); var_ = list_next(var_, field_))

/**
 * List iteration wrapper for destructive operations.
 *
 * @param var_   Variable holding pointer to a current list item.
 * @param head_  Pointer to the list's head.
 * @param field_ Name of the field containing the respective struct list_item
 *               inside list items.
 * @param _tmp   Temporary variable for storing pointer to the next item.
 */
# define list_foreach_safe(var_, head_, field_, _tmp) \
	for (var_ = list_elem((head_)->next, typeof(*var_), field_), \
	    _tmp = list_elem((var_)->field_.next, typeof(*var_), field_); \
	    &var_->field_ != head_; var_ = _tmp, _tmp = list_next(_tmp, field_))

#endif /* !STRACE_LIST_H */