From fe45531b61dd9650fa59801337e37d30247ea41d Mon Sep 17 00:00:00 2001 From: Ronan Dalton Date: Wed, 20 Nov 2024 11:16:11 +1300 Subject: [PATCH] c-list: add reverse iterators Add reverse versions of each for_each macro, allowing for reverse iteration of lists. --- src/c-list.h | 132 +++++++++++++++++++++++++++++++++++------------ src/test-api.c | 45 ++++++++++++++++ src/test-basic.c | 101 ++++++++++++++++++++++++++++++++++++ src/test-embed.c | 97 ++++++++++++++++++++++++++++++++++ 4 files changed, 341 insertions(+), 34 deletions(-) diff --git a/src/c-list.h b/src/c-list.h index 711b54d..c05a219 100644 --- a/src/c-list.h +++ b/src/c-list.h @@ -362,72 +362,136 @@ static inline CList *c_list_last(CList *list) { * it assumes the entire list will be unlinked. You must not * break out of the loop, or the list will be in an inconsistent * state. + * + * - "reverse": The list is iterated in reverse order. + * + * Note: macros starting with "__" are not meant to be used directly. */ /* direct/raw iterators */ -#define c_list_for_each(_iter, _list) \ - for (_iter = (_list)->next; \ +#define __c_list_for_each(_iter, _list, _dir) \ + for (_iter = (_list)->_dir; \ (_iter) != (_list); \ - _iter = (_iter)->next) + _iter = (_iter)->_dir) -#define c_list_for_each_safe(_iter, _safe, _list) \ - for (_iter = (_list)->next, _safe = (_iter)->next; \ +#define __c_list_for_each_safe(_iter, _safe, _list, _dir) \ + for (_iter = (_list)->_dir, _safe = (_iter)->_dir; \ (_iter) != (_list); \ - _iter = (_safe), _safe = (_safe)->next) + _iter = (_safe), _safe = (_safe)->_dir) -#define c_list_for_each_continue(_iter, _list) \ - for (_iter = (_iter) ? (_iter)->next : (_list)->next; \ +#define __c_list_for_each_continue(_iter, _list, _dir) \ + for (_iter = (_iter) ? (_iter)->_dir : (_list)->_dir; \ (_iter) != (_list); \ - _iter = (_iter)->next) + _iter = (_iter)->_dir) -#define c_list_for_each_safe_continue(_iter, _safe, _list) \ - for (_iter = (_iter) ? (_iter)->next : (_list)->next, \ - _safe = (_iter)->next; \ +#define __c_list_for_each_safe_continue(_iter, _safe, _list, _dir) \ + for (_iter = (_iter) ? (_iter)->_dir : (_list)->_dir, \ + _safe = (_iter)->_dir; \ (_iter) != (_list); \ - _iter = (_safe), _safe = (_safe)->next) + _iter = (_safe), _safe = (_safe)->_dir) -#define c_list_for_each_safe_unlink(_iter, _safe, _list) \ - for (_iter = (_list)->next, _safe = (_iter)->next; \ +#define __c_list_for_each_safe_unlink(_iter, _safe, _list, _dir) \ + for (_iter = (_list)->_dir, _safe = (_iter)->_dir; \ c_list_init(_iter) != (_list); \ - _iter = (_safe), _safe = (_safe)->next) + _iter = (_safe), _safe = (_safe)->_dir) + +#define c_list_for_each(_iter, _list) \ + __c_list_for_each(_iter, _list, next) + +#define c_list_for_each_reverse(_iter, _list) \ + __c_list_for_each(_iter, _list, prev) + +#define c_list_for_each_safe(_iter, _safe, _list) \ + __c_list_for_each_safe(_iter, _safe, _list, next) + +#define c_list_for_each_safe_reverse(_iter, _safe, _list) \ + __c_list_for_each_safe(_iter, _safe, _list, prev) + +#define c_list_for_each_continue(_iter, _list) \ + __c_list_for_each_continue(_iter, _list, next) + +#define c_list_for_each_continue_reverse(_iter, _list) \ + __c_list_for_each_continue(_iter, _list, prev) + +#define c_list_for_each_safe_continue(_iter, _safe, _list) \ + __c_list_for_each_safe_continue(_iter, _safe, _list, next) + +#define c_list_for_each_safe_continue_reverse(_iter, _safe, _list) \ + __c_list_for_each_safe_continue(_iter, _safe, _list, prev) + +#define c_list_for_each_safe_unlink(_iter, _safe, _list) \ + __c_list_for_each_safe_unlink(_iter, _safe, _list, next) + +#define c_list_for_each_safe_unlink_reverse(_iter, _safe, _list) \ + __c_list_for_each_safe_unlink(_iter, _safe, _list, prev) /* c_list_entry() based iterators */ -#define c_list_for_each_entry(_iter, _list, _m) \ - for (_iter = c_list_entry((_list)->next, __typeof__(*_iter), _m); \ +#define __c_list_for_each_entry(_iter, _list, _m, _dir) \ + for (_iter = c_list_entry((_list)->_dir, __typeof__(*_iter), _m); \ &(_iter)->_m != (_list); \ - _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m)) + _iter = c_list_entry((_iter)->_m._dir, __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); \ +#define __c_list_for_each_entry_safe(_iter, _safe, _list, _m, _dir) \ + for (_iter = c_list_entry((_list)->_dir, __typeof__(*_iter), _m), \ + _safe = c_list_entry((_iter)->_m._dir, __typeof__(*_iter), _m); \ &(_iter)->_m != (_list); \ _iter = (_safe), \ - _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) + _safe = c_list_entry((_safe)->_m._dir, __typeof__(*_iter), _m)) -#define c_list_for_each_entry_continue(_iter, _list, _m) \ - for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \ +#define __c_list_for_each_entry_continue(_iter, _list, _m, _dir) \ + for (_iter = c_list_entry((_iter) ? (_iter)->_m._dir : (_list)->_dir, \ __typeof__(*_iter), \ _m); \ &(_iter)->_m != (_list); \ - _iter = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m)) + _iter = c_list_entry((_iter)->_m._dir, __typeof__(*_iter), _m)) -#define c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m) \ - for (_iter = c_list_entry((_iter) ? (_iter)->_m.next : (_list)->next, \ +#define __c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m, _dir) \ + for (_iter = c_list_entry((_iter) ? (_iter)->_m._dir : (_list)->_dir, \ __typeof__(*_iter), \ _m), \ - _safe = c_list_entry((_iter)->_m.next, __typeof__(*_iter), _m); \ + _safe = c_list_entry((_iter)->_m._dir, __typeof__(*_iter), _m); \ &(_iter)->_m != (_list); \ _iter = (_safe), \ - _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) + _safe = c_list_entry((_safe)->_m._dir, __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); \ +#define __c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m, _dir) \ + for (_iter = c_list_entry((_list)->_dir, __typeof__(*_iter), _m), \ + _safe = c_list_entry((_iter)->_m._dir, __typeof__(*_iter), _m); \ c_list_init(&(_iter)->_m) != (_list); \ _iter = (_safe), \ - _safe = c_list_entry((_safe)->_m.next, __typeof__(*_iter), _m)) + _safe = c_list_entry((_safe)->_m._dir, __typeof__(*_iter), _m)) + +#define c_list_for_each_entry(_iter, _list, _m) \ + __c_list_for_each_entry(_iter, _list, _m, next) + +#define c_list_for_each_entry_reverse(_iter, _list, _m) \ + __c_list_for_each_entry(_iter, _list, _m, prev) + +#define c_list_for_each_entry_safe(_iter, _safe, _list, _m) \ + __c_list_for_each_entry_safe(_iter, _safe, _list, _m, next) + +#define c_list_for_each_entry_safe_reverse(_iter, _safe, _list, _m) \ + __c_list_for_each_entry_safe(_iter, _safe, _list, _m, prev) + +#define c_list_for_each_entry_continue(_iter, _list, _m) \ + __c_list_for_each_entry_continue(_iter, _list, _m, next) + +#define c_list_for_each_entry_continue_reverse(_iter, _list, _m) \ + __c_list_for_each_entry_continue(_iter, _list, _m, prev) + +#define c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m) \ + __c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m, next) + +#define c_list_for_each_entry_safe_continue_reverse(_iter, _safe, _list, _m) \ + __c_list_for_each_entry_safe_continue(_iter, _safe, _list, _m, prev) + +#define c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m) \ + __c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m, next) + +#define c_list_for_each_entry_safe_unlink_reverse(_iter, _safe, _list, _m) \ + __c_list_for_each_entry_safe_unlink(_iter, _safe, _list, _m, prev) /** * c_list_flush() - flush all entries from a list diff --git a/src/test-api.c b/src/test-api.c index 864d198..a876443 100644 --- a/src/test-api.c +++ b/src/test-api.c @@ -86,20 +86,37 @@ static void test_api(void) { c_list_for_each(list_iter, &list) assert(list_iter != &list); + c_list_for_each_reverse(list_iter, &list) + assert(list_iter != &list); + c_list_for_each_safe(list_iter, list_safe, &list) assert(list_iter != &list); + c_list_for_each_safe_reverse(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_continue_reverse(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); + list_iter = NULL; + c_list_for_each_safe_continue_reverse(list_iter, list_safe, &list) + assert(list_iter != &list); + c_list_for_each_safe_unlink(list_iter, list_safe, &list) assert(list_iter != &list); + c_list_for_each_safe_unlink_reverse(list_iter, list_safe, &list) + assert(list_iter != &list); + /* list accessors */ assert(!c_list_first(&list)); @@ -132,13 +149,41 @@ static void test_api_gnu(void) { c_list_for_each_entry_safe_unlink(node_iter, node_safe, &list, link) assert(&node_iter->link != &list); } + +static void test_api_gnu_reverse(void) { + CList list = C_LIST_INIT(list); + Node *node_iter, *node_safe; + + /* c_list_entry() based iterators */ + + c_list_for_each_entry_reverse(node_iter, &list, link) + assert(&node_iter->link != &list); + + c_list_for_each_entry_safe_reverse(node_iter, node_safe, &list, link) + assert(&node_iter->link != &list); + + node_iter = NULL; + c_list_for_each_entry_continue_reverse(node_iter, &list, link) + assert(&node_iter->link != &list); + + node_iter = NULL; + c_list_for_each_entry_safe_continue_reverse(node_iter, node_safe, &list, link) + assert(&node_iter->link != &list); + + c_list_for_each_entry_safe_unlink_reverse(node_iter, node_safe, &list, link) + assert(&node_iter->link != &list); +} #else static void test_api_gnu(void) { } + +static void test_api_gnu_reverse(void) { +} #endif int main(void) { test_api(); test_api_gnu(); + test_api_gnu_reverse(); return 0; } diff --git a/src/test-basic.c b/src/test-basic.c index 58ed863..bb4c997 100644 --- a/src/test-basic.c +++ b/src/test-basic.c @@ -123,6 +123,106 @@ static void test_iterators(void) { assert(c_list_is_empty(&list)); } +static void test_iterators_reverse(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_reverse(iter, &list) { + assert(iter == &a); + ++i; + } + assert(i == 1); + + i = 0; + iter = NULL; + c_list_for_each_continue_reverse(iter, &list) { + assert(iter == &a); + ++i; + } + assert(i == 1); + + i = 0; + iter = &a; + c_list_for_each_continue_reverse(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_reverse(iter, &list) { + assert((i == 0 && iter == &b) || + (i == 1 && iter == &a)); + ++i; + } + assert(i == 2); + + i = 0; + iter = NULL; + c_list_for_each_continue_reverse(iter, &list) { + assert((i == 0 && iter == &b) || + (i == 1 && iter == &a)); + ++i; + } + assert(i == 2); + + i = 0; + iter = &b; + c_list_for_each_continue_reverse(iter, &list) { + assert(iter == &a); + ++i; + } + assert(i == 1); + + i = 0; + iter = &a; + c_list_for_each_continue_reverse(iter, &list) + ++i; + assert(i == 0); + + /* verify safe-iterator while removing elements */ + + i = 0; + c_list_for_each_safe_reverse(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_reverse(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); @@ -309,6 +409,7 @@ static void test_gnu(void) { int main(void) { test_iterators(); + test_iterators_reverse(); test_swap(); test_splice(); test_split(); diff --git a/src/test-embed.c b/src/test-embed.c index 7ee6ff0..c5e7bf7 100644 --- a/src/test-embed.c +++ b/src/test-embed.c @@ -57,6 +57,17 @@ static void test_entry(void) { } assert(i == 2); + i = 0; + c_list_for_each_reverse(iter, &list) { + e = c_list_entry(iter, Entry, link); + assert(i != 0 || e == &e2); + assert(i != 1 || e == &e1); + assert(i < 2); + ++i; + } + assert(i == 2); + + /* link 2 more entries */ c_list_link_tail(&list, &e3.link); @@ -79,6 +90,18 @@ static void test_entry(void) { } assert(i == 4); + i = 0; + c_list_for_each_reverse(iter, &list) { + e = c_list_entry(iter, Entry, link); + assert(i != 0 || e == &e4); + assert(i != 1 || e == &e3); + assert(i != 2 || e == &e2); + assert(i != 3 || e == &e1); + 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)); @@ -105,6 +128,38 @@ static void test_entry(void) { 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 in reverse */ + + 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); + + 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)); + + i = 0; + c_list_for_each_safe_reverse(iter, safe, &list) { + e = c_list_entry(iter, Entry, link); + assert(i != 0 || e == &e4); + assert(i != 1 || e == &e3); + assert(i != 2 || e == &e2); + assert(i != 3 || e == &e1); + 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__) @@ -135,6 +190,17 @@ static void test_entry_gnu(void) { } assert(i == 4); + i = 0; + c_list_for_each_entry_reverse(e, &list, link) { + assert(i != 0 || e == &e4); + assert(i != 1 || e == &e3); + assert(i != 2 || e == &e2); + assert(i != 3 || e == &e1); + 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)); @@ -160,6 +226,37 @@ static void test_entry_gnu(void) { 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 in reverse */ + + 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); + + 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)); + + i = 0; + c_list_for_each_entry_safe_reverse(e, safe, &list, link) { + assert(i != 0 || e == &e4); + assert(i != 1 || e == &e3); + assert(i != 2 || e == &e2); + assert(i != 3 || e == &e1); + 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) {