Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

c-list: add reverse iterators #14

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 98 additions & 34 deletions src/c-list.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 45 additions & 0 deletions src/test-api.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}
101 changes: 101 additions & 0 deletions src/test-basic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -309,6 +409,7 @@ static void test_gnu(void) {

int main(void) {
test_iterators();
test_iterators_reverse();
test_swap();
test_splice();
test_split();
Expand Down
Loading