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:
27
.github/workflows/ci.yml
vendored
27
.github/workflows/ci.yml
vendored
@@ -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
|
||||
|
2
AUTHORS
2
AUTHORS
@@ -33,6 +33,8 @@ COPYRIGHT: (ordered alphabetically)
|
||||
Copyright (C) 2015-2019 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>
|
||||
Thomas Haller <thaller@redhat.com>
|
||||
Tom Gundersen <teg@jklm.no>
|
||||
|
@@ -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'
|
||||
|
98
src/c-list.h
98
src/c-list.h
@@ -22,6 +22,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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)
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user