Browse Source

Add a generic list implementation

Similar to the one used in the Linux kernel.

* macros.h (cast_ptr, containerof): New macros.
* list.h: New file.
* Makefile.am (strace_SOURCES): Add it.
Eugene Syromyatnikov 3 years ago
parent
commit
0e1b907215
3 changed files with 320 additions and 0 deletions
  1. 1
    0
      Makefile.am
  2. 300
    0
      list.h
  3. 19
    0
      macros.h

+ 1
- 0
Makefile.am View File

@@ -168,6 +168,7 @@ strace_SOURCES =	\
168 168
 	linux/asm_stat.h \
169 169
 	linux/x32/asm_stat.h \
170 170
 	linux/x86_64/asm_stat.h \
171
+	list.h		\
171 172
 	listen.c	\
172 173
 	lookup_dcookie.c \
173 174
 	loop.c		\

+ 300
- 0
list.h View File

@@ -0,0 +1,300 @@
1
+/*
2
+ * Some simple implementation of lists similar to the one used in the kernel.
3
+ *
4
+ * Copyright (c) 2016-2019 The strace developers.
5
+ * All rights reserved.
6
+ *
7
+ * SPDX-License-Identifier: LGPL-2.1-or-later
8
+ */
9
+
10
+#ifndef STRACE_LIST_H
11
+#define STRACE_LIST_H
12
+
13
+/*
14
+ * struct list_item and related macros and functions provide an interface
15
+ * for manipulating and iterating linked lists.  In order to have list
16
+ * associated with its payload, struct list_item has to be embedded into
17
+ * a structure type representing payload, and (optionally) an additional
18
+ * struct list_item should be added somewhere as a starting point for list
19
+ * iteration (creating a list with a designated head). A situation where
20
+ * no designated head exists, and each embedded struct list_head is considered
21
+ * a head (i.e. starting point for list iteration), is also possible.
22
+ *
23
+ * List head has to be initialised with list_init() call. Statically allocated
24
+ * list heads can also be defined with an EMPTY_LIST() macro.
25
+ *
26
+ * In order to get a pointer to list item from a struct list_item, list_elem
27
+ * macro is used.
28
+ *
29
+ * When a designated head is used, list_head() and list_tail() can be used
30
+ * for getting pointer to the first and the last list item, respectively.
31
+ *
32
+ * list_next() and list_prev() macros can be used for obtaining pointers
33
+ * to the next and the previous items in the list, respectively.  Note that
34
+ * they do not perform additional checks for the validity of these pointers,
35
+ * so they have to be guarded with respective list_head/list_tail checks in case
36
+ * of lists with designated heads (where the list's head is not embedded withing
37
+ * a list item.
38
+ *
39
+ * list_{insert,append,remove,remove_tail,remove_head,replace} provide some
40
+ * basic means of list manipulation.
41
+ *
42
+ * list_foreach() and list_foreach_safe() are wrapper macros for simplifying
43
+ * iteration over a list, with the latter having an additional argument
44
+ * for storing temporary pointer, thus allowing list manipulations during
45
+ * its iteration.
46
+ *
47
+ * A simple example:
48
+ *
49
+ *     struct my_struct {
50
+ *             int a;
51
+ *             struct list_item l1;
52
+ *             struct list_item l2;
53
+ *     };
54
+ *
55
+ *     EMPTY_LIST(list_1);              <--- Defining a designated head for list
56
+ *
57
+ *     struct my_struct *item =
58
+ *             calloc(1, sizeof(*item));
59
+ *     list_init(&item->l2);            <--- Initialising structure field that
60
+ *                                           is used for lists without designated
61
+ *                                           head.
62
+ *     list_insert(&list_1, &item->l1); <--- Inserting an item into the list
63
+ *
64
+ *     item = calloc(1, sizeof(*item));
65
+ *     list_init(&item->l2);
66
+ *
67
+ *     list_append(&(list_head(list_1, struct my_struct, l1)->l2), &item->l2);
68
+ *
69
+ *     struct my_struct *cur = item;    <--- Iteration over a headless list
70
+ *     do {
71
+ *             printf("%d\n", cur->a);
72
+ *     } while ((cur = list_next(&cur, l2)) != item);
73
+ *
74
+ *     struct my_struct *i;
75
+ *     list_foreach(i, list_1, l1) {    <--- Iteration over list_1 without list
76
+ *             printf("%d\n", i->a);         modification
77
+ *     }
78
+ *
79
+ *     struct my_struct *tmp;           <--- Iteration with modification
80
+ *     list_foreach_safe(i, list_1, l1, tmp) {
81
+ *             list_remove(&i->l1);
82
+ *             free(i);
83
+ *     }
84
+ *
85
+ * See also:
86
+ *     "Linux kernel design patterns - part 2", section "Linked Lists"
87
+ *     https://lwn.net/Articles/336255/
88
+ */
89
+
90
+#include "macros.h"
91
+
92
+struct list_item {
93
+	struct list_item *prev;
94
+	struct list_item *next;
95
+};
96
+
97
+/**
98
+ * Define an empty list head.
99
+ *
100
+ * @param l_ List head variable name.
101
+ */
102
+#define EMPTY_LIST(l_) struct list_item l_ = { &l_, &l_ }
103
+
104
+/** Initialise an empty list. */
105
+static inline void
106
+list_init(struct list_item *l)
107
+{
108
+	l->prev = l;
109
+	l->next = l;
110
+}
111
+
112
+/** Check whether list is empty. */
113
+static inline bool
114
+list_is_empty(const struct list_item *l)
115
+{
116
+	return ((l->next == l) && (l->prev == l))
117
+		/*
118
+		 * XXX This could be the case when struct list_item hasn't been
119
+		 *     initialised at all; we should probably also call some
120
+		 *     errror_func_msg() in that case, as it looks like sloppy
121
+		 *     programming.
122
+		 */
123
+		|| (!l->next && !l->prev);
124
+}
125
+
126
+/**
127
+ * Convert a pointer to a struct list_item to a pointer to a list item.
128
+ *
129
+ * @param var   Pointer to struct list_item.
130
+ * @param type  Type of the list's item.
131
+ * @param field Name of the field that holds the respective struct list_item.
132
+ */
133
+#define list_elem(var, type, field) containerof((var), type, field)
134
+
135
+/**
136
+ * Get the first element in a list.
137
+ *
138
+ * @param head  Pointer to the list's head.
139
+ * @param type  Type of the list's item.
140
+ * @param field Name of the field that holds the respective struct list_item.
141
+ */
142
+#define list_head(head, type, field) \
143
+	(list_is_empty(head) ? NULL : list_elem((head)->next, type, field))
144
+/**
145
+ * Get the last element in a list.
146
+ *
147
+ * @param head  Pointer to the list's head.
148
+ * @param type  Type of the list's item.
149
+ * @param field Name of the field that holds the respective struct list_item.
150
+ */
151
+#define list_tail(head, type, field) \
152
+	(list_is_empty(head) ? NULL : list_elem((head)->prev, type, field))
153
+
154
+/**
155
+ * Get the next element in a list.
156
+ *
157
+ * @param var   Pointer to a list item.
158
+ * @param field Name of the field that holds the respective struct list_item.
159
+ */
160
+#define list_next(var, field) \
161
+	list_elem((var)->field.next, typeof(*(var)), field)
162
+/**
163
+ * Get the previous element in a list.
164
+ *
165
+ * @param var   Pointer to a list item.
166
+ * @param field Name of the field that holds the respective struct list_item.
167
+ */
168
+#define list_prev(var, field) \
169
+	list_elem((var)->field.prev, typeof(*(var)), field)
170
+
171
+/**
172
+ * Insert an item into a list. The item is placed as the next list item
173
+ * to the head.
174
+ */
175
+static inline void
176
+list_insert(struct list_item *head, struct list_item *item)
177
+{
178
+	item->next = head->next;
179
+	item->prev = head;
180
+	head->next->prev = item;
181
+	head->next = item;
182
+}
183
+
184
+/**
185
+ * Insert an item into a list. The item is placed as the previous list item
186
+ * to the head.
187
+ */
188
+static inline void
189
+list_append(struct list_item *head, struct list_item *item)
190
+{
191
+	item->next = head;
192
+	item->prev = head->prev;
193
+	head->prev->next = item;
194
+	head->prev = item;
195
+}
196
+
197
+/**
198
+ * Remove an item from a list.
199
+ *
200
+ * @param item Pointer to struct list_item of the item to be removed.
201
+ * @return     Whether the action has been performed.
202
+ */
203
+static inline bool
204
+list_remove(struct list_item *item)
205
+{
206
+	if (!item->next || !item->prev || list_is_empty(item))
207
+		return false;
208
+
209
+	item->prev->next = item->next;
210
+	item->next->prev = item->prev;
211
+	item->next = item->prev = item;
212
+
213
+	return true;
214
+}
215
+
216
+/**
217
+ * Remove the last element of a list.
218
+ *
219
+ * @param head Pointer to the list's head.
220
+ * @return     Pointer to struct list_item removed from the list;
221
+ *             or NULL, if the list is empty.
222
+ */
223
+static inline struct list_item *
224
+list_remove_tail(struct list_item *head)
225
+{
226
+	struct list_item *t = list_is_empty(head) ? NULL : head->prev;
227
+
228
+	if (t)
229
+		list_remove(t);
230
+
231
+	return t;
232
+}
233
+
234
+/**
235
+ * Remove the first element of a list.
236
+ *
237
+ * @param head Pointer to the list's head.
238
+ * @return     Pointer to struct list_item removed from the list;
239
+ *             or NULL, if the list is empty.
240
+ */
241
+static inline struct list_item *
242
+list_remove_head(struct list_item *head)
243
+{
244
+	struct list_item *h = list_is_empty(head) ? NULL : head->next;
245
+
246
+	if (h)
247
+		list_remove(h);
248
+
249
+	return h;
250
+}
251
+
252
+/**
253
+ * Replace an old struct list_item in a list with the new one.
254
+ *
255
+ * @param old Pointer to struct list_item of the item to be replaced.
256
+ * @param new Pointer to struct list_item of the item to be replaced with.
257
+ * @return    Whether the replacement has been performed.
258
+ */
259
+static inline bool
260
+list_replace(struct list_item *old, struct list_item *new)
261
+{
262
+	if (!old->next || !old->prev || list_is_empty(old))
263
+		return false;
264
+
265
+	new->next = old->next;
266
+	new->prev = old->prev;
267
+	old->prev->next = new;
268
+	old->next->prev = new;
269
+	old->next = old->prev = old;
270
+
271
+	return true;
272
+}
273
+
274
+/**
275
+ * List iteration wrapper for non-destructive operations.
276
+ *
277
+ * @param var_   Variable holding pointer to a current list item.
278
+ * @param head_  Pointer to the list's head.
279
+ * @param field_ Name of the field containing the respective struct list_item
280
+ *               inside list items.
281
+ */
282
+#define list_foreach(var_, head_, field_) \
283
+	for (var_ = list_elem((head_)->next, typeof(*var_), field_); \
284
+	    &(var_->field_) != (head_); var_ = list_next(var_, field_))
285
+
286
+/**
287
+ * List iteration wrapper for destructive operations.
288
+ *
289
+ * @param var_   Variable holding pointer to a current list item.
290
+ * @param head_  Pointer to the list's head.
291
+ * @param field_ Name of the field containing the respective struct list_item
292
+ *               inside list items.
293
+ * @param _tmp   Temporary variable for storing pointer to the next item.
294
+ */
295
+#define list_foreach_safe(var_, head_, field_, _tmp) \
296
+	for (var_ = list_elem((head_)->next, typeof(*var_), field_), \
297
+	    _tmp = list_elem((var_)->field_.next, typeof(*var_), field_); \
298
+	    &var_->field_ != head_; var_ = _tmp, _tmp = list_next(_tmp, field_))
299
+
300
+#endif /* !STRACE_LIST_H */

+ 19
- 0
macros.h View File

@@ -41,6 +41,25 @@
41 41
 	(offsetof(type_, member_) + sizeof(((type_ *)0)->member_))
42 42
 # endif
43 43
 
44
+#ifndef cast_ptr
45
+# define cast_ptr(type_, var_)	\
46
+	((type_) (uintptr_t) (const volatile void *) (var_))
47
+#endif
48
+
49
+#ifndef containerof
50
+/**
51
+ * Return a pointer to a structure that contains the provided variable.
52
+ *
53
+ * @param ptr_    Pointer to data that is a field of the container structure.
54
+ * @param struct_ Type of the container structure.
55
+ * @param member_ Name of the member field.
56
+ * @return  Pointer to the container structure.
57
+ */
58
+# define containerof(ptr_, struct_, member_)	\
59
+	cast_ptr(struct_ *,			\
60
+		 (const volatile char *) (ptr_) - offsetof(struct_, member_))
61
+#endif
62
+
44 63
 static inline bool
45 64
 is_filled(const char *ptr, char fill, size_t size)
46 65
 {

Loading…
Cancel
Save