Skip to content

Commit

Permalink
c-list: add reverse iterators
Browse files Browse the repository at this point in the history
Add reverse versions of each for_each macro, allowing for reverse
iteration of lists.
  • Loading branch information
ronand-atl committed Nov 19, 2024
1 parent 9aa81d8 commit e868671
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 34 deletions.
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
34 changes: 34 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 All @@ -118,19 +135,36 @@ static void test_api_gnu(void) {
c_list_for_each_entry(node_iter, &list, link)
assert(&node_iter->link != &list);

c_list_for_each_entry_reverse(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);

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(node_iter, &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(node_iter, node_safe, &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(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) {
Expand Down
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

0 comments on commit e868671

Please sign in to comment.