diff options
Diffstat (limited to 'src/c-list')
| -rw-r--r-- | src/c-list/.editorconfig | 11 | ||||
| -rw-r--r-- | src/c-list/.github/workflows/ci.yml | 32 | ||||
| -rw-r--r-- | src/c-list/AUTHORS | 41 | ||||
| -rw-r--r-- | src/c-list/NEWS.md | 77 | ||||
| -rw-r--r-- | src/c-list/README.md | 52 | ||||
| -rw-r--r-- | src/c-list/meson.build | 46 | ||||
| -rw-r--r-- | src/c-list/src/meson.build | 34 | ||||
| -rw-r--r-- | src/c-list/src/test-api.c | 144 | ||||
| -rw-r--r-- | src/c-list/src/test-basic.c | 319 | ||||
| -rw-r--r-- | src/c-list/src/test-embed.c | 173 |
10 files changed, 929 insertions, 0 deletions
diff --git a/src/c-list/.editorconfig b/src/c-list/.editorconfig new file mode 100644 index 00000000..b10bb4f3 --- /dev/null +++ b/src/c-list/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +[*.{c,h}] +indent_style = space +indent_size = 8 diff --git a/src/c-list/.github/workflows/ci.yml b/src/c-list/.github/workflows/ci.yml new file mode 100644 index 00000000..00bca960 --- /dev/null +++ b/src/c-list/.github/workflows/ci.yml @@ -0,0 +1,32 @@ +name: Continuous Integration + +on: + push: + pull_request: + schedule: + - cron: '0 0 * * *' + +jobs: + ci-linux: + name: Linux CI + uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1 + with: + cabuild_ref: "v1" + linux: true + m32: true + matrixmode: true + valgrind: true + ci-macos: + name: MacOS CI + uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1 + with: + cabuild_ref: "v1" + linux: false + macos: true + ci-windows: + name: Windows CI + uses: bus1/cabuild/.github/workflows/ci-c-util.yml@v1 + with: + cabuild_ref: "v1" + linux: false + windows: true diff --git a/src/c-list/AUTHORS b/src/c-list/AUTHORS new file mode 100644 index 00000000..76dea872 --- /dev/null +++ b/src/c-list/AUTHORS @@ -0,0 +1,41 @@ +LICENSE: + This project is dual-licensed under both the Apache License, Version + 2.0, and the GNU Lesser General Public License, Version 2.1+. + +AUTHORS-ASL: + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +AUTHORS-LGPL: + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; If not, see <http://www.gnu.org/licenses/>. + +COPYRIGHT: (ordered alphabetically) + Copyright (C) 2015-2022 Red Hat, Inc. + +AUTHORS: (ordered alphabetically) + Danilo Horta <danilo.horta@pm.me> + David Rheinsberg <david.rheinsberg@gmail.com> + Lucas De Marchi <lucas.de.marchi@gmail.com> + Michele Dionisio + Thomas Haller <thaller@redhat.com> + Tom Gundersen <teg@jklm.no> diff --git a/src/c-list/NEWS.md b/src/c-list/NEWS.md new file mode 100644 index 00000000..095f1ea7 --- /dev/null +++ b/src/c-list/NEWS.md @@ -0,0 +1,77 @@ +# c-list - Circular Intrusive Double Linked List Collection + +## CHANGES WITH 3.1.0: + + * The minimum required meson version is now 0.60.0. + + * New function c_list_split() is added. It reverses c_list_splice() + and thus allows to split a list in half. + + Contributions from: David Rheinsberg, Michele Dionisio + + - Brno, 2022-06-22 + +## CHANGES WITH 3: + + * API break: The c_list_loop_*() symbols were removed, since we saw + little use for them. No user was known at the time, so + all projects should build with the new API version + unchanged. + Since c-list does not distribute any compiled code, there + is no ABI issue with this change. + + * Two new symbols c_list_length() and c_list_contains(). They are meant + for debugging purposes, to easily verify list integrity. Since they + run in O(n) time, they are not recommended for any other use than + debugging. + + * New symbol c_list_init() is provided as alternative to the verbose + C_LIST_INIT assignment. + + * The c-list API is extended to work well with `const CList` objects. + That is, any read-only accessor function allows constant objects as + input now. + Note that this does not propagate into other members linked in the + list. Using `const` for CList members is of little practical use. + However, it might be of use for its embedding objects, so we now + allow it in the CList API as well. + + * The c_list_splice() call now clears the source list, rather than + returning with stale pointers. Technically, this is also an API + break, but unlikely to affect any existing code. + + Contributions from: David Herrmann, Thomas Haller + + - Berlin, 2017-08-13 + +## CHANGES WITH 2: + + * Adjust project-name in build-system to reflect the actual project. The + previous releases incorrectly claimed to be c-rbtree in the build + system. + + * Add c_list_swap() that swaps two lists given their head pointers. + + * Add c_list_splice() that moves a list. + + * Add LGPL2.1+ as license so c-list can be imported into GPL2 projects. + It is now officially dual-licensed. + + * As usual a bunch of fixes, additional tests, and documentation + updates. + + Contributions from: David Herrmann, Tom Gundersen + + - Lund, 2017-05-03 + +## CHANGES WITH 1: + + * Initial release of c-list. + + * This project provides an implementation of a circular double linked + list in standard ISO-C11. License is ASL-2.0 and the build system + used is `Meson'. + + Contributions from: David Herrmann, Tom Gundersen + + - Berlin, 2017-03-03 diff --git a/src/c-list/README.md b/src/c-list/README.md new file mode 100644 index 00000000..a2e47528 --- /dev/null +++ b/src/c-list/README.md @@ -0,0 +1,52 @@ +c-list +====== + +Circular Intrusive Double Linked List Collection + +The c-list project implements an intrusive collection based on circular double +linked lists in ISO-C11. It aims for minimal API constraints, leaving maximum +control over the data-structures to the API consumer. + +### Project + + * **Website**: <https://c-util.github.io/c-list> + * **Bug Tracker**: <https://github.com/c-util/c-list/issues> + +### Requirements + +The requirements for this project are: + + * `libc` (e.g., `glibc >= 2.16`) + +At build-time, the following software is required: + + * `meson >= 0.60` + * `pkg-config >= 0.29` + +### Build + +The meson build-system is used for this project. Contact upstream +documentation for detailed help. In most situations the following +commands are sufficient to build and install from source: + +```sh +mkdir build +cd build +meson setup .. +ninja +meson test +ninja install +``` + +No custom configuration options are available. + +### Repository: + + - **web**: <https://github.com/c-util/c-list> + - **https**: `https://github.com/c-util/c-list.git` + - **ssh**: `git@github.com:c-util/c-list.git` + +### License: + + - **Apache-2.0** OR **LGPL-2.1-or-later** + - See AUTHORS file for details. diff --git a/src/c-list/meson.build b/src/c-list/meson.build new file mode 100644 index 00000000..b5ef78e6 --- /dev/null +++ b/src/c-list/meson.build @@ -0,0 +1,46 @@ +project( + 'c-list', + 'c', + default_options: [ + 'c_std=c99', + ], + license: 'Apache', + meson_version: '>=0.60.0', + version: '3.1.0', +) +major = meson.project_version().split('.')[0] +project_description = 'Circular Intrusive Double Linked List Collection' + +mod_pkgconfig = import('pkgconfig') + +# See c-stdaux for details on these. We do not have c-stdaux as dependency, so +# we keep a duplicated set here, reduced to the minimum. +cflags = meson.get_compiler('c').get_supported_arguments( + '-D_GNU_SOURCE', + + '-Wno-gnu-alignof-expression', + '-Wno-maybe-uninitialized', + '-Wno-unknown-warning-option', + '-Wno-unused-parameter', + + '-Wno-error=type-limits', + '-Wno-error=missing-field-initializers', + + '-Wdate-time', + '-Wdeclaration-after-statement', + '-Wlogical-op', + '-Wmissing-include-dirs', + '-Wmissing-noreturn', + '-Wnested-externs', + '-Wredundant-decls', + '-Wshadow', + '-Wstrict-aliasing=3', + '-Wsuggest-attribute=noreturn', + '-Wundef', + '-Wwrite-strings', +) +add_project_arguments(cflags, language: 'c') + +subdir('src') + +meson.override_dependency('libclist-'+major, libclist_dep, static: true) diff --git a/src/c-list/src/meson.build b/src/c-list/src/meson.build new file mode 100644 index 00000000..ec7f29d5 --- /dev/null +++ b/src/c-list/src/meson.build @@ -0,0 +1,34 @@ +# +# target: libclist.so +# (No .so is built so far, since we are header-only. This might change in the +# future, if we add more complex list helpers.) +# + +libclist_dep = declare_dependency( + include_directories: include_directories('.'), + version: meson.project_version(), +) + +if not meson.is_subproject() + install_headers('c-list.h') + + mod_pkgconfig.generate( + description: project_description, + filebase: 'libclist-'+major, + name: 'libclist', + version: meson.project_version(), + ) +endif + +# +# target: test-* +# + +test_api = executable('test-api', ['test-api.c'], dependencies: libclist_dep) +test('API Symbol Visibility', test_api) + +test_basic = executable('test-basic', ['test-basic.c'], dependencies: libclist_dep) +test('Basic API Behavior', test_basic) + +test_embed = executable('test-embed', ['test-embed.c'], dependencies: libclist_dep) +test('Embedded List Nodes', test_embed) diff --git a/src/c-list/src/test-api.c b/src/c-list/src/test-api.c new file mode 100644 index 00000000..864d198a --- /dev/null +++ b/src/c-list/src/test-api.c @@ -0,0 +1,144 @@ +/* + * Tests for Public API + * This test, unlikely the others, is linked against the real, distributed, + * shared library. Its sole purpose is to test for symbol availability. + */ + +#undef NDEBUG +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "c-list.h" + +typedef struct { + int id; + CList link; +} Node; + +static void test_api(void) { + CList *list_iter, *list_safe; + CList list = C_LIST_INIT(list), list2 = C_LIST_INIT(list2); + Node node = { .id = 0, .link = C_LIST_INIT(node.link) }; + + assert(c_list_init(&list) == &list); + assert(!c_list_entry_offset(NULL, 0)); + assert(!c_list_entry_offset(NULL, offsetof(Node, link))); + assert(!c_list_entry(NULL, Node, link)); + assert(c_list_entry(&node.link, Node, link) == &node); + assert(!c_list_is_linked(&node.link)); + assert(c_list_is_empty(&list)); + assert(c_list_length(&list) == 0); + assert(c_list_contains(&list, &list)); + assert(!c_list_contains(&list, &node.link)); + c_list_flush(&list); + + /* basic link / unlink calls */ + + c_list_link_before(&list, &node.link); + assert(c_list_is_linked(&node.link)); + assert(!c_list_is_empty(&list)); + assert(c_list_length(&list) == 1); + assert(c_list_contains(&list, &list)); + assert(c_list_contains(&list, &node.link)); + + c_list_unlink_stale(&node.link); + assert(c_list_is_linked(&node.link)); + assert(c_list_is_empty(&list)); + assert(c_list_length(&list) == 0); + + c_list_link_after(&list, &node.link); + assert(c_list_is_linked(&node.link)); + assert(!c_list_is_empty(&list)); + + c_list_unlink(&node.link); + assert(!c_list_is_linked(&node.link)); + assert(c_list_is_empty(&list)); + + /* link / unlink aliases */ + + c_list_link_front(&list, &node.link); + assert(c_list_is_linked(&node.link)); + + c_list_unlink(&node.link); + assert(!c_list_is_linked(&node.link)); + + c_list_link_tail(&list, &node.link); + assert(c_list_is_linked(&node.link)); + + c_list_unlink(&node.link); + assert(!c_list_is_linked(&node.link)); + + /* swap / splice / split list operators */ + + c_list_swap(&list, &list); + assert(c_list_is_empty(&list)); + + c_list_splice(&list, &list); + assert(c_list_is_empty(&list)); + + c_list_split(&list, &list, &list2); + assert(c_list_is_empty(&list)); + assert(c_list_is_empty(&list2)); + + /* direct/raw iterators */ + + c_list_for_each(list_iter, &list) + assert(list_iter != &list); + + c_list_for_each_safe(list_iter, list_safe, &list) + assert(list_iter != &list); + + list_iter = NULL; + c_list_for_each_continue(list_iter, &list) + assert(list_iter != &list); + + list_iter = NULL; + c_list_for_each_safe_continue(list_iter, list_safe, &list) + assert(list_iter != &list); + + c_list_for_each_safe_unlink(list_iter, list_safe, &list) + assert(list_iter != &list); + + /* list accessors */ + + assert(!c_list_first(&list)); + assert(!c_list_last(&list)); + assert(!c_list_first_entry(&list, Node, link)); + assert(!c_list_last_entry(&list, Node, link)); +} + +#if defined(__GNUC__) || defined(__clang__) +static void test_api_gnu(void) { + CList list = C_LIST_INIT(list); + Node *node_iter, *node_safe; + + /* c_list_entry() based iterators */ + + c_list_for_each_entry(node_iter, &list, link) + assert(&node_iter->link != &list); + + c_list_for_each_entry_safe(node_iter, node_safe, &list, link) + assert(&node_iter->link != &list); + + node_iter = NULL; + c_list_for_each_entry_continue(node_iter, &list, link) + assert(&node_iter->link != &list); + + node_iter = NULL; + c_list_for_each_entry_safe_continue(node_iter, node_safe, &list, link) + assert(&node_iter->link != &list); + + c_list_for_each_entry_safe_unlink(node_iter, node_safe, &list, link) + assert(&node_iter->link != &list); +} +#else +static void test_api_gnu(void) { +} +#endif + +int main(void) { + test_api(); + test_api_gnu(); + return 0; +} diff --git a/src/c-list/src/test-basic.c b/src/c-list/src/test-basic.c new file mode 100644 index 00000000..58ed8636 --- /dev/null +++ b/src/c-list/src/test-basic.c @@ -0,0 +1,319 @@ +/* + * Tests for basic functionality + * This contains basic, deterministic tests for list behavior, API + * functionality, and usage. + */ + +#undef NDEBUG +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "c-list.h" + +static void assert_list_integrity(CList *list) { + CList *iter; + + iter = list; + do { + assert(iter->next->prev == iter); + assert(iter->prev->next == iter); + + iter = iter->next; + } while (iter != list); +} + +static void test_iterators(void) { + CList *iter, *safe, a, b, list = C_LIST_INIT(list); + unsigned int i; + + assert(!c_list_first(&list)); + assert(!c_list_last(&list)); + + /* link @a and verify iterators see just it */ + + c_list_link_tail(&list, &a); + assert(c_list_is_linked(&a)); + assert(c_list_first(&list) == &a); + assert(c_list_last(&list) == &a); + + i = 0; + c_list_for_each(iter, &list) { + assert(iter == &a); + ++i; + } + assert(i == 1); + + i = 0; + iter = NULL; + c_list_for_each_continue(iter, &list) { + assert(iter == &a); + ++i; + } + assert(i == 1); + + i = 0; + iter = &a; + c_list_for_each_continue(iter, &list) + ++i; + assert(i == 0); + + /* link @b as well and verify iterators again */ + + c_list_link_tail(&list, &b); + assert(c_list_is_linked(&a)); + assert(c_list_is_linked(&b)); + + i = 0; + c_list_for_each(iter, &list) { + assert((i == 0 && iter == &a) || + (i == 1 && iter == &b)); + ++i; + } + assert(i == 2); + + i = 0; + iter = NULL; + c_list_for_each_continue(iter, &list) { + assert((i == 0 && iter == &a) || + (i == 1 && iter == &b)); + ++i; + } + assert(i == 2); + + i = 0; + iter = &a; + c_list_for_each_continue(iter, &list) { + assert(iter == &b); + ++i; + } + assert(i == 1); + + i = 0; + iter = &b; + c_list_for_each_continue(iter, &list) + ++i; + assert(i == 0); + + /* verify safe-iterator while removing elements */ + + i = 0; + c_list_for_each_safe(iter, safe, &list) { + assert(iter == &a || iter == &b); + c_list_unlink_stale(iter); + ++i; + } + assert(i == 2); + + assert(c_list_is_empty(&list)); + + /* link both and verify *_unlink() iterators */ + + c_list_link_tail(&list, &a); + c_list_link_tail(&list, &b); + + i = 0; + c_list_for_each_safe_unlink(iter, safe, &list) { + assert(iter == &a || iter == &b); + assert(!c_list_is_linked(iter)); + ++i; + } + assert(i == 2); + + assert(c_list_is_empty(&list)); +} + +static void test_swap(void) { + CList list1 = (CList)C_LIST_INIT(list1); + CList list2 = (CList)C_LIST_INIT(list2); + CList list; + + c_list_swap(&list1, &list2); + + assert(list1.prev == list1.next && list1.prev == &list1); + assert(list2.prev == list2.next && list2.prev == &list2); + + c_list_link_tail(&list1, &list); + + assert(c_list_first(&list1) == &list); + assert(c_list_last(&list1) == &list); + assert(list.next == &list1); + assert(list.prev == &list1); + + c_list_swap(&list1, &list2); + + assert(c_list_first(&list2) == &list); + assert(c_list_last(&list2) == &list); + assert(list.next == &list2); + assert(list.prev == &list2); + + assert(list1.prev == list1.next && list1.prev == &list1); +} + +static void test_splice(void) { + CList target = (CList)C_LIST_INIT(target); + CList source = (CList)C_LIST_INIT(source); + CList e1, e2; + + c_list_link_tail(&source, &e1); + + c_list_splice(&target, &source); + assert(c_list_first(&target) == &e1); + assert(c_list_last(&target) == &e1); + + source = (CList)C_LIST_INIT(source); + + c_list_link_tail(&source, &e2); + + c_list_splice(&target, &source); + assert(c_list_first(&target) == &e1); + assert(c_list_last(&target) == &e2); +} + +static void test_split(void) { + CList e1, e2; + + /* split empty list */ + { + CList source = C_LIST_INIT(source), target; + + c_list_split(&source, &source, &target); + assert(c_list_is_empty(&source)); + assert(c_list_is_empty(&target)); + assert_list_integrity(&source); + assert_list_integrity(&target); + } + + /* split 1-element list excluding the element */ + { + CList source = C_LIST_INIT(source), target; + + c_list_link_tail(&source, &e1); + c_list_split(&source, &source, &target); + assert(!c_list_is_empty(&source)); + assert(c_list_is_empty(&target)); + assert_list_integrity(&source); + assert_list_integrity(&target); + } + + /* split 1-element list including the element */ + { + CList source = C_LIST_INIT(source), target; + + c_list_link_tail(&source, &e1); + c_list_split(&source, &e1, &target); + assert(c_list_is_empty(&source)); + assert(!c_list_is_empty(&target)); + assert_list_integrity(&source); + assert_list_integrity(&target); + } + + /* split 2-element list excluding the elements */ + { + CList source = C_LIST_INIT(source), target; + + c_list_link_tail(&source, &e1); + c_list_link_tail(&source, &e2); + c_list_split(&source, &source, &target); + assert(!c_list_is_empty(&source)); + assert(c_list_is_empty(&target)); + assert_list_integrity(&source); + assert_list_integrity(&target); + } + + /* split 2-element list including one element */ + { + CList source = C_LIST_INIT(source), target; + + c_list_link_tail(&source, &e1); + c_list_link_tail(&source, &e2); + c_list_split(&source, &e2, &target); + assert(!c_list_is_empty(&source)); + assert(!c_list_is_empty(&target)); + assert_list_integrity(&source); + assert_list_integrity(&target); + } + + /* split 2-element list including both elements */ + { + CList source = C_LIST_INIT(source), target; + + c_list_link_tail(&source, &e1); + c_list_link_tail(&source, &e2); + c_list_split(&source, &e1, &target); + assert(c_list_is_empty(&source)); + assert(!c_list_is_empty(&target)); + assert_list_integrity(&source); + assert_list_integrity(&target); + } +} + + +static void test_flush(void) { + CList e1 = C_LIST_INIT(e1), e2 = C_LIST_INIT(e2); + CList list1 = C_LIST_INIT(list1), list2 = C_LIST_INIT(list2); + + c_list_link_tail(&list2, &e1); + c_list_link_tail(&list2, &e2); + + assert(c_list_is_linked(&e1)); + assert(c_list_is_linked(&e2)); + + c_list_flush(&list1); + c_list_flush(&list2); + + assert(!c_list_is_linked(&e1)); + assert(!c_list_is_linked(&e2)); +} + +static void test_macros(void) { + /* Verify `c_list_entry()` evaluates arguments only once. */ + { + struct TestList { + int a; + CList link; + int b; + } list = { .link = C_LIST_INIT(list.link) }; + CList *p[2] = { &list.link, NULL }; + unsigned int i = 0; + + assert(i == 0); + assert(c_list_entry(p[i++], struct TestList, link) == &list); + assert(i == 1); + } +} + +#if defined(__GNUC__) || defined(__clang__) +static void test_gnu(void) { + CList e1 = C_LIST_INIT(e1), e2 = C_LIST_INIT(e2); + + /* Test `c_list_flush()` in combination with cleanup attributes. */ + { + __attribute((cleanup(c_list_flush))) CList list1 = C_LIST_INIT(list1); + __attribute((cleanup(c_list_flush))) CList list2 = C_LIST_INIT(list2); + + c_list_link_tail(&list2, &e1); + c_list_link_tail(&list2, &e2); + + assert(c_list_is_linked(&e1)); + assert(c_list_is_linked(&e2)); + } + + assert(!c_list_is_linked(&e1)); + assert(!c_list_is_linked(&e2)); +} +#else +static void test_gnu(void) { +} +#endif + +int main(void) { + test_iterators(); + test_swap(); + test_splice(); + test_split(); + test_flush(); + test_macros(); + test_gnu(); + return 0; +} diff --git a/src/c-list/src/test-embed.c b/src/c-list/src/test-embed.c new file mode 100644 index 00000000..7ee6ff01 --- /dev/null +++ b/src/c-list/src/test-embed.c @@ -0,0 +1,173 @@ +/* + * Tests for embedded CList members + */ + +#undef NDEBUG +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "c-list.h" + +typedef struct Entry Entry; + +struct Entry { + short foo; + CList link; + short bar; +}; + +static void test_entry(void) { + CList list = C_LIST_INIT(list); + Entry e1 = { .foo = 1 * 7, .bar = 1 * 11 }; + Entry e2 = { .foo = 2 * 7, .bar = 2 * 11 }; + Entry e3 = { .foo = 3 * 7, .bar = 3 * 11 }; + Entry e4 = { .foo = 4 * 7, .bar = 4 * 11 }; + Entry *e; + CList *iter, *safe; + size_t i; + + /* verify c_list_entry() works as expected (even with NULL) */ + + assert(!c_list_entry(NULL, Entry, link)); + assert(&e1 == c_list_entry(&e1.link, Entry, link)); + + /* verify @list is empty */ + + assert(!c_list_first_entry(&list, Entry, link)); + assert(!c_list_last_entry(&list, Entry, link)); + + /* link 2 entries and verify list state */ + + c_list_link_tail(&list, &e1.link); + c_list_link_tail(&list, &e2.link); + + assert(c_list_first_entry(&list, Entry, link)->foo == 1 * 7); + assert(c_list_first_entry(&list, Entry, link)->bar == 1 * 11); + assert(c_list_last_entry(&list, Entry, link)->foo == 2 * 7); + assert(c_list_last_entry(&list, Entry, link)->bar == 2 * 11); + + i = 0; + c_list_for_each(iter, &list) { + e = c_list_entry(iter, Entry, link); + assert(i != 0 || e == &e1); + assert(i != 1 || e == &e2); + assert(i < 2); + ++i; + } + assert(i == 2); + + /* link 2 more entries */ + + c_list_link_tail(&list, &e3.link); + c_list_link_tail(&list, &e4.link); + + assert(c_list_first_entry(&list, Entry, link)->foo == 1 * 7); + assert(c_list_first_entry(&list, Entry, link)->bar == 1 * 11); + assert(c_list_last_entry(&list, Entry, link)->foo == 4 * 7); + assert(c_list_last_entry(&list, Entry, link)->bar == 4 * 11); + + i = 0; + c_list_for_each(iter, &list) { + e = c_list_entry(iter, Entry, link); + assert(i != 0 || e == &e1); + assert(i != 1 || e == &e2); + assert(i != 2 || e == &e3); + assert(i != 3 || e == &e4); + assert(i < 4); + ++i; + } + assert(i == 4); + + assert(!c_list_is_empty(&list)); + assert(c_list_is_linked(&e1.link)); + assert(c_list_is_linked(&e2.link)); + assert(c_list_is_linked(&e3.link)); + assert(c_list_is_linked(&e4.link)); + + /* remove via safe iterator */ + + i = 0; + c_list_for_each_safe(iter, safe, &list) { + e = c_list_entry(iter, Entry, link); + assert(i != 0 || e == &e1); + assert(i != 1 || e == &e2); + assert(i != 2 || e == &e3); + assert(i != 3 || e == &e4); + assert(i < 4); + ++i; + c_list_unlink(&e->link); + } + assert(i == 4); + + assert(c_list_is_empty(&list)); + assert(!c_list_is_linked(&e1.link)); + assert(!c_list_is_linked(&e2.link)); + assert(!c_list_is_linked(&e3.link)); + assert(!c_list_is_linked(&e4.link)); +} + +#if defined(__GNUC__) || defined(__clang__) +static void test_entry_gnu(void) { + CList list = C_LIST_INIT(list); + Entry e1 = { .foo = 1 * 7, .bar = 1 * 11 }; + Entry e2 = { .foo = 2 * 7, .bar = 2 * 11 }; + Entry e3 = { .foo = 3 * 7, .bar = 3 * 11 }; + Entry e4 = { .foo = 4 * 7, .bar = 4 * 11 }; + Entry *e, *safe; + size_t i; + + /* link entries and verify list state */ + + c_list_link_tail(&list, &e1.link); + c_list_link_tail(&list, &e2.link); + c_list_link_tail(&list, &e3.link); + c_list_link_tail(&list, &e4.link); + + i = 0; + c_list_for_each_entry(e, &list, link) { + assert(i != 0 || e == &e1); + assert(i != 1 || e == &e2); + assert(i != 2 || e == &e3); + assert(i != 3 || e == &e4); + assert(i < 4); + ++i; + } + assert(i == 4); + + assert(!c_list_is_empty(&list)); + assert(c_list_is_linked(&e1.link)); + assert(c_list_is_linked(&e2.link)); + assert(c_list_is_linked(&e3.link)); + assert(c_list_is_linked(&e4.link)); + + /* remove via safe iterator */ + + i = 0; + c_list_for_each_entry_safe(e, safe, &list, link) { + assert(i != 0 || e == &e1); + assert(i != 1 || e == &e2); + assert(i != 2 || e == &e3); + assert(i != 3 || e == &e4); + assert(i < 4); + ++i; + c_list_unlink(&e->link); + } + assert(i == 4); + + assert(c_list_is_empty(&list)); + assert(!c_list_is_linked(&e1.link)); + assert(!c_list_is_linked(&e2.link)); + assert(!c_list_is_linked(&e3.link)); + assert(!c_list_is_linked(&e4.link)); +} +#else +static void test_entry_gnu(void) { +} +#endif + +int main(void) { + test_entry(); + test_entry_gnu(); + return 0; +} |