Squashed 'src/c-list/' changes from 96455db9f04a..a0970f12f1f4

a0970f12f1f4 c-list: avoid comma-operator in conditionals
2ea502c5ee83 c-list: return self from c_list_init()
ca473417f7b3 build: use c99 instead of c11
99d8daf152fa ci: run on windows-2016
857a37cfc960 test/embed: split off GNU-extensions
c09e29697912 ci: run through MSVC
c3ba3dc82466 build: run test_embed only with gcc/clang
77c872dcc67d test/api: split of GNU tests
3ab849ea658e test/basic: test_extensions() -> test_gnu()
04fda9508c86 c-list: reorder iterators
58ab8fb29472 test: add c_list_entry_offset() to API tests
6d2fdc76de70 build: update AUTHORS
92b893fb3c4b c-list: remove trailing '\\' from macro definitions
c255c6a97a04 c-list: use size_t
b47da33dedf1 c-list: use uintptr_t for pointer arithmetic in c_list_entry()
9b798f50bbd0 c-list: require CList pointer for argument to c_list_entry() macro
42bbf43ab0af test: guard gcc'ism
942fcfd80862 c-list: make _c_list_entry_eval() part of the API
73a620259ca0 c-list: don't use elvis operator for c_list_entry() macro
da5e122bd698 test: drop unused argc/argv
f1eadf27377e test: verify c_list_entry() does not double-evaluate

git-subtree-dir: src/c-list
git-subtree-split: a0970f12f1f406a5578a5dedf3580cd682e55812
This commit is contained in:
Thomas Haller
2021-10-01 16:17:09 +02:00
parent 59d9106df3
commit 7521ea44a3
7 changed files with 251 additions and 51 deletions

View File

@@ -19,3 +19,30 @@ jobs:
with: with:
m32: 1 m32: 1
valgrind: 1 valgrind: 1
ci-msvc:
name: CI with MSVC
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-2016, windows-latest]
steps:
- name: Fetch Sources
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install Python Dependencies
run: pip install meson ninja
- name: Prepare MSVC
uses: bus1/cabuild/action/msdevshell@v1
with:
architecture: x64
- name: Prepare Build
run: meson setup build
- name: Run Build
run: meson compile -v -C build
- name: Run Test Suite
run: meson test -v -C build

View File

@@ -33,6 +33,8 @@ COPYRIGHT: (ordered alphabetically)
Copyright (C) 2015-2019 Red Hat, Inc. Copyright (C) 2015-2019 Red Hat, Inc.
AUTHORS: (ordered alphabetically) AUTHORS: (ordered alphabetically)
Danilo Horta <danilo.horta@pm.me>
David Rheinsberg <david.rheinsberg@gmail.com> David Rheinsberg <david.rheinsberg@gmail.com>
Lucas De Marchi <lucas.de.marchi@gmail.com>
Thomas Haller <thaller@redhat.com> Thomas Haller <thaller@redhat.com>
Tom Gundersen <teg@jklm.no> Tom Gundersen <teg@jklm.no>

View File

@@ -4,7 +4,7 @@ project(
version: '3', version: '3',
license: 'Apache', license: 'Apache',
default_options: [ default_options: [
'c_std=c11', 'c_std=c99',
], ],
) )
project_description = 'Circular Intrusive Double Linked List Collection' project_description = 'Circular Intrusive Double Linked List Collection'

View File

@@ -22,6 +22,7 @@ extern "C" {
#endif #endif
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
typedef struct CList CList; typedef struct CList CList;
@@ -50,9 +51,42 @@ struct CList {
/** /**
* c_list_init() - initialize list entry * c_list_init() - initialize list entry
* @what: list entry to initialize * @what: list entry to initialize
*
* Return: @what is returned.
*/ */
static inline void c_list_init(CList *what) { static inline CList *c_list_init(CList *what) {
*what = (CList)C_LIST_INIT(*what); *what = (CList)C_LIST_INIT(*what);
return what;
}
/**
* c_list_entry_offset() - get parent container of list entry
* @what: list entry, or NULL
* @offset: offset of the list member in its surrounding type
*
* If the list entry @what is embedded into a surrounding structure, this will
* turn the list entry pointer @what into a pointer to the parent container
* (sometimes called container_of(3)). Use the `c_list_entry()` macro for an
* easier API.
*
* If @what is NULL, this will also return NULL.
*
* Return: Pointer to parent container, or NULL.
*/
static inline void *c_list_entry_offset(const CList *what, size_t offset) {
if (what) {
/*
* We allow calling "c_list_entry()" on the list head, which is
* commonly a plain CList struct. The returned entry pointer is
* thus invalid. For instance, this is used by the
* c_list_for_each_entry*() macros. Gcc correctly warns about that
* with "-Warray-bounds". However, as long as the value is never
* dereferenced, this is fine. We explicitly use integer arithmetic
* to circumvent the Gcc warning.
*/
return (void *)(((uintptr_t)(void *)what) - offset);
}
return NULL;
} }
/** /**
@@ -70,8 +104,7 @@ static inline void c_list_init(CList *what) {
* Return: Pointer to parent container, or NULL. * Return: Pointer to parent container, or NULL.
*/ */
#define c_list_entry(_what, _t, _m) \ #define c_list_entry(_what, _t, _m) \
((_t *)(void *)(((unsigned long)(void *)(_what) ?: \ ((_t *)c_list_entry_offset((_what), offsetof(_t, _m)))
offsetof(_t, _m)) - offsetof(_t, _m)))
/** /**
* c_list_is_linked() - check whether an entry is linked * c_list_is_linked() - check whether an entry is linked
@@ -306,32 +339,47 @@ static inline CList *c_list_last(CList *list) {
* state. * state.
*/ */
/* direct/raw iterators */
#define c_list_for_each(_iter, _list) \ #define c_list_for_each(_iter, _list) \
for (_iter = (_list)->next; \ for (_iter = (_list)->next; \
(_iter) != (_list); \ (_iter) != (_list); \
_iter = (_iter)->next) _iter = (_iter)->next)
#define c_list_for_each_entry(_iter, _list, _m) \
for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m); \
&(_iter)->_m != (_list); \
_iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m))
#define c_list_for_each_safe(_iter, _safe, _list) \ #define c_list_for_each_safe(_iter, _safe, _list) \
for (_iter = (_list)->next, _safe = (_iter)->next; \ for (_iter = (_list)->next, _safe = (_iter)->next; \
(_iter) != (_list); \ (_iter) != (_list); \
_iter = (_safe), _safe = (_safe)->next) _iter = (_safe), _safe = (_safe)->next)
#define c_list_for_each_continue(_iter, _list) \
for (_iter = (_iter) ? (_iter)->next : (_list)->next; \
(_iter) != (_list); \
_iter = (_iter)->next)
#define c_list_for_each_safe_continue(_iter, _safe, _list) \
for (_iter = (_iter) ? (_iter)->next : (_list)->next, \
_safe = (_iter)->next; \
(_iter) != (_list); \
_iter = (_safe), _safe = (_safe)->next)
#define c_list_for_each_safe_unlink(_iter, _safe, _list) \
for (_iter = (_list)->next, _safe = (_iter)->next; \
c_list_init(_iter) != (_list); \
_iter = (_safe), _safe = (_safe)->next)
/* c_list_entry() based iterators */
#define c_list_for_each_entry(_iter, _list, _m) \
for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m); \
&(_iter)->_m != (_list); \
_iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m))
#define c_list_for_each_entry_safe(_iter, _safe, _list, _m) \ #define c_list_for_each_entry_safe(_iter, _safe, _list, _m) \
for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \ for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \
_safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \
&(_iter)->_m != (_list); \ &(_iter)->_m != (_list); \
_iter = (_safe), \ _iter = (_safe), \
_safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) \ _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m))
#define c_list_for_each_continue(_iter, _list) \
for (_iter = (_iter) ? (_iter)->next : (_list)->next; \
(_iter) != (_list); \
_iter = (_iter)->next)
#define c_list_for_each_entry_continue(_iter, _list, _m) \ #define c_list_for_each_entry_continue(_iter, _list, _m) \
for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \ for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \
@@ -340,12 +388,6 @@ static inline CList *c_list_last(CList *list) {
&(_iter)->_m != (_list); \ &(_iter)->_m != (_list); \
_iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m)) _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m))
#define c_list_for_each_safe_continue(_iter, _safe, _list) \
for (_iter = (_iter) ? (_iter)->next : (_list)->next, \
_safe = (_iter)->next; \
(_iter) != (_list); \
_iter = (_safe), _safe = (_safe)->next)
#define c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m) \ #define c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m) \
for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \ for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \
__typeof__(*_iter), \ __typeof__(*_iter), \
@@ -353,20 +395,14 @@ static inline CList *c_list_last(CList *list) {
_safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \
&(_iter)->_m != (_list); \ &(_iter)->_m != (_list); \
_iter = (_safe), \ _iter = (_safe), \
_safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) \ _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m))
#define c_list_for_each_safe_unlink(_iter, _safe, _list) \
for (_iter = (_list)->next, _safe = (_iter)->next; \
((*_iter = (CList)C_LIST_INIT(*_iter)), (_iter) != (_list)); \
_iter = (_safe), _safe = (_safe)->next)
#define c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m) \ #define c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m) \
for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \ for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \
_safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \
(((_iter)->_m = (CList)C_LIST_INIT((_iter)->_m)), \ c_list_init(&(_iter)->_m) != (_list); \
&(_iter)->_m != (_list)); \
_iter = (_safe), \ _iter = (_safe), \
_safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) \ _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m))
/** /**
* c_list_flush() - flush all entries from a list * c_list_flush() - flush all entries from a list
@@ -402,8 +438,8 @@ static inline void c_list_flush(CList *list) {
* *
* Return: Number of items in @list. * Return: Number of items in @list.
*/ */
static inline unsigned long c_list_length(const CList *list) { static inline size_t c_list_length(const CList *list) {
unsigned long n = 0; size_t n = 0;
const CList *iter; const CList *iter;
c_list_for_each(iter, list) c_list_for_each(iter, list)

View File

@@ -18,8 +18,11 @@ typedef struct {
static void test_api(void) { static void test_api(void) {
CList *list_iter, *list_safe, list = C_LIST_INIT(list); CList *list_iter, *list_safe, list = C_LIST_INIT(list);
Node *node_iter, *node_safe, node = { .id = 0, .link = C_LIST_INIT(node.link) }; 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(NULL, Node, link));
assert(c_list_entry(&node.link, Node, link) == &node); assert(c_list_entry(&node.link, Node, link) == &node);
assert(!c_list_is_linked(&node.link)); assert(!c_list_is_linked(&node.link));
@@ -73,28 +76,24 @@ static void test_api(void) {
c_list_splice(&list, &list); c_list_splice(&list, &list);
assert(c_list_is_empty(&list)); assert(c_list_is_empty(&list));
/* loop macros */ /* direct/raw iterators */
c_list_for_each(list_iter, &list) c_list_for_each(list_iter, &list)
assert(list_iter != &list); assert(list_iter != &list);
c_list_for_each_entry(node_iter, &list, link)
assert(&node_iter->link != &list);
c_list_for_each_safe(list_iter, list_safe, &list) c_list_for_each_safe(list_iter, list_safe, &list)
assert(list_iter != &list); assert(list_iter != &list);
c_list_for_each_entry_safe(node_iter, node_safe, &list, link)
assert(&node_iter->link != &list); list_iter = NULL;
c_list_for_each_continue(list_iter, &list) c_list_for_each_continue(list_iter, &list)
assert(list_iter != &list); assert(list_iter != &list);
c_list_for_each_entry_continue(node_iter, &list, link)
assert(&node_iter->link != &list); list_iter = NULL;
c_list_for_each_safe_continue(list_iter, list_safe, &list) c_list_for_each_safe_continue(list_iter, list_safe, &list)
assert(list_iter != &list); assert(list_iter != &list);
c_list_for_each_entry_safe_continue(node_iter, node_safe, &list, link)
assert(&node_iter->link != &list);
c_list_for_each_safe_unlink(list_iter, list_safe, &list) c_list_for_each_safe_unlink(list_iter, list_safe, &list)
assert(list_iter != &list); assert(list_iter != &list);
c_list_for_each_entry_safe_unlink(node_iter, node_safe, &list, link)
assert(&node_iter->link != &list);
/* list accessors */ /* list accessors */
@@ -104,7 +103,37 @@ static void test_api(void) {
assert(!c_list_last_entry(&list, Node, link)); assert(!c_list_last_entry(&list, Node, link));
} }
int main(int argc, char **argv) { #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();
test_api_gnu();
return 0; return 0;
} }

View File

@@ -160,10 +160,46 @@ static void test_splice(void) {
static void test_flush(void) { static void test_flush(void) {
CList e1 = C_LIST_INIT(e1), e2 = C_LIST_INIT(e2); 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. */
{ {
__attribute__((__cleanup__(c_list_flush))) CList list1 = C_LIST_INIT(list1); struct TestList {
__attribute__((__cleanup__(c_list_flush))) CList list2 = C_LIST_INIT(list2); 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, &e1);
c_list_link_tail(&list2, &e2); c_list_link_tail(&list2, &e2);
@@ -175,11 +211,17 @@ static void test_flush(void) {
assert(!c_list_is_linked(&e1)); assert(!c_list_is_linked(&e1));
assert(!c_list_is_linked(&e2)); assert(!c_list_is_linked(&e2));
} }
#else
static void test_gnu(void) {
}
#endif
int main(int argc, char **argv) { int main(void) {
test_iterators(); test_iterators();
test_swap(); test_swap();
test_splice(); test_splice();
test_flush(); test_flush();
test_macros();
test_gnu();
return 0; return 0;
} }

View File

@@ -23,7 +23,8 @@ static void test_entry(void) {
Entry e2 = { .foo = 2 * 7, .bar = 2 * 11 }; Entry e2 = { .foo = 2 * 7, .bar = 2 * 11 };
Entry e3 = { .foo = 3 * 7, .bar = 3 * 11 }; Entry e3 = { .foo = 3 * 7, .bar = 3 * 11 };
Entry e4 = { .foo = 4 * 7, .bar = 4 * 11 }; Entry e4 = { .foo = 4 * 7, .bar = 4 * 11 };
Entry *e, *safe; Entry *e;
CList *iter, *safe;
size_t i; size_t i;
/* verify c_list_entry() works as expected (even with NULL) */ /* verify c_list_entry() works as expected (even with NULL) */
@@ -47,7 +48,8 @@ static void test_entry(void) {
assert(c_list_last_entry(&list, Entry, link)->bar == 2 * 11); assert(c_list_last_entry(&list, Entry, link)->bar == 2 * 11);
i = 0; i = 0;
c_list_for_each_entry(e, &list, link) { c_list_for_each(iter, &list) {
e = c_list_entry(iter, Entry, link);
assert(i != 0 || e == &e1); assert(i != 0 || e == &e1);
assert(i != 1 || e == &e2); assert(i != 1 || e == &e2);
assert(i < 2); assert(i < 2);
@@ -65,6 +67,63 @@ static void test_entry(void) {
assert(c_list_last_entry(&list, Entry, link)->foo == 4 * 7); assert(c_list_last_entry(&list, Entry, link)->foo == 4 * 7);
assert(c_list_last_entry(&list, Entry, link)->bar == 4 * 11); 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; i = 0;
c_list_for_each_entry(e, &list, link) { c_list_for_each_entry(e, &list, link) {
assert(i != 0 || e == &e1); assert(i != 0 || e == &e1);
@@ -102,8 +161,13 @@ static void test_entry(void) {
assert(!c_list_is_linked(&e3.link)); assert(!c_list_is_linked(&e3.link));
assert(!c_list_is_linked(&e4.link)); assert(!c_list_is_linked(&e4.link));
} }
#else
static void test_entry_gnu(void) {
}
#endif
int main(int argc, char **argv) { int main(void) {
test_entry(); test_entry();
test_entry_gnu();
return 0; return 0;
} }