From: Timo Sirainen Date: Thu, 3 Jun 2021 16:04:26 +0000 (+0300) Subject: lib: Add array_foreach_reverse[_modifiable]() X-Git-Tag: 2.3.16~56 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bb618563b778fcb5d22307aaeca4111ca0e0e04a;p=thirdparty%2Fdovecot%2Fcore.git lib: Add array_foreach_reverse[_modifiable]() This is especially useful when deleting multiple elements inside the loop. --- diff --git a/src/lib/array.h b/src/lib/array.h index 38f8bec0cd..9349e8af47 100644 --- a/src/lib/array.h +++ b/src/lib/array.h @@ -72,7 +72,13 @@ array_foreach(&foo_arr, foo) { .. - } */ + } + + Note that deleting an element while iterating will cause the iteration to + skip over the next element. So deleting a single element and breaking out + of the loop is fine, but continuing the loop is likely a bug. Use + array_foreach_reverse() instead when deleting multiple elements. +*/ #define array_foreach(array, elem) \ for (const void *elem ## __foreach_end = \ (const char *)(elem = *(array)->v) + (array)->arr.buffer->used; \ @@ -83,6 +89,17 @@ buffer_get_modifiable_data((array)->arr.buffer, NULL)) + \ (array)->arr.buffer->used; \ elem != elem ## _end; (elem)++) + +/* Iterate the array in reverse order. */ +#define array_foreach_reverse(array, elem) \ + for (elem = CONST_PTR_OFFSET(*(array)->v, (array)->arr.buffer->used); \ + (const char *)(elem--) > (const char *)*(array)->v; ) +#define array_foreach_reverse_modifiable(array, elem) \ + for (elem = ARRAY_TYPE_CAST_MODIFIABLE(array) \ + ((char *)buffer_get_modifiable_data((array)->arr.buffer, NULL) + \ + (array)->arr.buffer->used); \ + (const char *)(elem--) > (const char *)*(array)->v; ) + /* Usage: ARRAY(struct foo *) foo_ptrs_arr; struct foo *foo; diff --git a/src/lib/test-array.c b/src/lib/test-array.c index 7d2d9bb664..e6aade12c4 100644 --- a/src/lib/test-array.c +++ b/src/lib/test-array.c @@ -89,6 +89,56 @@ static void test_array_foreach(void) test_assert(foo == array_idx(&foos, i)+1); test_end(); } + +static void test_array_foreach_reverse(void) +{ + ARRAY(unsigned int) arr; + const unsigned int *i_p; + unsigned int i, i2, *imod_p; + + test_begin("array foreach reverse"); + t_array_init(&arr, 32); + + /* first test that array_foreach() + array_delete() doesn't really + work as we might hope.. */ + for (i = 1; i <= 5; i++) + array_push_back(&arr, &i); + array_foreach(&arr, i_p) { + i = array_foreach_idx(&arr, i_p); + array_delete(&arr, i, 1); + } + test_assert(array_count(&arr) == 2); + + /* but using array_foreach_reverse() + array_delete() does work: */ + array_clear(&arr); + i2 = 5; + for (i = 1; i <= i2; i++) + array_push_back(&arr, &i); + array_foreach_reverse(&arr, i_p) { + i = array_foreach_idx(&arr, i_p); + test_assert(*i_p == i2); + test_assert(*i_p == i + 1); + array_delete(&arr, i, 1); + i2--; + } + test_assert(array_count(&arr) == 0); + + /* also array_foreach_reverse_modifiable() + array_delete() works: */ + i2 = 5; + for (i = 1; i <= i2; i++) + array_push_back(&arr, &i); + array_foreach_reverse_modifiable(&arr, imod_p) { + i = array_foreach_idx(&arr, imod_p); + test_assert(*imod_p == i2); + test_assert(*imod_p == i + 1); + array_delete(&arr, i, 1); + i2--; + } + test_assert(array_count(&arr) == 0); + + test_end(); +} + static void test_array_foreach_elem_string(void) { ARRAY(char *) blurbs; @@ -307,6 +357,7 @@ void test_array(void) test_array_elem(); test_array_count(); test_array_foreach(); + test_array_foreach_reverse(); test_array_foreach_elem_string(); test_array_reverse(); test_array_cmp();