add array iterator over pointer arrays

Wed, 01 Jan 2025 14:03:49 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 01 Jan 2025 14:03:49 +0100
changeset 1070
0a5a356a4486
parent 1069
6565e7ef5ee3
child 1071
028cb6d22197

add array iterator over pointer arrays

CHANGELOG file | annotate | diff | comparison | revisions
src/cx/iterator.h file | annotate | diff | comparison | revisions
src/iterator.c file | annotate | diff | comparison | revisions
tests/test_iterator.c file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Wed Jan 01 13:31:38 2025 +0100
+++ b/CHANGELOG	Wed Jan 01 14:03:49 2025 +0100
@@ -7,6 +7,7 @@
  * adds locale-independent string to number conversion functions
  * adds reallocarray() like functions to allocator.h
  * adds cxIterator() to create iterators over raw C arrays
+ * adds cxIteratorPtr() to create iterators over raw C pointer arrays
  * adds cx_array_reallocator() and cx_array_default_reallocator
  * adds several new array and list functions
  * adds cxBufferReset()
--- a/src/cx/iterator.h	Wed Jan 01 13:31:38 2025 +0100
+++ b/src/cx/iterator.h	Wed Jan 01 14:03:49 2025 +0100
@@ -230,11 +230,16 @@
  * The \p array can be \c NULL in which case the iterator will be immediately
  * initialized such that #cxIteratorValid() returns \c false.
  *
+ * This iterator yields the addresses of the array elements.
+ * If you want to iterator over an array of pointers, you can
+ * use cxIteratorPtr() to create an iterator which directly
+ * yields the stored pointers.
  *
  * @param array a pointer to the array (can be \c NULL)
  * @param elem_size the size of one array element
  * @param elem_count the number of elements in the array
  * @return an iterator for the specified array
+ * @see cxIteratorPtr()
  */
 cx_attr_nodiscard
 CxIterator cxIterator(
@@ -274,6 +279,46 @@
         bool remove_keeps_order
 );
 
+/**
+ * Creates an iterator for the specified plain pointer array.
+ *
+ * This iterator assumes that every element in the array is a pointer
+ * and yields exactly those pointers during iteration (while in contrast
+ * an iterator created with cxIterator() would return the addresses
+ * of those pointers within the array).
+ *
+ * @param array a pointer to the array (can be \c NULL)
+ * @param elem_count the number of elements in the array
+ * @return an iterator for the specified array
+ * @see cxIterator()
+ */
+cx_attr_nodiscard
+CxIterator cxIteratorPtr(
+        const void *array,
+        size_t elem_count
+);
+
+/**
+ * Creates a mutating iterator for the specified plain pointer array.
+ *
+ * This is the mutating variant of cxIteratorPtr(). See also
+ * cxMutIterator().
+ *
+ * @param array a pointer to the array (can be \c NULL)
+ * @param elem_count the number of elements in the array
+ * @param remove_keeps_order \c true if the order of elements must be preserved
+ * when removing an element
+ * @return an iterator for the specified array
+ * @see cxMutIterator()
+ * @see cxIteratorPtr()
+ */
+cx_attr_nodiscard
+CxIterator cxMutIteratorPtr(
+        void *array,
+        size_t elem_count,
+        bool remove_keeps_order
+);
+
 #ifdef __cplusplus
 } // extern "C"
 #endif
--- a/src/iterator.c	Wed Jan 01 13:31:38 2025 +0100
+++ b/src/iterator.c	Wed Jan 01 14:03:49 2025 +0100
@@ -40,6 +40,11 @@
     return iter->elem_handle;
 }
 
+static void *cx_iter_current_ptr(const void *it) {
+    const struct cx_iterator_s *iter = it;
+    return *(void**)iter->elem_handle;
+}
+
 static void cx_iter_next_fast(void *it) {
     struct cx_iterator_s *iter = it;
     if (iter->base.remove) {
@@ -110,3 +115,22 @@
     iter.base.mutating = false;
     return iter;
 }
+
+CxIterator cxMutIteratorPtr(
+        void *array,
+        size_t elem_count,
+        bool remove_keeps_order
+) {
+    CxIterator iter = cxMutIterator(array, sizeof(void*), elem_count, remove_keeps_order);
+    iter.base.current = cx_iter_current_ptr;
+    return iter;
+}
+
+CxIterator cxIteratorPtr(
+        const void *array,
+        size_t elem_count
+) {
+    CxIterator iter = cxMutIteratorPtr((void*) array, elem_count, false);
+    iter.base.mutating = false;
+    return iter;
+}
--- a/tests/test_iterator.c	Wed Jan 01 13:31:38 2025 +0100
+++ b/tests/test_iterator.c	Wed Jan 01 14:03:49 2025 +0100
@@ -65,13 +65,13 @@
 
     CxIterator iter = cxIterator(array, sizeof(unsigned), size);
     CX_TEST_DO {
+        CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
+        CX_TEST_ASSERT(iter.elem_count == size);
+        CX_TEST_ASSERT(iter.src_handle.c == array);
         unsigned expected = 0;
         cx_foreach(unsigned *, e, iter) {
             CX_TEST_ASSERT(iter.index == expected);
             CX_TEST_ASSERT(*e == expected);
-            CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
-            CX_TEST_ASSERT(iter.elem_count == size);
-            CX_TEST_ASSERT(iter.src_handle.c == array);
             CX_TEST_ASSERT(iter.elem_handle == &array[expected]);
             expected++;
         }
@@ -79,6 +79,31 @@
     }
 }
 
+CX_TEST(test_iterator_iterate_pointers) {
+    unsigned array[20];
+    unsigned* ptr_array[20];
+    size_t size = cx_nmemb(array);
+    for (unsigned i = 0 ; i < size ; i++) {
+        array[i] = 3*i;
+        ptr_array[i] = &array[i];
+    }
+
+    CxIterator iter = cxIteratorPtr(ptr_array, size);
+    CX_TEST_DO {
+        CX_TEST_ASSERT(iter.elem_size == sizeof(void*));
+        CX_TEST_ASSERT(iter.elem_count == size);
+        CX_TEST_ASSERT(iter.src_handle.c == ptr_array);
+        unsigned idx = 0;
+        cx_foreach(unsigned *, e, iter) {
+            CX_TEST_ASSERT(iter.index == idx);
+            CX_TEST_ASSERT(*e == array[idx]);
+            CX_TEST_ASSERT(iter.elem_handle == &ptr_array[idx]);
+            idx++;
+        }
+        CX_TEST_ASSERT(idx == size);
+    }
+}
+
 CX_TEST(test_iterator_with_slow_remove) {
     unsigned array[20];
     size_t size = cx_nmemb(array);
@@ -170,6 +195,7 @@
 
     cx_test_register(suite, test_iterator_create);
     cx_test_register(suite, test_iterator_iterate);
+    cx_test_register(suite, test_iterator_iterate_pointers);
     cx_test_register(suite, test_iterator_with_slow_remove);
     cx_test_register(suite, test_iterator_with_fast_remove);
 

mercurial