├── .editorconfig
├── .github
└── workflows
│ └── ci.yml
├── AUTHORS
├── NEWS.md
├── README.md
├── meson.build
└── src
├── c-list.h
├── meson.build
├── test-api.c
├── test-basic.c
└── test-embed.c
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 | trim_trailing_whitespace = true
7 | charset = utf-8
8 |
9 | [*.{c,h}]
10 | indent_style = space
11 | indent_size = 8
12 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Continuous Integration
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: '0 0 * * *'
8 |
9 | jobs:
10 | ci-linux:
11 | name: Linux CI
12 | uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
13 | with:
14 | cabuild_ref: "v1"
15 | linux: true
16 | m32: true
17 | matrixmode: true
18 | valgrind: true
19 | ci-macos:
20 | name: MacOS CI
21 | uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
22 | with:
23 | cabuild_ref: "v1"
24 | linux: false
25 | macos: true
26 | ci-windows:
27 | name: Windows CI
28 | uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1
29 | with:
30 | cabuild_ref: "v1"
31 | linux: false
32 | windows: true
33 |
--------------------------------------------------------------------------------
/AUTHORS:
--------------------------------------------------------------------------------
1 | LICENSE:
2 | This project is dual-licensed under both the Apache License, Version
3 | 2.0, and the GNU Lesser General Public License, Version 2.1+.
4 |
5 | AUTHORS-ASL:
6 | Licensed under the Apache License, Version 2.0 (the "License");
7 | you may not use this file except in compliance with the License.
8 | You may obtain a copy of the License at
9 |
10 | http://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing, software
13 | distributed under the License is distributed on an "AS IS" BASIS,
14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | See the License for the specific language governing permissions and
16 | limitations under the License.
17 |
18 | AUTHORS-LGPL:
19 | This program is free software; you can redistribute it and/or modify it
20 | under the terms of the GNU Lesser General Public License as published
21 | by the Free Software Foundation; either version 2.1 of the License, or
22 | (at your option) any later version.
23 |
24 | This program is distributed in the hope that it will be useful, but
25 | WITHOUT ANY WARRANTY; without even the implied warranty of
26 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
27 | Lesser General Public License for more details.
28 |
29 | You should have received a copy of the GNU Lesser General Public License
30 | along with this program; If not, see .
31 |
32 | COPYRIGHT: (ordered alphabetically)
33 | Copyright (C) 2015-2022 Red Hat, Inc.
34 |
35 | AUTHORS: (ordered alphabetically)
36 | Danilo Horta
37 | David Rheinsberg
38 | Lucas De Marchi
39 | Michele Dionisio
40 | Thomas Haller
41 | Tom Gundersen
42 |
--------------------------------------------------------------------------------
/NEWS.md:
--------------------------------------------------------------------------------
1 | # c-list - Circular Intrusive Double Linked List Collection
2 |
3 | ## CHANGES WITH 3.1.0:
4 |
5 | * The minimum required meson version is now 0.60.0.
6 |
7 | * New function c_list_split() is added. It reverses c_list_splice()
8 | and thus allows to split a list in half.
9 |
10 | Contributions from: David Rheinsberg, Michele Dionisio
11 |
12 | - Brno, 2022-06-22
13 |
14 | ## CHANGES WITH 3:
15 |
16 | * API break: The c_list_loop_*() symbols were removed, since we saw
17 | little use for them. No user was known at the time, so
18 | all projects should build with the new API version
19 | unchanged.
20 | Since c-list does not distribute any compiled code, there
21 | is no ABI issue with this change.
22 |
23 | * Two new symbols c_list_length() and c_list_contains(). They are meant
24 | for debugging purposes, to easily verify list integrity. Since they
25 | run in O(n) time, they are not recommended for any other use than
26 | debugging.
27 |
28 | * New symbol c_list_init() is provided as alternative to the verbose
29 | C_LIST_INIT assignment.
30 |
31 | * The c-list API is extended to work well with `const CList` objects.
32 | That is, any read-only accessor function allows constant objects as
33 | input now.
34 | Note that this does not propagate into other members linked in the
35 | list. Using `const` for CList members is of little practical use.
36 | However, it might be of use for its embedding objects, so we now
37 | allow it in the CList API as well.
38 |
39 | * The c_list_splice() call now clears the source list, rather than
40 | returning with stale pointers. Technically, this is also an API
41 | break, but unlikely to affect any existing code.
42 |
43 | Contributions from: David Herrmann, Thomas Haller
44 |
45 | - Berlin, 2017-08-13
46 |
47 | ## CHANGES WITH 2:
48 |
49 | * Adjust project-name in build-system to reflect the actual project. The
50 | previous releases incorrectly claimed to be c-rbtree in the build
51 | system.
52 |
53 | * Add c_list_swap() that swaps two lists given their head pointers.
54 |
55 | * Add c_list_splice() that moves a list.
56 |
57 | * Add LGPL2.1+ as license so c-list can be imported into GPL2 projects.
58 | It is now officially dual-licensed.
59 |
60 | * As usual a bunch of fixes, additional tests, and documentation
61 | updates.
62 |
63 | Contributions from: David Herrmann, Tom Gundersen
64 |
65 | - Lund, 2017-05-03
66 |
67 | ## CHANGES WITH 1:
68 |
69 | * Initial release of c-list.
70 |
71 | * This project provides an implementation of a circular double linked
72 | list in standard ISO-C11. License is ASL-2.0 and the build system
73 | used is `Meson'.
74 |
75 | Contributions from: David Herrmann, Tom Gundersen
76 |
77 | - Berlin, 2017-03-03
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | c-list
2 | ======
3 |
4 | Circular Intrusive Double Linked List Collection
5 |
6 | The c-list project implements an intrusive collection based on circular double
7 | linked lists in ISO-C11. It aims for minimal API constraints, leaving maximum
8 | control over the data-structures to the API consumer.
9 |
10 | ### Project
11 |
12 | * **Website**:
13 | * **Bug Tracker**:
14 |
15 | ### Requirements
16 |
17 | The requirements for this project are:
18 |
19 | * `libc` (e.g., `glibc >= 2.16`)
20 |
21 | At build-time, the following software is required:
22 |
23 | * `meson >= 0.60`
24 | * `pkg-config >= 0.29`
25 |
26 | ### Build
27 |
28 | The meson build-system is used for this project. Contact upstream
29 | documentation for detailed help. In most situations the following
30 | commands are sufficient to build and install from source:
31 |
32 | ```sh
33 | mkdir build
34 | cd build
35 | meson setup ..
36 | ninja
37 | meson test
38 | ninja install
39 | ```
40 |
41 | No custom configuration options are available.
42 |
43 | ### Repository:
44 |
45 | - **web**:
46 | - **https**: `https://github.com/c-util/c-list.git`
47 | - **ssh**: `git@github.com:c-util/c-list.git`
48 |
49 | ### License:
50 |
51 | - **Apache-2.0** OR **LGPL-2.1-or-later**
52 | - See AUTHORS file for details.
53 |
--------------------------------------------------------------------------------
/meson.build:
--------------------------------------------------------------------------------
1 | project(
2 | 'c-list',
3 | 'c',
4 | default_options: [
5 | 'c_std=c99',
6 | ],
7 | license: 'Apache',
8 | meson_version: '>=0.60.0',
9 | version: '3.1.0',
10 | )
11 | major = meson.project_version().split('.')[0]
12 | project_description = 'Circular Intrusive Double Linked List Collection'
13 |
14 | mod_pkgconfig = import('pkgconfig')
15 |
16 | # See c-stdaux for details on these. We do not have c-stdaux as dependency, so
17 | # we keep a duplicated set here, reduced to the minimum.
18 | cflags = meson.get_compiler('c').get_supported_arguments(
19 | '-D_GNU_SOURCE',
20 |
21 | '-Wno-gnu-alignof-expression',
22 | '-Wno-maybe-uninitialized',
23 | '-Wno-unknown-warning-option',
24 | '-Wno-unused-parameter',
25 |
26 | '-Wno-error=type-limits',
27 | '-Wno-error=missing-field-initializers',
28 |
29 | '-Wdate-time',
30 | '-Wdeclaration-after-statement',
31 | '-Wlogical-op',
32 | '-Wmissing-include-dirs',
33 | '-Wmissing-noreturn',
34 | '-Wnested-externs',
35 | '-Wredundant-decls',
36 | '-Wshadow',
37 | '-Wstrict-aliasing=3',
38 | '-Wsuggest-attribute=noreturn',
39 | '-Wundef',
40 | '-Wwrite-strings',
41 | )
42 | add_project_arguments(cflags, language: 'c')
43 |
44 | subdir('src')
45 |
46 | meson.override_dependency('libclist-'+major, libclist_dep, static: true)
47 |
--------------------------------------------------------------------------------
/src/c-list.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | /*
4 | * Circular Intrusive Double Linked List Collection in ISO-C11
5 | *
6 | * This implements a generic circular double linked list. List entries must
7 | * embed the CList object, which provides pointers to the next and previous
8 | * element. Insertion and removal can be done in O(1) due to the double links.
9 | * Furthermore, the list is circular, thus allows access to front/tail in O(1)
10 | * as well, even if you only have a single head pointer (which is not how the
11 | * list is usually operated, though).
12 | *
13 | * Note that you are free to use the list implementation without a head
14 | * pointer. However, usual operation uses a single CList object as head, which
15 | * is itself linked in the list and as such must be identified as list head.
16 | * This allows very simply list operations and avoids a lot of special cases.
17 | * Most importantly, you can unlink entries without requiring a head pointer.
18 | */
19 |
20 | #ifdef __cplusplus
21 | extern "C" {
22 | #endif
23 |
24 | #include
25 | #include
26 |
27 | typedef struct CList CList;
28 |
29 | /**
30 | * struct CList - Entry of a circular double linked list
31 | * @next: next entry
32 | * @prev: previous entry
33 | *
34 | * Each entry in a list must embed a CList object. This object contains
35 | * pointers to its next and previous elements, which can be freely accessed by
36 | * the API user at any time. Note that the list is circular, and the list head
37 | * is linked in the list as well.
38 | *
39 | * The list head must be initialized via C_LIST_INIT before use. There is no
40 | * reason to initialize entry objects before linking them. However, if you need
41 | * a boolean state that tells you whether the entry is linked or not, you should
42 | * initialize the entry via C_LIST_INIT as well.
43 | */
44 | struct CList {
45 | CList *next;
46 | CList *prev;
47 | };
48 |
49 | #define C_LIST_INIT(_var) { .next = &(_var), .prev = &(_var) }
50 |
51 | /**
52 | * c_list_init() - initialize list entry
53 | * @what: list entry to initialize
54 | *
55 | * Return: @what is returned.
56 | */
57 | static inline CList *c_list_init(CList *what) {
58 | *what = (CList)C_LIST_INIT(*what);
59 | return what;
60 | }
61 |
62 | /**
63 | * c_list_entry_offset() - get parent container of list entry
64 | * @what: list entry, or NULL
65 | * @offset: offset of the list member in its surrounding type
66 | *
67 | * If the list entry @what is embedded into a surrounding structure, this will
68 | * turn the list entry pointer @what into a pointer to the parent container
69 | * (sometimes called container_of(3)). Use the `c_list_entry()` macro for an
70 | * easier API.
71 | *
72 | * If @what is NULL, this will also return NULL.
73 | *
74 | * Return: Pointer to parent container, or NULL.
75 | */
76 | static inline void *c_list_entry_offset(const CList *what, size_t offset) {
77 | if (what) {
78 | /*
79 | * We allow calling "c_list_entry()" on the list head, which is
80 | * commonly a plain CList struct. The returned entry pointer is
81 | * thus invalid. For instance, this is used by the
82 | * c_list_for_each_entry*() macros. Gcc correctly warns about that
83 | * with "-Warray-bounds". However, as long as the value is never
84 | * dereferenced, this is fine. We explicitly use integer arithmetic
85 | * to circumvent the Gcc warning.
86 | */
87 | return (void *)(((uintptr_t)(void *)what) - offset);
88 | }
89 | return NULL;
90 | }
91 |
92 | /**
93 | * c_list_entry() - get parent container of list entry
94 | * @_what: list entry, or NULL
95 | * @_t: type of parent container
96 | * @_m: member name of list entry in @_t
97 | *
98 | * If the list entry @_what is embedded into a surrounding structure, this will
99 | * turn the list entry pointer @_what into a pointer to the parent container
100 | * (using offsetof(3), or sometimes called container_of(3)).
101 | *
102 | * If @_what is NULL, this will also return NULL.
103 | *
104 | * Return: Pointer to parent container, or NULL.
105 | */
106 | #define c_list_entry(_what, _t, _m) \
107 | ((_t *)c_list_entry_offset((_what), offsetof(_t, _m)))
108 |
109 | /**
110 | * c_list_is_linked() - check whether an entry is linked
111 | * @what: entry to check, or NULL
112 | *
113 | * Return: True if @what is linked in a list, false if not.
114 | */
115 | static inline _Bool c_list_is_linked(const CList *what) {
116 | return what && what->next != what;
117 | }
118 |
119 | /**
120 | * c_list_is_empty() - check whether a list is empty
121 | * @list: list to check, or NULL
122 | *
123 | * This is the same as !c_list_is_linked().
124 | *
125 | * Return: True if @list is empty, false if not.
126 | */
127 | static inline _Bool c_list_is_empty(const CList *list) {
128 | return !c_list_is_linked(list);
129 | }
130 |
131 | /**
132 | * c_list_link_before() - link entry into list
133 | * @where: linked list entry used as anchor
134 | * @what: entry to link
135 | *
136 | * This links @what directly in front of @where. @where can either be a list
137 | * head or any entry in the list.
138 | *
139 | * If @where points to the list head, this effectively links @what as new tail
140 | * element. Hence, the macro c_list_link_tail() is an alias to this.
141 | *
142 | * @what is not inspected prior to being linked. Hence, it better not be linked
143 | * into another list, or the other list will be corrupted.
144 | */
145 | static inline void c_list_link_before(CList *where, CList *what) {
146 | CList *prev = where->prev, *next = where;
147 |
148 | next->prev = what;
149 | what->next = next;
150 | what->prev = prev;
151 | prev->next = what;
152 | }
153 | #define c_list_link_tail(_list, _what) c_list_link_before((_list), (_what))
154 |
155 | /**
156 | * c_list_link_after() - link entry into list
157 | * @where: linked list entry used as anchor
158 | * @what: entry to link
159 | *
160 | * This links @what directly after @where. @where can either be a list head or
161 | * any entry in the list.
162 | *
163 | * If @where points to the list head, this effectively links @what as new front
164 | * element. Hence, the macro c_list_link_front() is an alias to this.
165 | *
166 | * @what is not inspected prior to being linked. Hence, it better not be linked
167 | * into another list, or the other list will be corrupted.
168 | */
169 | static inline void c_list_link_after(CList *where, CList *what) {
170 | CList *prev = where, *next = where->next;
171 |
172 | next->prev = what;
173 | what->next = next;
174 | what->prev = prev;
175 | prev->next = what;
176 | }
177 | #define c_list_link_front(_list, _what) c_list_link_after((_list), (_what))
178 |
179 | /**
180 | * c_list_unlink_stale() - unlink element from list
181 | * @what: element to unlink
182 | *
183 | * This unlinks @what. If @what was initialized via C_LIST_INIT(), it has no
184 | * effect. If @what was never linked, nor initialized, behavior is undefined.
185 | *
186 | * Note that this does not modify @what. It just modifies the previous and next
187 | * elements in the list to no longer reference @what. If you want to make sure
188 | * @what is re-initialized after removal, use c_list_unlink().
189 | */
190 | static inline void c_list_unlink_stale(CList *what) {
191 | CList *prev = what->prev, *next = what->next;
192 |
193 | next->prev = prev;
194 | prev->next = next;
195 | }
196 |
197 | /**
198 | * c_list_unlink() - unlink element from list and re-initialize
199 | * @what: element to unlink
200 | *
201 | * This is like c_list_unlink_stale() but re-initializes @what after removal.
202 | */
203 | static inline void c_list_unlink(CList *what) {
204 | /* condition is not needed, but avoids STOREs in fast-path */
205 | if (c_list_is_linked(what)) {
206 | c_list_unlink_stale(what);
207 | *what = (CList)C_LIST_INIT(*what);
208 | }
209 | }
210 |
211 | /**
212 | * c_list_swap() - exchange the contents of two lists
213 | * @list1: the list to operate on
214 | * @list2: the list to operate on
215 | *
216 | * This replaces the contents of the list @list1 with the contents
217 | * of @list2, and vice versa.
218 | */
219 | static inline void c_list_swap(CList *list1, CList *list2) {
220 | CList t;
221 |
222 | /* make neighbors of list1 point to list2, and vice versa */
223 | t = *list1;
224 | t.next->prev = list2;
225 | t.prev->next = list2;
226 | t = *list2;
227 | t.next->prev = list1;
228 | t.prev->next = list1;
229 |
230 | /* swap list1 and list2 now that their neighbors were fixed up */
231 | t = *list1;
232 | *list1 = *list2;
233 | *list2 = t;
234 | }
235 |
236 | /**
237 | * c_list_splice() - splice one list into another
238 | * @target: the list to splice into
239 | * @source: the list to splice
240 | *
241 | * This removes all the entries from @source and splice them into @target.
242 | * The order of the two lists is preserved and the source is appended
243 | * to the end of target.
244 | *
245 | * On return, the source list will be empty.
246 | */
247 | static inline void c_list_splice(CList *target, CList *source) {
248 | if (!c_list_is_empty(source)) {
249 | /* attach the front of @source to the tail of @target */
250 | source->next->prev = target->prev;
251 | target->prev->next = source->next;
252 |
253 | /* attach the tail of @source to the front of @target */
254 | source->prev->next = target;
255 | target->prev = source->prev;
256 |
257 | /* clear source */
258 | *source = (CList)C_LIST_INIT(*source);
259 | }
260 | }
261 |
262 | /**
263 | * c_list_split() - split one list in two
264 | * @source: the list to split
265 | * @where: new starting element of newlist
266 | * @target: new list
267 | *
268 | * This splits @source in two. All elements following @where (including @where)
269 | * are moved to @target, replacing any old list. If @where points to @source
270 | * (i.e., the end of the list), @target will be empty.
271 | */
272 | static inline void c_list_split(CList *source, CList *where, CList *target) {
273 | if (where == source) {
274 | *target = (CList)C_LIST_INIT(*target);
275 | } else {
276 | target->next = where;
277 | target->prev = source->prev;
278 |
279 | where->prev->next = source;
280 | source->prev = where->prev;
281 |
282 | where->prev = target;
283 | target->prev->next = target;
284 | }
285 | }
286 |
287 | /**
288 | * c_list_first() - return pointer to first element, or NULL if empty
289 | * @list: list to operate on, or NULL
290 | *
291 | * This returns a pointer to the first element, or NULL if empty. This never
292 | * returns a pointer to the list head.
293 | *
294 | * Return: Pointer to first list element, or NULL if empty.
295 | */
296 | static inline CList *c_list_first(CList *list) {
297 | return c_list_is_empty(list) ? NULL : list->next;
298 | }
299 |
300 | /**
301 | * c_list_last() - return pointer to last element, or NULL if empty
302 | * @list: list to operate on, or NULL
303 | *
304 | * This returns a pointer to the last element, or NULL if empty. This never
305 | * returns a pointer to the list head.
306 | *
307 | * Return: Pointer to last list element, or NULL if empty.
308 | */
309 | static inline CList *c_list_last(CList *list) {
310 | return c_list_is_empty(list) ? NULL : list->prev;
311 | }
312 |
313 | /**
314 | * c_list_first_entry() - return pointer to first entry, or NULL if empty
315 | * @_list: list to operate on, or NULL
316 | * @_t: type of list entries
317 | * @_m: name of CList member in @_t
318 | *
319 | * This is like c_list_first(), but also applies c_list_entry() on the result.
320 | *
321 | * Return: Pointer to first list entry, or NULL if empty.
322 | */
323 | #define c_list_first_entry(_list, _t, _m) \
324 | c_list_entry(c_list_first(_list), _t, _m)
325 |
326 | /**
327 | * c_list_last_entry() - return pointer to last entry, or NULL if empty
328 | * @_list: list to operate on, or NULL
329 | * @_t: type of list entries
330 | * @_m: name of CList member in @_t
331 | *
332 | * This is like c_list_last(), but also applies c_list_entry() on the result.
333 | *
334 | * Return: Pointer to last list entry, or NULL if empty.
335 | */
336 | #define c_list_last_entry(_list, _t, _m) \
337 | c_list_entry(c_list_last(_list), _t, _m)
338 |
339 | /**
340 | * c_list_for_each*() - iterators
341 | *
342 | * The c_list_for_each*() macros provide simple for-loop wrappers to iterate
343 | * a linked list. They come in a set of flavours:
344 | *
345 | * - "entry": This combines c_list_entry() with the loop iterator, so the
346 | * iterator always has the type of the surrounding object, rather
347 | * than CList.
348 | *
349 | * - "safe": The loop iterator always keeps track of the next element to
350 | * visit. This means, you can safely modify the current element,
351 | * while retaining loop-integrity.
352 | * You still must not touch any other entry of the list. Otherwise,
353 | * the loop-iterator will be corrupted.
354 | *
355 | * - "continue": Rather than starting the iteration at the front of the list,
356 | * use the current value of the iterator as starting position.
357 | * Note that the first loop iteration will be the following
358 | * element, not the given element.
359 | *
360 | * - "unlink": This unlinks the current element from the list before the loop
361 | * code is run. Note that this only does a partial unlink, since
362 | * it assumes the entire list will be unlinked. You must not
363 | * break out of the loop, or the list will be in an inconsistent
364 | * state.
365 | */
366 |
367 | /* direct/raw iterators */
368 |
369 | #define c_list_for_each(_iter, _list) \
370 | for (_iter = (_list)->next; \
371 | (_iter) != (_list); \
372 | _iter = (_iter)->next)
373 |
374 | #define c_list_for_each_safe(_iter, _safe, _list) \
375 | for (_iter = (_list)->next, _safe = (_iter)->next; \
376 | (_iter) != (_list); \
377 | _iter = (_safe), _safe = (_safe)->next)
378 |
379 | #define c_list_for_each_continue(_iter, _list) \
380 | for (_iter = (_iter) ? (_iter)->next : (_list)->next; \
381 | (_iter) != (_list); \
382 | _iter = (_iter)->next)
383 |
384 | #define c_list_for_each_safe_continue(_iter, _safe, _list) \
385 | for (_iter = (_iter) ? (_iter)->next : (_list)->next, \
386 | _safe = (_iter)->next; \
387 | (_iter) != (_list); \
388 | _iter = (_safe), _safe = (_safe)->next)
389 |
390 | #define c_list_for_each_safe_unlink(_iter, _safe, _list) \
391 | for (_iter = (_list)->next, _safe = (_iter)->next; \
392 | c_list_init(_iter) != (_list); \
393 | _iter = (_safe), _safe = (_safe)->next)
394 |
395 | /* c_list_entry() based iterators */
396 |
397 | #define c_list_for_each_entry(_iter, _list, _m) \
398 | for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m); \
399 | &(_iter)->_m != (_list); \
400 | _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m))
401 |
402 | #define c_list_for_each_entry_safe(_iter, _safe, _list, _m) \
403 | for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \
404 | _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \
405 | &(_iter)->_m != (_list); \
406 | _iter = (_safe), \
407 | _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m))
408 |
409 | #define c_list_for_each_entry_continue(_iter, _list, _m) \
410 | for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \
411 | __typeof__(*_iter), \
412 | _m); \
413 | &(_iter)->_m != (_list); \
414 | _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m))
415 |
416 | #define c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m) \
417 | for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \
418 | __typeof__(*_iter), \
419 | _m), \
420 | _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \
421 | &(_iter)->_m != (_list); \
422 | _iter = (_safe), \
423 | _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m))
424 |
425 | #define c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m) \
426 | for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \
427 | _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \
428 | c_list_init(&(_iter)->_m) != (_list); \
429 | _iter = (_safe), \
430 | _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m))
431 |
432 | /**
433 | * c_list_flush() - flush all entries from a list
434 | * @list: list to flush
435 | *
436 | * This unlinks all entries from the given list @list and reinitializes their
437 | * link-nodes via C_LIST_INIT().
438 | *
439 | * Note that the entries are not modified in any other way, nor is their memory
440 | * released. This function just unlinks them and resets all the list nodes. It
441 | * is particularly useful with temporary lists on the stack in combination with
442 | * the GCC-extension __attribute__((__cleanup__(arg))).
443 | */
444 | static inline void c_list_flush(CList *list) {
445 | CList *iter, *safe;
446 |
447 | c_list_for_each_safe_unlink(iter, safe, list)
448 | /* empty */ ;
449 | }
450 |
451 | /**
452 | * c_list_length() - return number of linked entries, excluding the head
453 | * @list: list to operate on
454 | *
455 | * Returns the number of entries in the list, excluding the list head @list.
456 | * That is, for a list that is empty according to c_list_is_empty(), the
457 | * returned length is 0. This requires to iterate the list and has thus O(n)
458 | * runtime.
459 | *
460 | * Note that this function is meant for debugging purposes only. If you need
461 | * the list size during normal operation, you should maintain a counter
462 | * separately.
463 | *
464 | * Return: Number of items in @list.
465 | */
466 | static inline size_t c_list_length(const CList *list) {
467 | size_t n = 0;
468 | const CList *iter;
469 |
470 | c_list_for_each(iter, list)
471 | ++n;
472 |
473 | return n;
474 | }
475 |
476 | /**
477 | * c_list_contains() - check whether an entry is linked in a certain list
478 | * @list: list to operate on
479 | * @what: entry to look for
480 | *
481 | * This checks whether @what is linked into @list. This requires a linear
482 | * search through the list, as such runs in O(n). Note that the list-head is
483 | * considered part of the list, and hence this returns true if @what equals
484 | * @list.
485 | *
486 | * Note that this function is meant for debugging purposes, and consistency
487 | * checks. You should always be aware whether your objects are linked in a
488 | * specific list.
489 | *
490 | * Return: True if @what is in @list, false otherwise.
491 | */
492 | static inline _Bool c_list_contains(const CList *list, const CList *what) {
493 | const CList *iter;
494 |
495 | c_list_for_each(iter, list)
496 | if (what == iter)
497 | return 1;
498 |
499 | return what == list;
500 | }
501 |
502 | #ifdef __cplusplus
503 | }
504 | #endif
505 |
--------------------------------------------------------------------------------
/src/meson.build:
--------------------------------------------------------------------------------
1 | #
2 | # target: libclist.so
3 | # (No .so is built so far, since we are header-only. This might change in the
4 | # future, if we add more complex list helpers.)
5 | #
6 |
7 | libclist_dep = declare_dependency(
8 | include_directories: include_directories('.'),
9 | version: meson.project_version(),
10 | )
11 |
12 | if not meson.is_subproject()
13 | install_headers('c-list.h')
14 |
15 | mod_pkgconfig.generate(
16 | description: project_description,
17 | filebase: 'libclist-'+major,
18 | name: 'libclist',
19 | version: meson.project_version(),
20 | )
21 | endif
22 |
23 | #
24 | # target: test-*
25 | #
26 |
27 | test_api = executable('test-api', ['test-api.c'], dependencies: libclist_dep)
28 | test('API Symbol Visibility', test_api)
29 |
30 | test_basic = executable('test-basic', ['test-basic.c'], dependencies: libclist_dep)
31 | test('Basic API Behavior', test_basic)
32 |
33 | test_embed = executable('test-embed', ['test-embed.c'], dependencies: libclist_dep)
34 | test('Embedded List Nodes', test_embed)
35 |
--------------------------------------------------------------------------------
/src/test-api.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Tests for Public API
3 | * This test, unlikely the others, is linked against the real, distributed,
4 | * shared library. Its sole purpose is to test for symbol availability.
5 | */
6 |
7 | #undef NDEBUG
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include "c-list.h"
13 |
14 | typedef struct {
15 | int id;
16 | CList link;
17 | } Node;
18 |
19 | static void test_api(void) {
20 | CList *list_iter, *list_safe;
21 | CList list = C_LIST_INIT(list), list2 = C_LIST_INIT(list2);
22 | Node node = { .id = 0, .link = C_LIST_INIT(node.link) };
23 |
24 | assert(c_list_init(&list) == &list);
25 | assert(!c_list_entry_offset(NULL, 0));
26 | assert(!c_list_entry_offset(NULL, offsetof(Node, link)));
27 | assert(!c_list_entry(NULL, Node, link));
28 | assert(c_list_entry(&node.link, Node, link) == &node);
29 | assert(!c_list_is_linked(&node.link));
30 | assert(c_list_is_empty(&list));
31 | assert(c_list_length(&list) == 0);
32 | assert(c_list_contains(&list, &list));
33 | assert(!c_list_contains(&list, &node.link));
34 | c_list_flush(&list);
35 |
36 | /* basic link / unlink calls */
37 |
38 | c_list_link_before(&list, &node.link);
39 | assert(c_list_is_linked(&node.link));
40 | assert(!c_list_is_empty(&list));
41 | assert(c_list_length(&list) == 1);
42 | assert(c_list_contains(&list, &list));
43 | assert(c_list_contains(&list, &node.link));
44 |
45 | c_list_unlink_stale(&node.link);
46 | assert(c_list_is_linked(&node.link));
47 | assert(c_list_is_empty(&list));
48 | assert(c_list_length(&list) == 0);
49 |
50 | c_list_link_after(&list, &node.link);
51 | assert(c_list_is_linked(&node.link));
52 | assert(!c_list_is_empty(&list));
53 |
54 | c_list_unlink(&node.link);
55 | assert(!c_list_is_linked(&node.link));
56 | assert(c_list_is_empty(&list));
57 |
58 | /* link / unlink aliases */
59 |
60 | c_list_link_front(&list, &node.link);
61 | assert(c_list_is_linked(&node.link));
62 |
63 | c_list_unlink(&node.link);
64 | assert(!c_list_is_linked(&node.link));
65 |
66 | c_list_link_tail(&list, &node.link);
67 | assert(c_list_is_linked(&node.link));
68 |
69 | c_list_unlink(&node.link);
70 | assert(!c_list_is_linked(&node.link));
71 |
72 | /* swap / splice / split list operators */
73 |
74 | c_list_swap(&list, &list);
75 | assert(c_list_is_empty(&list));
76 |
77 | c_list_splice(&list, &list);
78 | assert(c_list_is_empty(&list));
79 |
80 | c_list_split(&list, &list, &list2);
81 | assert(c_list_is_empty(&list));
82 | assert(c_list_is_empty(&list2));
83 |
84 | /* direct/raw iterators */
85 |
86 | c_list_for_each(list_iter, &list)
87 | assert(list_iter != &list);
88 |
89 | c_list_for_each_safe(list_iter, list_safe, &list)
90 | assert(list_iter != &list);
91 |
92 | list_iter = NULL;
93 | c_list_for_each_continue(list_iter, &list)
94 | assert(list_iter != &list);
95 |
96 | list_iter = NULL;
97 | c_list_for_each_safe_continue(list_iter, list_safe, &list)
98 | assert(list_iter != &list);
99 |
100 | c_list_for_each_safe_unlink(list_iter, list_safe, &list)
101 | assert(list_iter != &list);
102 |
103 | /* list accessors */
104 |
105 | assert(!c_list_first(&list));
106 | assert(!c_list_last(&list));
107 | assert(!c_list_first_entry(&list, Node, link));
108 | assert(!c_list_last_entry(&list, Node, link));
109 | }
110 |
111 | #if defined(__GNUC__) || defined(__clang__)
112 | static void test_api_gnu(void) {
113 | CList list = C_LIST_INIT(list);
114 | Node *node_iter, *node_safe;
115 |
116 | /* c_list_entry() based iterators */
117 |
118 | c_list_for_each_entry(node_iter, &list, link)
119 | assert(&node_iter->link != &list);
120 |
121 | c_list_for_each_entry_safe(node_iter, node_safe, &list, link)
122 | assert(&node_iter->link != &list);
123 |
124 | node_iter = NULL;
125 | c_list_for_each_entry_continue(node_iter, &list, link)
126 | assert(&node_iter->link != &list);
127 |
128 | node_iter = NULL;
129 | c_list_for_each_entry_safe_continue(node_iter, node_safe, &list, link)
130 | assert(&node_iter->link != &list);
131 |
132 | c_list_for_each_entry_safe_unlink(node_iter, node_safe, &list, link)
133 | assert(&node_iter->link != &list);
134 | }
135 | #else
136 | static void test_api_gnu(void) {
137 | }
138 | #endif
139 |
140 | int main(void) {
141 | test_api();
142 | test_api_gnu();
143 | return 0;
144 | }
145 |
--------------------------------------------------------------------------------
/src/test-basic.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Tests for basic functionality
3 | * This contains basic, deterministic tests for list behavior, API
4 | * functionality, and usage.
5 | */
6 |
7 | #undef NDEBUG
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include "c-list.h"
13 |
14 | static void assert_list_integrity(CList *list) {
15 | CList *iter;
16 |
17 | iter = list;
18 | do {
19 | assert(iter->next->prev == iter);
20 | assert(iter->prev->next == iter);
21 |
22 | iter = iter->next;
23 | } while (iter != list);
24 | }
25 |
26 | static void test_iterators(void) {
27 | CList *iter, *safe, a, b, list = C_LIST_INIT(list);
28 | unsigned int i;
29 |
30 | assert(!c_list_first(&list));
31 | assert(!c_list_last(&list));
32 |
33 | /* link @a and verify iterators see just it */
34 |
35 | c_list_link_tail(&list, &a);
36 | assert(c_list_is_linked(&a));
37 | assert(c_list_first(&list) == &a);
38 | assert(c_list_last(&list) == &a);
39 |
40 | i = 0;
41 | c_list_for_each(iter, &list) {
42 | assert(iter == &a);
43 | ++i;
44 | }
45 | assert(i == 1);
46 |
47 | i = 0;
48 | iter = NULL;
49 | c_list_for_each_continue(iter, &list) {
50 | assert(iter == &a);
51 | ++i;
52 | }
53 | assert(i == 1);
54 |
55 | i = 0;
56 | iter = &a;
57 | c_list_for_each_continue(iter, &list)
58 | ++i;
59 | assert(i == 0);
60 |
61 | /* link @b as well and verify iterators again */
62 |
63 | c_list_link_tail(&list, &b);
64 | assert(c_list_is_linked(&a));
65 | assert(c_list_is_linked(&b));
66 |
67 | i = 0;
68 | c_list_for_each(iter, &list) {
69 | assert((i == 0 && iter == &a) ||
70 | (i == 1 && iter == &b));
71 | ++i;
72 | }
73 | assert(i == 2);
74 |
75 | i = 0;
76 | iter = NULL;
77 | c_list_for_each_continue(iter, &list) {
78 | assert((i == 0 && iter == &a) ||
79 | (i == 1 && iter == &b));
80 | ++i;
81 | }
82 | assert(i == 2);
83 |
84 | i = 0;
85 | iter = &a;
86 | c_list_for_each_continue(iter, &list) {
87 | assert(iter == &b);
88 | ++i;
89 | }
90 | assert(i == 1);
91 |
92 | i = 0;
93 | iter = &b;
94 | c_list_for_each_continue(iter, &list)
95 | ++i;
96 | assert(i == 0);
97 |
98 | /* verify safe-iterator while removing elements */
99 |
100 | i = 0;
101 | c_list_for_each_safe(iter, safe, &list) {
102 | assert(iter == &a || iter == &b);
103 | c_list_unlink_stale(iter);
104 | ++i;
105 | }
106 | assert(i == 2);
107 |
108 | assert(c_list_is_empty(&list));
109 |
110 | /* link both and verify *_unlink() iterators */
111 |
112 | c_list_link_tail(&list, &a);
113 | c_list_link_tail(&list, &b);
114 |
115 | i = 0;
116 | c_list_for_each_safe_unlink(iter, safe, &list) {
117 | assert(iter == &a || iter == &b);
118 | assert(!c_list_is_linked(iter));
119 | ++i;
120 | }
121 | assert(i == 2);
122 |
123 | assert(c_list_is_empty(&list));
124 | }
125 |
126 | static void test_swap(void) {
127 | CList list1 = (CList)C_LIST_INIT(list1);
128 | CList list2 = (CList)C_LIST_INIT(list2);
129 | CList list;
130 |
131 | c_list_swap(&list1, &list2);
132 |
133 | assert(list1.prev == list1.next && list1.prev == &list1);
134 | assert(list2.prev == list2.next && list2.prev == &list2);
135 |
136 | c_list_link_tail(&list1, &list);
137 |
138 | assert(c_list_first(&list1) == &list);
139 | assert(c_list_last(&list1) == &list);
140 | assert(list.next == &list1);
141 | assert(list.prev == &list1);
142 |
143 | c_list_swap(&list1, &list2);
144 |
145 | assert(c_list_first(&list2) == &list);
146 | assert(c_list_last(&list2) == &list);
147 | assert(list.next == &list2);
148 | assert(list.prev == &list2);
149 |
150 | assert(list1.prev == list1.next && list1.prev == &list1);
151 | }
152 |
153 | static void test_splice(void) {
154 | CList target = (CList)C_LIST_INIT(target);
155 | CList source = (CList)C_LIST_INIT(source);
156 | CList e1, e2;
157 |
158 | c_list_link_tail(&source, &e1);
159 |
160 | c_list_splice(&target, &source);
161 | assert(c_list_first(&target) == &e1);
162 | assert(c_list_last(&target) == &e1);
163 |
164 | source = (CList)C_LIST_INIT(source);
165 |
166 | c_list_link_tail(&source, &e2);
167 |
168 | c_list_splice(&target, &source);
169 | assert(c_list_first(&target) == &e1);
170 | assert(c_list_last(&target) == &e2);
171 | }
172 |
173 | static void test_split(void) {
174 | CList e1, e2;
175 |
176 | /* split empty list */
177 | {
178 | CList source = C_LIST_INIT(source), target;
179 |
180 | c_list_split(&source, &source, &target);
181 | assert(c_list_is_empty(&source));
182 | assert(c_list_is_empty(&target));
183 | assert_list_integrity(&source);
184 | assert_list_integrity(&target);
185 | }
186 |
187 | /* split 1-element list excluding the element */
188 | {
189 | CList source = C_LIST_INIT(source), target;
190 |
191 | c_list_link_tail(&source, &e1);
192 | c_list_split(&source, &source, &target);
193 | assert(!c_list_is_empty(&source));
194 | assert(c_list_is_empty(&target));
195 | assert_list_integrity(&source);
196 | assert_list_integrity(&target);
197 | }
198 |
199 | /* split 1-element list including the element */
200 | {
201 | CList source = C_LIST_INIT(source), target;
202 |
203 | c_list_link_tail(&source, &e1);
204 | c_list_split(&source, &e1, &target);
205 | assert(c_list_is_empty(&source));
206 | assert(!c_list_is_empty(&target));
207 | assert_list_integrity(&source);
208 | assert_list_integrity(&target);
209 | }
210 |
211 | /* split 2-element list excluding the elements */
212 | {
213 | CList source = C_LIST_INIT(source), target;
214 |
215 | c_list_link_tail(&source, &e1);
216 | c_list_link_tail(&source, &e2);
217 | c_list_split(&source, &source, &target);
218 | assert(!c_list_is_empty(&source));
219 | assert(c_list_is_empty(&target));
220 | assert_list_integrity(&source);
221 | assert_list_integrity(&target);
222 | }
223 |
224 | /* split 2-element list including one element */
225 | {
226 | CList source = C_LIST_INIT(source), target;
227 |
228 | c_list_link_tail(&source, &e1);
229 | c_list_link_tail(&source, &e2);
230 | c_list_split(&source, &e2, &target);
231 | assert(!c_list_is_empty(&source));
232 | assert(!c_list_is_empty(&target));
233 | assert_list_integrity(&source);
234 | assert_list_integrity(&target);
235 | }
236 |
237 | /* split 2-element list including both elements */
238 | {
239 | CList source = C_LIST_INIT(source), target;
240 |
241 | c_list_link_tail(&source, &e1);
242 | c_list_link_tail(&source, &e2);
243 | c_list_split(&source, &e1, &target);
244 | assert(c_list_is_empty(&source));
245 | assert(!c_list_is_empty(&target));
246 | assert_list_integrity(&source);
247 | assert_list_integrity(&target);
248 | }
249 | }
250 |
251 |
252 | static void test_flush(void) {
253 | CList e1 = C_LIST_INIT(e1), e2 = C_LIST_INIT(e2);
254 | CList list1 = C_LIST_INIT(list1), list2 = C_LIST_INIT(list2);
255 |
256 | c_list_link_tail(&list2, &e1);
257 | c_list_link_tail(&list2, &e2);
258 |
259 | assert(c_list_is_linked(&e1));
260 | assert(c_list_is_linked(&e2));
261 |
262 | c_list_flush(&list1);
263 | c_list_flush(&list2);
264 |
265 | assert(!c_list_is_linked(&e1));
266 | assert(!c_list_is_linked(&e2));
267 | }
268 |
269 | static void test_macros(void) {
270 | /* Verify `c_list_entry()` evaluates arguments only once. */
271 | {
272 | struct TestList {
273 | int a;
274 | CList link;
275 | int b;
276 | } list = { .link = C_LIST_INIT(list.link) };
277 | CList *p[2] = { &list.link, NULL };
278 | unsigned int i = 0;
279 |
280 | assert(i == 0);
281 | assert(c_list_entry(p[i++], struct TestList, link) == &list);
282 | assert(i == 1);
283 | }
284 | }
285 |
286 | #if defined(__GNUC__) || defined(__clang__)
287 | static void test_gnu(void) {
288 | CList e1 = C_LIST_INIT(e1), e2 = C_LIST_INIT(e2);
289 |
290 | /* Test `c_list_flush()` in combination with cleanup attributes. */
291 | {
292 | __attribute((cleanup(c_list_flush))) CList list1 = C_LIST_INIT(list1);
293 | __attribute((cleanup(c_list_flush))) CList list2 = C_LIST_INIT(list2);
294 |
295 | c_list_link_tail(&list2, &e1);
296 | c_list_link_tail(&list2, &e2);
297 |
298 | assert(c_list_is_linked(&e1));
299 | assert(c_list_is_linked(&e2));
300 | }
301 |
302 | assert(!c_list_is_linked(&e1));
303 | assert(!c_list_is_linked(&e2));
304 | }
305 | #else
306 | static void test_gnu(void) {
307 | }
308 | #endif
309 |
310 | int main(void) {
311 | test_iterators();
312 | test_swap();
313 | test_splice();
314 | test_split();
315 | test_flush();
316 | test_macros();
317 | test_gnu();
318 | return 0;
319 | }
320 |
--------------------------------------------------------------------------------
/src/test-embed.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Tests for embedded CList members
3 | */
4 |
5 | #undef NDEBUG
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "c-list.h"
11 |
12 | typedef struct Entry Entry;
13 |
14 | struct Entry {
15 | short foo;
16 | CList link;
17 | short bar;
18 | };
19 |
20 | static void test_entry(void) {
21 | CList list = C_LIST_INIT(list);
22 | Entry e1 = { .foo = 1 * 7, .bar = 1 * 11 };
23 | Entry e2 = { .foo = 2 * 7, .bar = 2 * 11 };
24 | Entry e3 = { .foo = 3 * 7, .bar = 3 * 11 };
25 | Entry e4 = { .foo = 4 * 7, .bar = 4 * 11 };
26 | Entry *e;
27 | CList *iter, *safe;
28 | size_t i;
29 |
30 | /* verify c_list_entry() works as expected (even with NULL) */
31 |
32 | assert(!c_list_entry(NULL, Entry, link));
33 | assert(&e1 == c_list_entry(&e1.link, Entry, link));
34 |
35 | /* verify @list is empty */
36 |
37 | assert(!c_list_first_entry(&list, Entry, link));
38 | assert(!c_list_last_entry(&list, Entry, link));
39 |
40 | /* link 2 entries and verify list state */
41 |
42 | c_list_link_tail(&list, &e1.link);
43 | c_list_link_tail(&list, &e2.link);
44 |
45 | assert(c_list_first_entry(&list, Entry, link)->foo == 1 * 7);
46 | assert(c_list_first_entry(&list, Entry, link)->bar == 1 * 11);
47 | assert(c_list_last_entry(&list, Entry, link)->foo == 2 * 7);
48 | assert(c_list_last_entry(&list, Entry, link)->bar == 2 * 11);
49 |
50 | i = 0;
51 | c_list_for_each(iter, &list) {
52 | e = c_list_entry(iter, Entry, link);
53 | assert(i != 0 || e == &e1);
54 | assert(i != 1 || e == &e2);
55 | assert(i < 2);
56 | ++i;
57 | }
58 | assert(i == 2);
59 |
60 | /* link 2 more entries */
61 |
62 | c_list_link_tail(&list, &e3.link);
63 | c_list_link_tail(&list, &e4.link);
64 |
65 | assert(c_list_first_entry(&list, Entry, link)->foo == 1 * 7);
66 | assert(c_list_first_entry(&list, Entry, link)->bar == 1 * 11);
67 | assert(c_list_last_entry(&list, Entry, link)->foo == 4 * 7);
68 | assert(c_list_last_entry(&list, Entry, link)->bar == 4 * 11);
69 |
70 | i = 0;
71 | c_list_for_each(iter, &list) {
72 | e = c_list_entry(iter, Entry, link);
73 | assert(i != 0 || e == &e1);
74 | assert(i != 1 || e == &e2);
75 | assert(i != 2 || e == &e3);
76 | assert(i != 3 || e == &e4);
77 | assert(i < 4);
78 | ++i;
79 | }
80 | assert(i == 4);
81 |
82 | assert(!c_list_is_empty(&list));
83 | assert(c_list_is_linked(&e1.link));
84 | assert(c_list_is_linked(&e2.link));
85 | assert(c_list_is_linked(&e3.link));
86 | assert(c_list_is_linked(&e4.link));
87 |
88 | /* remove via safe iterator */
89 |
90 | i = 0;
91 | c_list_for_each_safe(iter, safe, &list) {
92 | e = c_list_entry(iter, Entry, link);
93 | assert(i != 0 || e == &e1);
94 | assert(i != 1 || e == &e2);
95 | assert(i != 2 || e == &e3);
96 | assert(i != 3 || e == &e4);
97 | assert(i < 4);
98 | ++i;
99 | c_list_unlink(&e->link);
100 | }
101 | assert(i == 4);
102 |
103 | assert(c_list_is_empty(&list));
104 | assert(!c_list_is_linked(&e1.link));
105 | assert(!c_list_is_linked(&e2.link));
106 | assert(!c_list_is_linked(&e3.link));
107 | assert(!c_list_is_linked(&e4.link));
108 | }
109 |
110 | #if defined(__GNUC__) || defined(__clang__)
111 | static void test_entry_gnu(void) {
112 | CList list = C_LIST_INIT(list);
113 | Entry e1 = { .foo = 1 * 7, .bar = 1 * 11 };
114 | Entry e2 = { .foo = 2 * 7, .bar = 2 * 11 };
115 | Entry e3 = { .foo = 3 * 7, .bar = 3 * 11 };
116 | Entry e4 = { .foo = 4 * 7, .bar = 4 * 11 };
117 | Entry *e, *safe;
118 | size_t i;
119 |
120 | /* link entries and verify list state */
121 |
122 | c_list_link_tail(&list, &e1.link);
123 | c_list_link_tail(&list, &e2.link);
124 | c_list_link_tail(&list, &e3.link);
125 | c_list_link_tail(&list, &e4.link);
126 |
127 | i = 0;
128 | c_list_for_each_entry(e, &list, link) {
129 | assert(i != 0 || e == &e1);
130 | assert(i != 1 || e == &e2);
131 | assert(i != 2 || e == &e3);
132 | assert(i != 3 || e == &e4);
133 | assert(i < 4);
134 | ++i;
135 | }
136 | assert(i == 4);
137 |
138 | assert(!c_list_is_empty(&list));
139 | assert(c_list_is_linked(&e1.link));
140 | assert(c_list_is_linked(&e2.link));
141 | assert(c_list_is_linked(&e3.link));
142 | assert(c_list_is_linked(&e4.link));
143 |
144 | /* remove via safe iterator */
145 |
146 | i = 0;
147 | c_list_for_each_entry_safe(e, safe, &list, link) {
148 | assert(i != 0 || e == &e1);
149 | assert(i != 1 || e == &e2);
150 | assert(i != 2 || e == &e3);
151 | assert(i != 3 || e == &e4);
152 | assert(i < 4);
153 | ++i;
154 | c_list_unlink(&e->link);
155 | }
156 | assert(i == 4);
157 |
158 | assert(c_list_is_empty(&list));
159 | assert(!c_list_is_linked(&e1.link));
160 | assert(!c_list_is_linked(&e2.link));
161 | assert(!c_list_is_linked(&e3.link));
162 | assert(!c_list_is_linked(&e4.link));
163 | }
164 | #else
165 | static void test_entry_gnu(void) {
166 | }
167 | #endif
168 |
169 | int main(void) {
170 | test_entry();
171 | test_entry_gnu();
172 | return 0;
173 | }
174 |
--------------------------------------------------------------------------------