diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b40abf690..76a41592d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,3 +19,30 @@ jobs: with: m32: 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 diff --git a/AUTHORS b/AUTHORS index 2327c052d..439473ced 100644 --- a/AUTHORS +++ b/AUTHORS @@ -33,6 +33,8 @@ COPYRIGHT: (ordered alphabetically) Copyright (C) 2015-2019 Red Hat, Inc. AUTHORS: (ordered alphabetically) + Danilo Horta David Rheinsberg + Lucas De Marchi Thomas Haller Tom Gundersen diff --git a/meson.build b/meson.build index 66504a29e..21bb784f1 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,7 @@ project( version: '3', license: 'Apache', default_options: [ - 'c_std=c11', + 'c_std=c99', ], ) project_description = 'Circular Intrusive Double Linked List Collection' diff --git a/src/c-list.h b/src/c-list.h index c92aa6f3a..69de0b31e 100644 --- a/src/c-list.h +++ b/src/c-list.h @@ -22,6 +22,7 @@ extern "C" { #endif #include +#include typedef struct CList CList; @@ -50,9 +51,42 @@ struct CList { /** * c_list_init() - initialize list entry * @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); + 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. */ #define c_list_entry(_what, _t, _m) \ - ((_t *)(void *)(((unsigned long)(void *)(_what) ?: \ - offsetof(_t, _m)) - offsetof(_t, _m))) + ((_t *)c_list_entry_offset((_what), offsetof(_t, _m))) /** * c_list_is_linked() - check whether an entry is linked @@ -306,32 +339,47 @@ static inline CList *c_list_last(CList *list) { * state. */ +/* direct/raw iterators */ + #define c_list_for_each(_iter, _list) \ for (_iter = (_list)->next; \ (_iter) != (_list); \ _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) \ for (_iter = (_list)->next, _safe = (_iter)->next; \ (_iter) != (_list); \ _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) \ for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \ _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ &(_iter)->_m != (_list); \ _iter = (_safe), \ - _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) + _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) #define c_list_for_each_entry_continue(_iter, _list, _m) \ 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 = 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) \ for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \ __typeof__(*_iter), \ @@ -353,20 +395,14 @@ static inline CList *c_list_last(CList *list) { _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ &(_iter)->_m != (_list); \ _iter = (_safe), \ - _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) + _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) #define c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m) \ for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m), \ _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ - (((_iter)->_m = (CList)C_LIST_INIT((_iter)->_m)), \ - &(_iter)->_m != (_list)); \ + c_list_init(&(_iter)->_m) != (_list); \ _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 @@ -402,8 +438,8 @@ static inline void c_list_flush(CList *list) { * * Return: Number of items in @list. */ -static inline unsigned long c_list_length(const CList *list) { - unsigned long n = 0; +static inline size_t c_list_length(const CList *list) { + size_t n = 0; const CList *iter; c_list_for_each(iter, list) diff --git a/src/test-api.c b/src/test-api.c index caba7fb0f..bf760cf92 100644 --- a/src/test-api.c +++ b/src/test-api.c @@ -18,8 +18,11 @@ typedef struct { static void test_api(void) { 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(&node.link, Node, link) == &node); assert(!c_list_is_linked(&node.link)); @@ -73,28 +76,24 @@ static void test_api(void) { c_list_splice(&list, &list); assert(c_list_is_empty(&list)); - /* loop macros */ + /* direct/raw iterators */ c_list_for_each(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) 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) 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) 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) assert(list_iter != &list); - c_list_for_each_entry_safe_unlink(node_iter, node_safe, &list, link) - assert(&node_iter->link != &list); /* list accessors */ @@ -104,7 +103,37 @@ static void test_api(void) { 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_gnu(); return 0; } diff --git a/src/test-basic.c b/src/test-basic.c index e68af1806..06cccd852 100644 --- a/src/test-basic.c +++ b/src/test-basic.c @@ -160,10 +160,46 @@ static void test_splice(void) { 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. */ { - __attribute__((__cleanup__(c_list_flush))) CList list1 = C_LIST_INIT(list1); - __attribute__((__cleanup__(c_list_flush))) CList list2 = C_LIST_INIT(list2); + 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); @@ -175,11 +211,17 @@ static void test_flush(void) { assert(!c_list_is_linked(&e1)); 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_swap(); test_splice(); test_flush(); + test_macros(); + test_gnu(); return 0; } diff --git a/src/test-embed.c b/src/test-embed.c index 8b88575ff..7ee6ff013 100644 --- a/src/test-embed.c +++ b/src/test-embed.c @@ -23,7 +23,8 @@ static void test_entry(void) { 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; + Entry *e; + CList *iter, *safe; size_t i; /* 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); 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 != 1 || e == &e2); 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)->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); @@ -102,8 +161,13 @@ static void test_entry(void) { assert(!c_list_is_linked(&e3.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_gnu(); return 0; }