]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib: Add array_foreach_reverse[_modifiable]()
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Thu, 3 Jun 2021 16:04:26 +0000 (19:04 +0300)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Mon, 14 Jun 2021 13:34:07 +0000 (13:34 +0000)
This is especially useful when deleting multiple elements inside the loop.

src/lib/array.h
src/lib/test-array.c

index 38f8bec0cd9c482afab32ed37ab639fe573f8aee..9349e8af47202eb3872ab21b5b4e5d71a7510605 100644 (file)
 
    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; \
                        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;
index 7d2d9bb6641596fb103fb8b8be2aea10b14258e5..e6aade12c4b8bbaaff3ffd82e4e6ea635df2d7c4 100644 (file)
@@ -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();