remove the concept of "mutating iterators" - resolves #579

Fri, 17 Oct 2025 16:53:24 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 17 Oct 2025 16:53:24 +0200
changeset 1429
6e0c3a8a914a
parent 1428
0ac4aa1737fd
child 1430
5917ed4e5a79

remove the concept of "mutating iterators" - resolves #579

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/array_list.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/iterator.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/list.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/map.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/tree.h.md file | annotate | diff | comparison | revisions
src/array_list.c file | annotate | diff | comparison | revisions
src/cx/iterator.h file | annotate | diff | comparison | revisions
src/cx/list.h file | annotate | diff | comparison | revisions
src/cx/map.h file | annotate | diff | comparison | revisions
src/hash_map.c file | annotate | diff | comparison | revisions
src/iterator.c file | annotate | diff | comparison | revisions
src/json.c file | annotate | diff | comparison | revisions
src/kv_list.c file | annotate | diff | comparison | revisions
src/linked_list.c file | annotate | diff | comparison | revisions
src/list.c file | annotate | diff | comparison | revisions
src/map.c file | annotate | diff | comparison | revisions
src/tree.c file | annotate | diff | comparison | revisions
tests/test_hash_map.c file | annotate | diff | comparison | revisions
tests/test_iterator.c file | annotate | diff | comparison | revisions
tests/test_kv_list.c file | annotate | diff | comparison | revisions
tests/test_list.c file | annotate | diff | comparison | revisions
tests/test_tree.c file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Fri Oct 17 15:04:56 2025 +0200
+++ b/CHANGELOG	Fri Oct 17 16:53:24 2025 +0200
@@ -44,6 +44,7 @@
  * fixes allocator arguments for some printf.h functions not being const
  * fixes that cx_tree_search() did not investigate subtrees with equally good distance
  * fixes that memory was freed by the wrong allocator in cx_vasprintf_a() when the underlying vsnprintf() failed
+ * removes all functions that create mutating iterators in favor of making all iterators possibly mutating
  * removes the use of C23 attributes because they don't mix well with GNU attributes in GCC 15
 
 Version 3.1 - 2025-02-11
--- a/docs/Writerside/topics/about.md	Fri Oct 17 15:04:56 2025 +0200
+++ b/docs/Writerside/topics/about.md	Fri Oct 17 16:53:24 2025 +0200
@@ -71,6 +71,7 @@
 * fixes allocator arguments for some printf.h functions not being const
 * fixes that cx_tree_search() did not investigate subtrees with equally good distance
 * fixes that memory was freed by the wrong allocator in cx_vasprintf_a() when the underlying vsnprintf() failed
+* removes all functions that create mutating iterators in favor of making all iterators possibly mutating
 * removes the use of C23 attributes because they don't mix well with GNU attributes in GCC 15
 
 ### Version 3.1 - 2025-02-11 {collapsible="true"}
--- a/docs/Writerside/topics/array_list.h.md	Fri Oct 17 15:04:56 2025 +0200
+++ b/docs/Writerside/topics/array_list.h.md	Fri Oct 17 16:53:24 2025 +0200
@@ -326,14 +326,11 @@
 #include <cx/iterator.h>
 
 CxIterator cxIterator(const void *array,
-        size_t elem_size, size_t elem_count);
-        
-CxIterator cxMutIterator(void *array,
-        size_t elem_size, size_t elem_count, bool remove_keeps_order);
-        
-CxIterator cxIteratorPtr(const void *array, size_t elem_count);
-        
-CxIterator cxMutIteratorPtr(void *array, size_t elem_count,
+        size_t elem_size, size_t elem_count,
+        bool remove_keeps_order);
+       
+CxIterator cxIteratorPtr(const void *array,
+        size_t elem_count,
         bool remove_keeps_order);
 ```
 
--- a/docs/Writerside/topics/iterator.h.md	Fri Oct 17 15:04:56 2025 +0200
+++ b/docs/Writerside/topics/iterator.h.md	Fri Oct 17 16:53:24 2025 +0200
@@ -27,23 +27,18 @@
 #include <cx/iterator.h>
 
 CxIterator cxIterator(const void *array,
-        size_t elem_size, size_t elem_count);
-        
-CxIterator cxMutIterator(void *array,
-        size_t elem_size, size_t elem_count, bool remove_keeps_order);
-        
-CxIterator cxIteratorPtr(const void *array, size_t elem_count);
-        
-CxIterator cxMutIteratorPtr(void *array, size_t elem_count,
+        size_t elem_size, size_t elem_count,
+        bool remove_keeps_order);
+       
+CxIterator cxIteratorPtr(const void *array,
+        size_t elem_count,
         bool remove_keeps_order);
 ```
 
 The `cxIterator()` function creates an iterator over the elements of `array` where
 each element is `elem_size` bytes large and the array contains a total of `elem_count` elements.
-The `cxMutIterator()` function creates an equivalent [mutating iterator](#mutating-iterators). 
 
-The `cxIteratorPtr()` and `cxMutIteratorPtr()` functions are equivalent to
-the `cxIteratorPtr()` and `cxMutIteratorPtr()`, except they assume `sizeof(void*)` as the `elem_size`.
+The `cxIteratorPtr()` function is equivalent to `cxIterator()`, except it assumes `sizeof(void*)` as the `elem_size`.
 
 The UCX collections also define functions for creating iterators over their items.
 You can read more about them in the respective Sections of the documentation.
@@ -86,15 +81,13 @@
 > You should read the documentation of the function creating the iterator to learn
 > what exactly the iterator is iterating over.
 
-## Mutating Iterators
+## Removing Elements via Iterators
 
 Usually an iterator is not mutating the collection it is iterating over.
 But sometimes it is desirable to remove an element from the collection while iterating over it.
 
-For this purpose, most collections allow the creation of a _mutating_ iterator.
-On mutating iterators the `mutating` flag in the base structure is set to `true`,
-and it is allowed to call the `cxFlagForRemoval()` function, which instructs the iterator to remove
-the current element from the collection on the next call to `cxIteratorNext()` and clear the flag afterward.
+For this purpose, most collections allow to use `cxIteratorFlagRemoval()`, which instructs the iterator to remove
+the current element from the collection on the next call to `cxIteratorNext()`.
 If you are implementing your own iterator, it is up to you to implement this behavior.
 
 ## Passing Iterators to Functions
@@ -127,9 +120,10 @@
 struct cx_iterator_base_s {
     bool (*valid)(const void *);
     void *(*current)(const void *);
+    void (*next)(void *);
     void *(*current_impl)(const void *);
-    void (*next)(void *);
-    bool mutating;
+    void (*next_impl)(void *);
+    bool allow_remove;
     bool remove;
 };
 
@@ -139,14 +133,17 @@
 The `valid` function indicates whether the iterator is currently pointing to an element in the collection.
 The `current` function is supposed to return that element,
 and the `next` function shall advance the iterator to the next element.
-The booleans `mutating` and `remove` are used for [mutating iterators](#mutating-iterators) as explained above.
 
-Iterators may be wrapped in which case the original implementation can be stored in `current_impl` and
-called by a wrapper implementation pointed to by `current`.
+The booleans `allow_remove` and `remove` are used for [removing elements](#removing-elements-via-iterators) as explained above.
+When an iterator is created, the `allow_remove` field is set to indicate if removal of elements is supported.
+The `remove` field is set to indicate if the current element should be removed on the next call to `next()` (see `cxIteratorFlagRemoval()`).
+
+Iterators may be wrapped in which case the original implementation can be stored in `current_impl` and `next_impl`.
+They can then be called by a wrapper implementation pointed to by `current` and `next`, respectively.
 This can be useful when you want to support the `store_pointer` field of the [](collection.h.md) API.
 
 A specialized, simple, and fast iterator over an array of a certain type
-that does not support mutation can be implemented as follows:
+that does not support removing elements can be implemented as follows:
 ```C
 #include <cx/iterator.h>
 
@@ -184,7 +181,7 @@
     iter.base.current = my_foo_iter_current;
     iter.base.next = my_foo_iter_next;
     iter.base.remove = false;
-    iter.base.mutating = false;
+    iter.base.allow_remove = false;
     
     // custom fields
     iter.index = 0;
--- a/docs/Writerside/topics/list.h.md	Fri Oct 17 15:04:56 2025 +0200
+++ b/docs/Writerside/topics/list.h.md	Fri Oct 17 16:53:24 2025 +0200
@@ -154,7 +154,6 @@
 
 When you are currently iterating through a list, you can insert elements before or after the current position of the iterator
 with `cxListInsertBefore()` or `cxListInsertAfter()`, respectively.
-This _should_ be done with [mutating iterators](iterator.h.md#mutating-iterators) only, but is also defined for normal iterators.
 
 If the list is storing pointers (cf. `cxCollectionStoresPointers()`), the pointer `elem` is directly added to the list.
 Otherwise, the contents at the location pointed to by `elem` are copied to the list's memory with the element size specified during creation of the list.
@@ -284,14 +283,6 @@
 CxIterator cxListIteratorAt(const CxList *list, size_t index);
 
 CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
-
-CxIterator cxListMutIterator(CxList *list);
-
-CxIterator cxListMutBackwardsIterator(CxList *list);
-
-CxIterator cxListMutIteratorAt(CxList *list, size_t index);
-
-CxIterator cxListMutBackwardsIteratorAt(CxList *list, size_t index);
 ```
 
 The functions `cxListIterator()` and `cxListBackwardsIterator()` create iterators
@@ -300,8 +291,7 @@
 The functions `cxListIteratorAt()` and `cxListBackwardsIteratorAt()` start with the element at the specified index
 and iterate until the end, or the beginning of the list, respectively.
 
-The functions with `Mut` in are equivalent, except that they create a [mutating iterator](iterator.h.md#mutating-iterators).
-Removing elements via a mutating iterator will cause an invocation of the [destructor functions](collection.h.md#destructor-functions) for the removed element. 
+Removing elements via an iterator will cause an invocation of the [destructor functions](collection.h.md#destructor-functions) for the removed element. 
 
 It is safe to specify an out-of-bounds index, or a `NULL` pointer, in which cases the returned iterator will behave like an iterator over an empty list.
 
--- a/docs/Writerside/topics/map.h.md	Fri Oct 17 15:04:56 2025 +0200
+++ b/docs/Writerside/topics/map.h.md	Fri Oct 17 16:53:24 2025 +0200
@@ -262,15 +262,9 @@
 CxMapIterator cxMapIteratorKeys(const CxMap *map);
 
 CxMapIterator cxMapIteratorValues(const CxMap *map);
-
-CxMapIterator cxMapMutIterator(CxMap *map);
-
-CxMapIterator cxMapMutIteratorKeys(CxMap *map);
-
-CxMapIterator cxMapMutIteratorValues(CxMap *map);
 ```
 
-The above functions create a ([mutating](iterator.h.md#mutating-iterators)) iterator over the
+The above functions create iterators over the
 pairs, keys, or values of the specified `map`, respectively.
 
 Iterators over pairs yield elements of type `CxMapEntry*` which is a struct containing a pointer to the `key` and the value, respectively.
--- a/docs/Writerside/topics/tree.h.md	Fri Oct 17 15:04:56 2025 +0200
+++ b/docs/Writerside/topics/tree.h.md	Fri Oct 17 16:53:24 2025 +0200
@@ -348,7 +348,8 @@
 void cxTreeVisitorDispose(CxTreeVisitor *visitor);
 ```
 
-There are two different kinds of iterators for trees.
+There are two different kinds of iterators for trees: one for depth-first iteration and one for breadth-first iteration.
+
 The `CxTreeIterator` is performing a depth-first iteration with the capability of visiting a node twice:
 first when the iterator enters the node coming from its parent, and secondly when the iterator tracks back from its last child.
 This behavior is controlled via the `visit_on_exit` argument.
@@ -357,6 +358,8 @@
 
 On the other hand, the `CxTreeVisitor` performs a breadth-first iteration and visits every node only once.
 
+Both iterators do not support the removal of nodes.
+
 Since tree iteration needs to keep track of a stack (depth-first) or queue (breadth-first), internal memory is allocated.
 This memory is _automatically_ disposed of when the iteration _completes_.
 If you break from the iteration early, you must call `cxTreeIteratorDispose()` or `cxTreeVisitorDispose()`, respectively, to deallocate the memory.
--- a/src/array_list.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/array_list.c	Fri Oct 17 16:53:24 2025 +0200
@@ -880,7 +880,7 @@
         const void *elem,
         int prepend
 ) {
-    struct cx_list_s *list = iter->src_handle.m;
+    struct cx_list_s *list = iter->src_handle;
     if (iter->index < list->collection.size) {
         if (cx_arl_insert_element(list,
                 iter->index + 1 - prepend, elem) == NULL) {
@@ -1091,7 +1091,7 @@
 
 static bool cx_arl_iter_valid(const void *it) {
     const struct cx_iterator_s *iter = it;
-    const struct cx_list_s *list = iter->src_handle.c;
+    const struct cx_list_s *list = iter->src_handle;
     return iter->index < list->collection.size;
 }
 
@@ -1104,25 +1104,25 @@
     struct cx_iterator_s *iter = it;
     if (iter->base.remove) {
         iter->base.remove = false;
-        cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL);
+        cx_arl_remove(iter->src_handle, iter->index, 1, NULL);
         iter->elem_count--;
     } else {
         iter->index++;
         iter->elem_handle =
                 ((char *) iter->elem_handle)
-                + ((const struct cx_list_s *) iter->src_handle.c)->collection.elem_size;
+                + ((const struct cx_list_s *) iter->src_handle)->collection.elem_size;
     }
 }
 
 static void cx_arl_iter_prev(void *it) {
     struct cx_iterator_s *iter = it;
-    const cx_array_list *list = iter->src_handle.c;
     if (iter->base.remove) {
         iter->base.remove = false;
-        cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL);
+        cx_arl_remove(iter->src_handle, iter->index, 1, NULL);
         iter->elem_count--;
     }
     iter->index--;
+    cx_array_list *list = iter->src_handle;
     if (iter->index < list->base.collection.size) {
         iter->elem_handle = ((char *) list->data)
                             + iter->index * list->base.collection.elem_size;
@@ -1138,7 +1138,7 @@
     struct cx_iterator_s iter;
 
     iter.index = index;
-    iter.src_handle.c = list;
+    iter.src_handle = (void*)list;
     iter.elem_handle = cx_arl_at(list, index);
     iter.elem_size = list->collection.elem_size;
     iter.elem_count = list->collection.size;
@@ -1146,7 +1146,7 @@
     iter.base.current = cx_arl_iter_current;
     iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
     iter.base.remove = false;
-    iter.base.mutating = false;
+    iter.base.allow_remove = true;
 
     return iter;
 }
--- a/src/cx/iterator.h	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/cx/iterator.h	Fri Oct 17 16:53:24 2025 +0200
@@ -62,7 +62,6 @@
      * Original implementation in case the function needs to be wrapped.
      */
     void *(*current_impl)(const void *);
-
     /**
      * Advances the iterator.
      *
@@ -76,7 +75,7 @@
     /**
      * Indicates whether this iterator may remove elements.
      */
-    bool mutating;
+    bool allow_remove;
     /**
      * Internal flag for removing the current element when advancing.
      */
@@ -112,16 +111,7 @@
     /**
      * Handle for the source collection, if any.
      */
-    union {
-        /**
-         * Access for mutating iterators.
-         */
-        void *m;
-        /**
-         * Access for normal iterators.
-         */
-        const void *c;
-    } src_handle;
+    void *src_handle;
 
     /**
      * If the iterator is position-aware, contains the index of the element in the underlying collection.
@@ -149,7 +139,7 @@
  * to be "position-aware", which means that they keep track of the current index within the collection.
  *
  * @note Objects that are pointed to by an iterator are always mutable through that iterator. However,
- * any concurrent mutation of the collection other than by this iterator makes this iterator invalid,
+ * any concurrent mutation of the collection other than by this iterator makes this iterator obsolete,
  * and it must not be used anymore.
  */
 typedef struct cx_iterator_s CxIterator;
@@ -182,13 +172,12 @@
 #define cxIteratorNext(iter) (iter).base.next(&iter)
 
 /**
- * Flags the current element for removal if this iterator is mutating.
- *
- * Does nothing for non-mutating iterators.
+ * Flags the current element for removal if the iterator allows it.
  *
  * @param iter the iterator
+ * @return @c true if removal is allowed, @c false otherwise
  */
-#define cxIteratorFlagRemoval(iter) (iter).base.remove |= (iter).base.mutating
+#define cxIteratorFlagRemoval(iter) ((iter).base.remove = (iter).base.allow_remove)
 
 /**
  * Obtains a reference to an arbitrary iterator.
@@ -222,18 +211,34 @@
  * use cxIteratorPtr() to create an iterator which directly
  * yields the stored pointers.
  *
+ * While the iterator is in use, the array may only be altered by removing
+ * elements through #cxIteratorFlagRemoval(). Every other change to the array
+ * will bring this iterator to an undefined state.
+ *
+ * When @p remove_keeps_order is set to @c false, removing an element will only
+ * move the last element to the position of the removed element, instead of
+ * moving all subsequent elements by one. Usually, when the order of elements is
+ * not important, this parameter should be set to @c false.
+ *
  * @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
+ * @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 cxIteratorPtr()
  */
 cx_attr_nodiscard
 CX_EXPORT CxIterator cxIterator(const void *array,
-        size_t elem_size, size_t elem_count);
+        size_t elem_size, size_t elem_count, bool remove_keeps_order);
 
 /**
- * Creates a mutating iterator for the specified plain array.
+ * 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 (on the other
+ * hand, an iterator created with cxIterator() would return the
+ * addresses of those pointers within the array).
  *
  * While the iterator is in use, the array may only be altered by removing
  * elements through #cxIteratorFlagRemoval(). Every other change to the array
@@ -244,54 +249,16 @@
  * moving all subsequent elements by one. Usually, when the order of elements is
  * not important, this parameter should be set to @c false.
  *
- * The @p array can be @c NULL, in which case the iterator will be immediately
- * initialized such that #cxIteratorValid() returns @c false.
- *
- *
- * @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
- * @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
- */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxMutIterator(void *array,
-        size_t elem_size, size_t elem_count, 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 (on the other
- * hand, 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
-CX_EXPORT 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()
+ * @see cxIterator()
  */
 cx_attr_nodiscard
-CX_EXPORT CxIterator cxMutIteratorPtr(void *array,
-        size_t elem_count, bool remove_keeps_order);
+CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count,
+        bool remove_keeps_order);
 
 #ifdef __cplusplus
 } // extern "C"
--- a/src/cx/list.h	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/cx/list.h	Fri Oct 17 16:53:24 2025 +0200
@@ -791,35 +791,6 @@
 CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index);
 
 /**
- * Returns a mutating iterator pointing to the item at the specified index.
- *
- * The returned iterator is position-aware.
- *
- * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
- *
- * @param list the list
- * @param index the index where the iterator shall point at
- * @return a new iterator
- */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListMutIteratorAt(CxList *list, size_t index);
-
-/**
- * Returns a mutating backwards iterator pointing to the item at the
- * specified index.
- *
- * The returned iterator is position-aware.
- *
- * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned.
- *
- * @param list the list
- * @param index the index where the iterator shall point at
- * @return a new iterator
- */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListMutBackwardsIteratorAt(CxList *list, size_t index);
-
-/**
  * Returns an iterator pointing to the first item of the list.
  *
  * The returned iterator is position-aware.
@@ -833,19 +804,6 @@
 CX_EXPORT CxIterator cxListIterator(const CxList *list);
 
 /**
- * Returns a mutating iterator pointing to the first item of the list.
- *
- * The returned iterator is position-aware.
- *
- * If the list is empty or @c NULL, a past-the-end iterator will be returned.
- *
- * @param list the list
- * @return a new iterator
- */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListMutIterator(CxList *list);
-
-/**
  * Returns a backwards iterator pointing to the last item of the list.
  *
  * The returned iterator is position-aware.
@@ -859,19 +817,6 @@
 CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list);
 
 /**
- * Returns a mutating backwards iterator pointing to the last item of the list.
- *
- * The returned iterator is position-aware.
- *
- * If the list is empty or @c NULL, a past-the-end iterator will be returned.
- *
- * @param list the list
- * @return a new iterator
- */
-cx_attr_nodiscard
-CX_EXPORT CxIterator cxListMutBackwardsIterator(CxList *list);
-
-/**
  * Returns the index of the first element that equals @p elem.
  *
  * Determining equality is performed by the list's comparator function.
--- a/src/cx/map.h	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/cx/map.h	Fri Oct 17 16:53:24 2025 +0200
@@ -111,16 +111,7 @@
     /**
      * Handle for the source map.
      */
-    union {
-        /**
-         * Access for mutating iterators.
-         */
-        CxMap *m;
-        /**
-         * Access for normal iterators.
-         */
-        const CxMap *c;
-    } map;
+    CxMap *map;
 
     /**
      * Handle for the current element.
@@ -302,54 +293,6 @@
 CX_EXPORT CxMapIterator cxMapIterator(const CxMap *map);
 
 /**
- * Creates a mutating iterator over the values of a map.
- *
- * When the map is storing pointers, those pointers are returned.
- * Otherwise, the iterator iterates over pointers to the memory within the map where the
- * respective elements are stored.
- *
- * @note An iterator iterates over all elements successively. Therefore, the order
- * highly depends on the map implementation and may change arbitrarily when the contents change.
- *
- * @param map the map to create the iterator for (can be @c NULL)
- * @return an iterator for the currently stored values
- */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapMutIteratorValues(CxMap *map);
-
-/**
- * Creates a mutating iterator over the keys of a map.
- *
- * The elements of the iterator are keys of type CxHashKey, and the pointer returned
- * during iterator shall be treated as @c const @c CxHashKey* .
- *
- * @note An iterator iterates over all elements successively. Therefore, the order
- * highly depends on the map implementation and may change arbitrarily when the contents change.
- *
- * @param map the map to create the iterator for (can be @c NULL)
- * @return an iterator for the currently stored keys
- */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapMutIteratorKeys(CxMap *map);
-
-/**
- * Creates a mutating iterator for a map.
- *
- * The elements of the iterator are key/value pairs of type CxMapEntry, and the pointer returned
- * during iterator shall be treated as @c const @c CxMapEntry* .
- *
- * @note An iterator iterates over all elements successively. Therefore, the order
- * highly depends on the map implementation and may change arbitrarily when the contents change.
- *
- * @param map the map to create the iterator for (can be @c NULL)
- * @return an iterator for the currently stored entries
- * @see cxMapMutIteratorKeys()
- * @see cxMapMutIteratorValues()
- */
-cx_attr_nodiscard
-CX_EXPORT CxMapIterator cxMapMutIterator(CxMap *map);
-
-/**
  * Puts a key/value-pair into the map.
  *
  * A possible existing value will be overwritten.
--- a/src/hash_map.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/hash_map.c	Fri Oct 17 16:53:24 2025 +0200
@@ -281,7 +281,7 @@
 
 static void cx_hash_map_iter_next(void *it) {
     CxMapIterator *iter = it;
-    CxMap *map = iter->map.m;
+    CxMap *map = iter->map;
     struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map;
     struct cx_hash_map_element_s *elm = iter->elem;
 
@@ -329,7 +329,7 @@
     // must not modify the iterator (the parameter is const)
     if (elm != NULL) {
         iter->entry.key = &elm->key;
-        if (iter->map.c->collection.store_pointer) {
+        if (map->collection.store_pointer) {
             iter->entry.value = *(void **) elm->data;
         } else {
             iter->entry.value = elm->data;
@@ -343,7 +343,7 @@
 ) {
     CxMapIterator iter;
 
-    iter.map.c = map;
+    iter.map = (CxMap*) map;
     iter.elem_count = map->collection.size;
 
     switch (type) {
@@ -366,7 +366,7 @@
     iter.base.valid = cx_hash_map_iter_valid;
     iter.base.next = cx_hash_map_iter_next;
     iter.base.remove = false;
-    iter.base.mutating = false;
+    iter.base.allow_remove = true;
 
     iter.slot = 0;
     iter.index = 0;
--- a/src/iterator.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/iterator.c	Fri Oct 17 16:53:24 2025 +0200
@@ -53,7 +53,7 @@
         // only move the last element when we are not currently aiming
         // at the last element already
         if (iter->index < iter->elem_count) {
-            void *last = ((char *) iter->src_handle.m)
+            void *last = ((char *) iter->src_handle)
                          + iter->elem_count * iter->elem_size;
             memcpy(iter->elem_handle, last, iter->elem_size);
         }
@@ -84,8 +84,8 @@
     }
 }
 
-CxIterator cxMutIterator(
-        void *array,
+CxIterator cxIterator(
+        const void *array,
         size_t elem_size,
         size_t elem_count,
         bool remove_keeps_order
@@ -93,44 +93,25 @@
     CxIterator iter;
 
     iter.index = 0;
-    iter.src_handle.m = array;
-    iter.elem_handle = array;
+    iter.src_handle = (void*) array;
+    iter.elem_handle = (void*) array;
     iter.elem_size = elem_size;
     iter.elem_count = array == NULL ? 0 : elem_count;
     iter.base.valid = cx_iter_valid;
     iter.base.current = cx_iter_current;
     iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast;
     iter.base.remove = false;
-    iter.base.mutating = true;
-
-    return iter;
-}
+    iter.base.allow_remove = true;
 
-CxIterator cxIterator(
-        const void *array,
-        size_t elem_size,
-        size_t elem_count
-) {
-    CxIterator iter = cxMutIterator((void*)array, elem_size, elem_count, false);
-    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
+        size_t elem_count,
+        bool remove_keeps_order
 ) {
-    CxIterator iter = cxMutIteratorPtr((void*) array, elem_count, false);
-    iter.base.mutating = false;
+    CxIterator iter = cxIterator(array, sizeof(void*), elem_count, remove_keeps_order);
+    iter.base.current = cx_iter_current_ptr;
     return iter;
 }
--- a/src/json.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/json.c	Fri Oct 17 16:53:24 2025 +0200
@@ -1163,7 +1163,8 @@
 CxIterator cxJsonArrIter(const CxJsonValue *value) {
     return cxIteratorPtr(
         value->value.array.array,
-        value->value.array.array_size
+        value->value.array.array_size,
+        true // arrays need to keep order
     );
 }
 
@@ -1171,7 +1172,8 @@
     return cxIterator(
         value->value.object.values,
         sizeof(CxJsonObjValue),
-        value->value.object.values_size
+        value->value.object.values_size,
+        true // TODO: objects do not always need to keep order
     );
 }
 
@@ -1191,7 +1193,7 @@
     } else {
         CxJsonObjValue kv = value->value.object.values[index];
         cx_strfree_a(value->allocator, &kv.name);
-        // TODO: replace with cx_array_remove()
+        // TODO: replace with cx_array_remove() / cx_array_remove_fast()
         value->value.object.values_size--;
         memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue));
         return kv.value;
--- a/src/kv_list.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/kv_list.c	Fri Oct 17 16:53:24 2025 +0200
@@ -152,7 +152,7 @@
         const void *elem,
         int prepend
 ) {
-    cx_kv_list *kv_list = iter->src_handle.m;
+    cx_kv_list *kv_list = iter->src_handle;
     return kv_list->list_methods->insert_iter(iter, elem, prepend);
 }
 
@@ -259,7 +259,7 @@
     struct cx_iterator_s *iter = it;
     if (iter->base.remove) {
         // remove the assigned key from the map before calling the actual function
-        cx_kv_list *kv_list = iter->src_handle.m;
+        cx_kv_list *kv_list = iter->src_handle;
         cx_kv_list_update_destructors(kv_list);
         char *node = iter->elem_handle;
         CxHashKey *key = cx_kv_list_loc_key(kv_list, node + kv_list->list.loc_data);
@@ -267,6 +267,7 @@
             kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL);
         }
     }
+    // note that we do not clear the remove flag, because the next_impl will do that
     iter->base.next_impl(it);
 }
 
@@ -397,7 +398,7 @@
 
 static void cx_kvl_iter_next(void *it) {
     CxMapIterator *iter = it;
-    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)iter->map.m)->list;
+    cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)iter->map)->list;
 
     // find the next list entry that has a key assigned
     CxHashKey *key = NULL;
@@ -458,7 +459,7 @@
     CxMapIterator iter = {0};
 
     iter.type = type;
-    iter.map.c = map;
+    iter.map = (CxMap*)map;
     // although we iterate over the list, we only report that many elements that have a key in the map
     iter.elem_count = map->collection.size;
 
@@ -479,6 +480,7 @@
             assert(false); // LCOV_EXCL_LINE
     }
 
+    iter.base.allow_remove = true;
     iter.base.next = cx_kvl_iter_next;
     iter.base.valid = cx_kvl_iter_valid;
 
--- a/src/linked_list.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/linked_list.c	Fri Oct 17 16:53:24 2025 +0200
@@ -1126,10 +1126,10 @@
 
 static void cx_ll_iter_next(void *it) {
     struct cx_iterator_s *iter = it;
+    cx_linked_list *ll = iter->src_handle;
     if (iter->base.remove) {
         iter->base.remove = false;
-        struct cx_list_s *list = iter->src_handle.m;
-        cx_linked_list *ll = iter->src_handle.m;
+        struct cx_list_s *list = iter->src_handle;
         char *node = iter->elem_handle;
         iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
         cx_invoke_destructor(list, node + ll->loc_data);
@@ -1139,7 +1139,6 @@
         iter->elem_count--;
         cxFree(list->collection.allocator, node);
     } else {
-        const cx_linked_list *ll = iter->src_handle.c;
         iter->index++;
         void *node = iter->elem_handle;
         iter->elem_handle = CX_LL_PTR(node, ll->loc_next);
@@ -1148,10 +1147,10 @@
 
 static void cx_ll_iter_prev(void *it) {
     struct cx_iterator_s *iter = it;
+    cx_linked_list *ll = iter->src_handle;
     if (iter->base.remove) {
         iter->base.remove = false;
-        struct cx_list_s *list = iter->src_handle.m;
-        cx_linked_list *ll = iter->src_handle.m;
+        struct cx_list_s *list = iter->src_handle;
         char *node = iter->elem_handle;
         iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
         iter->index--;
@@ -1162,7 +1161,6 @@
         iter->elem_count--;
         cxFree(list->collection.allocator, node);
     } else {
-        const cx_linked_list *ll = iter->src_handle.c;
         iter->index--;
         char *node = iter->elem_handle;
         iter->elem_handle = CX_LL_PTR(node, ll->loc_prev);
@@ -1171,7 +1169,7 @@
 
 static void *cx_ll_iter_current(const void *it) {
     const struct cx_iterator_s *iter = it;
-    const cx_linked_list *ll = iter->src_handle.c;
+    const cx_linked_list *ll = iter->src_handle;
     char *node = iter->elem_handle;
     return node + ll->loc_data;
 }
@@ -1183,14 +1181,14 @@
 ) {
     CxIterator iter;
     iter.index = index;
-    iter.src_handle.c = list;
+    iter.src_handle = (void*)list;
     iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index);
     iter.elem_size = list->collection.elem_size;
     iter.elem_count = list->collection.size;
     iter.base.valid = cx_ll_iter_valid;
     iter.base.current = cx_ll_iter_current;
     iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
-    iter.base.mutating = false;
+    iter.base.allow_remove = true;
     iter.base.remove = false;
     return iter;
 }
@@ -1200,8 +1198,8 @@
         const void *elem,
         int prepend
 ) {
-    struct cx_list_s *list = iter->src_handle.m;
-    cx_linked_list *ll = iter->src_handle.m;
+    struct cx_list_s *list = iter->src_handle;
+    cx_linked_list *ll = iter->src_handle;
     void *node = iter->elem_handle;
     if (node != NULL) {
         assert(prepend >= 0 && prepend <= 1);
--- a/src/list.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/list.c	Fri Oct 17 16:53:24 2025 +0200
@@ -115,7 +115,7 @@
         const void *elem,
         int prepend
 ) {
-    struct cx_list_s *list = iter->src_handle.m;
+    struct cx_list_s *list = iter->src_handle;
     return list->climpl->insert_iter(iter, &elem, prepend);
 }
 
@@ -246,7 +246,7 @@
         cx_attr_unused bool backwards
 ) {
     CxIterator iter = {0};
-    iter.src_handle.c = list;
+    iter.src_handle = (void*) list;
     iter.index = index;
     iter.base.valid = cx_emptyl_iter_valid;
     return iter;
@@ -650,13 +650,13 @@
 }
 
 int cxListInsertAfter(CxIterator *iter, const void *elem) {
-    CxList* list = (CxList*)iter->src_handle.m;
+    CxList* list = (CxList*)iter->src_handle;
     list->collection.sorted = false;
     return list->cl->insert_iter(iter, elem, 0);
 }
 
 int cxListInsertBefore(CxIterator *iter, const void *elem) {
-    CxList* list = (CxList*)iter->src_handle.m;
+    CxList* list = (CxList*)iter->src_handle;
     list->collection.sorted = false;
     return list->cl->insert_iter(iter, elem, 1);
 }
@@ -735,40 +735,16 @@
     return list->cl->iterator(list, index, true);
 }
 
-CxIterator cxListMutIteratorAt(CxList *list, size_t index) {
-    if (list == NULL) list = cxEmptyList;
-    CxIterator it = list->cl->iterator(list, index, false);
-    it.base.mutating = true;
-    return it;
-}
-
-CxIterator cxListMutBackwardsIteratorAt(CxList *list, size_t index) {
-    if (list == NULL) list = cxEmptyList;
-    CxIterator it = list->cl->iterator(list, index, true);
-    it.base.mutating = true;
-    return it;
-}
-
 CxIterator cxListIterator(const CxList *list) {
     if (list == NULL) list = cxEmptyList;
     return list->cl->iterator(list, 0, false);
 }
 
-CxIterator cxListMutIterator(CxList *list) {
-    if (list == NULL) list = cxEmptyList;
-    return cxListMutIteratorAt(list, 0);
-}
-
 CxIterator cxListBackwardsIterator(const CxList *list) {
     if (list == NULL) list = cxEmptyList;
     return list->cl->iterator(list, list->collection.size - 1, true);
 }
 
-CxIterator cxListMutBackwardsIterator(CxList *list) {
-    if (list == NULL) list = cxEmptyList;
-    return cxListMutBackwardsIteratorAt(list, list->collection.size - 1);
-}
-
 size_t cxListFind(const CxList *list, const void *elem) {
     return list->cl->find_remove((CxList*)list, elem, false);
 }
--- a/src/map.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/map.c	Fri Oct 17 16:53:24 2025 +0200
@@ -51,7 +51,7 @@
         cx_attr_unused enum cx_map_iterator_type type
 ) {
     CxMapIterator iter = {0};
-    iter.map.c = map;
+    iter.map = (CxMap*) map;
     iter.base.valid = cx_empty_map_iter_valid;
     return iter;
 }
@@ -107,27 +107,6 @@
     return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
 }
 
-CxMapIterator cxMapMutIteratorValues(CxMap *map) {
-    if (map == NULL) map = cxEmptyMap;
-    CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
-    it.base.mutating = true;
-    return it;
-}
-
-CxMapIterator cxMapMutIteratorKeys(CxMap *map) {
-    if (map == NULL) map = cxEmptyMap;
-    CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
-    it.base.mutating = true;
-    return it;
-}
-
-CxMapIterator cxMapMutIterator(CxMap *map) {
-    if (map == NULL) map = cxEmptyMap;
-    CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
-    it.base.mutating = true;
-    return it;
-}
-
 int cx_map_put(CxMap *map, CxHashKey key, void *value) {
     return map->cl->put(map, key, value) == NULL;
 }
--- a/src/tree.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/src/tree.c	Fri Oct 17 16:53:24 2025 +0200
@@ -375,7 +375,7 @@
     iter.skip = false;
 
     // assign base iterator functions
-    iter.base.mutating = false;
+    iter.base.allow_remove = false;
     iter.base.remove = false;
     iter.base.current_impl = NULL;
     iter.base.valid = cx_tree_iter_valid;
@@ -496,7 +496,7 @@
     iter.queue_last = NULL;
 
     // assign base iterator functions
-    iter.base.mutating = false;
+    iter.base.allow_remove = false;
     iter.base.remove = false;
     iter.base.current_impl = NULL;
     iter.base.valid = cx_tree_visitor_valid;
@@ -717,7 +717,7 @@
     }
 
     // otherwise, create iterator and hand over to other function
-    CxIterator iter = cxIterator(src, elem_size, num);
+    CxIterator iter = cxIterator(src, elem_size, num, false);
     return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc,
                             cfunc, cdata, failed, root,
                             loc_parent, loc_children, loc_last_child,
@@ -911,7 +911,7 @@
 size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n) {
     if (n == 0) return 0;
     if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0;
-    CxIterator iter = cxIterator(data, elem_size, n);
+    CxIterator iter = cxIterator(data, elem_size, n, false);
     return cxTreeInsertIter(tree, cxIteratorRef(iter), n);
 }
 
--- a/tests/test_hash_map.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/tests/test_hash_map.c	Fri Oct 17 16:53:24 2025 +0200
@@ -301,7 +301,7 @@
         cxMapPut(map, "key 5", (void *) "val 5");
         cxMapPut(map, "key 6", (void *) "val 6");
 
-        CxMapIterator iter = cxMapMutIterator(map);
+        CxMapIterator iter = cxMapIterator(map);
         cx_foreach(CxMapEntry*, entry, iter) {
             if (((const char *)entry->key->data)[4] % 2 == 1) cxIteratorFlagRemoval(iter);
         }
@@ -403,7 +403,7 @@
     // now remove k1 via key iterator and k5 (val 5) via value iterator
     v1[0] = 'y'; // mark v1 and check that destr is not called for previous val
     {
-        CxMapIterator iter = cxMapMutIteratorKeys(map);
+        CxMapIterator iter = cxMapIteratorKeys(map);
         CX_TEST_ASSERT(iter.elem_count == 4);
         CX_TEST_ASSERT(cxMapSize(map) == 4);
         cx_foreach(CxHashKey*, key, iter) {
@@ -413,7 +413,7 @@
         CX_TEST_ASSERT(cxMapSize(map) == 3);
     }
     {
-        CxMapIterator iter = cxMapMutIteratorValues(map);
+        CxMapIterator iter = cxMapIteratorValues(map);
         cx_foreach(struct test_destr_struct*, v, iter) {
             if (v->str[4] == '5') cxIteratorFlagRemoval(iter);
         }
@@ -511,9 +511,9 @@
     CxMapIterator it1 = cxMapIterator(map);
     CxMapIterator it2 = cxMapIteratorValues(map);
     CxMapIterator it3 = cxMapIteratorKeys(map);
-    CxMapIterator it4 = cxMapMutIterator(map);
-    CxMapIterator it5 = cxMapMutIteratorValues(map);
-    CxMapIterator it6 = cxMapMutIteratorKeys(map);
+    CxMapIterator it4 = cxMapIterator(map);
+    CxMapIterator it5 = cxMapIteratorValues(map);
+    CxMapIterator it6 = cxMapIteratorKeys(map);
 
     CX_TEST_DO {
         CX_TEST_ASSERT(!cxIteratorValid(it1));
@@ -540,9 +540,9 @@
     CxMapIterator it1 = cxMapIterator(map);
     CxMapIterator it2 = cxMapIteratorValues(map);
     CxMapIterator it3 = cxMapIteratorKeys(map);
-    CxMapIterator it4 = cxMapMutIterator(map);
-    CxMapIterator it5 = cxMapMutIteratorValues(map);
-    CxMapIterator it6 = cxMapMutIteratorKeys(map);
+    CxMapIterator it4 = cxMapIterator(map);
+    CxMapIterator it5 = cxMapIteratorValues(map);
+    CxMapIterator it6 = cxMapIteratorKeys(map);
 
     CX_TEST_DO {
         CX_TEST_ASSERT(!cxIteratorValid(it1));
--- a/tests/test_iterator.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/tests/test_iterator.c	Fri Oct 17 16:53:24 2025 +0200
@@ -35,24 +35,24 @@
     size_t size = cx_nmemb(array);
     for (unsigned i = 0 ; i < size ; i++) array[i] = i;
 
-    CxIterator iter = cxIterator(array, sizeof(unsigned), size);
+    CxIterator iter = cxIterator(array, sizeof(unsigned), size, true);
     CX_TEST_DO {
         CX_TEST_ASSERT(iter.index == 0);
         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.src_handle == array);
         CX_TEST_ASSERT(iter.elem_handle == &array[0]);
         CX_TEST_ASSERT(cxIteratorValid(iter));
     }
 }
 
 CX_TEST(test_iterator_create_null) {
-    CxIterator iter = cxIterator(NULL, sizeof(unsigned), 47);
+    CxIterator iter = cxIterator(NULL, sizeof(unsigned), 47, true);
     CX_TEST_DO {
         CX_TEST_ASSERT(iter.index == 0);
         CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
         CX_TEST_ASSERT(iter.elem_count == 0);
-        CX_TEST_ASSERT(iter.src_handle.c == NULL);
+        CX_TEST_ASSERT(iter.src_handle == NULL);
         CX_TEST_ASSERT(iter.elem_handle == NULL);
         CX_TEST_ASSERT(!cxIteratorValid(iter));
     }
@@ -63,11 +63,11 @@
     size_t size = cx_nmemb(array);
     for (unsigned i = 0 ; i < size ; i++) array[i] = i;
 
-    CxIterator iter = cxIterator(array, sizeof(unsigned), size);
+    CxIterator iter = cxIterator(array, sizeof(unsigned), size, true);
     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);
+        CX_TEST_ASSERT(iter.src_handle == array);
         unsigned expected = 0;
         cx_foreach(unsigned *, e, iter) {
             CX_TEST_ASSERT(iter.index == expected);
@@ -88,11 +88,11 @@
         ptr_array[i] = &array[i];
     }
 
-    CxIterator iter = cxIteratorPtr(ptr_array, size);
+    CxIterator iter = cxIteratorPtr(ptr_array, size, true);
     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);
+        CX_TEST_ASSERT(iter.src_handle == ptr_array);
         unsigned idx = 0;
         cx_foreach(unsigned *, e, iter) {
             CX_TEST_ASSERT(iter.index == idx);
@@ -121,7 +121,7 @@
             0, 2, 4, 6, 8, 10, 12, 14, 16, 18
     };
 
-    CxIterator iter = cxMutIterator(array, sizeof(unsigned), size, true);
+    CxIterator iter = cxIterator(array, sizeof(unsigned), size, true);
     CX_TEST_DO {
         unsigned expected = 0;
         cx_foreach(unsigned *, e, iter) {
@@ -129,7 +129,7 @@
             CX_TEST_ASSERT(iter.index == indices[expected]);
             CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
             CX_TEST_ASSERT(iter.elem_count == elem_counts[expected]);
-            CX_TEST_ASSERT(iter.src_handle.m == array);
+            CX_TEST_ASSERT(iter.src_handle == array);
             CX_TEST_ASSERT(iter.elem_handle == &array[indices[expected]]);
             expected++;
             if (expected % 2 == 0) {
@@ -166,7 +166,7 @@
             15, 6, 14, 7, 13, 8, 12, 9, 11, 10
     };
 
-    CxIterator iter = cxMutIterator(array, sizeof(unsigned), size, false);
+    CxIterator iter = cxIterator(array, sizeof(unsigned), size, false);
     CX_TEST_DO {
         unsigned expected = 0;
         cx_foreach(unsigned *, e, iter) {
@@ -174,7 +174,7 @@
             CX_TEST_ASSERT(iter.index == indices[expected]);
             CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
             CX_TEST_ASSERT(iter.elem_count == elem_counts[expected]);
-            CX_TEST_ASSERT(iter.src_handle.m == array);
+            CX_TEST_ASSERT(iter.src_handle == array);
             CX_TEST_ASSERT(iter.elem_handle == &array[indices[expected]]);
             expected++;
             if (expected % 2 == 0) {
--- a/tests/test_kv_list.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/tests/test_kv_list.c	Fri Oct 17 16:53:24 2025 +0200
@@ -956,7 +956,7 @@
         cxMapPut(map, "efghi", &z);
 
         kv_list_test_destr_val = 0;
-        CxMapIterator iter = cxMapMutIteratorValues(map);
+        CxMapIterator iter = cxMapIteratorValues(map);
         cx_foreach(int *, elem, iter) {
             if (*elem == 8016) {
                 cxIteratorFlagRemoval(iter);
@@ -989,7 +989,7 @@
         CxList *list = cxKvListAsList(map);
         kv_list_test_destr2_val = 0;
         kv_list_test_destr2_extra = NULL;
-        CxIterator iter = cxListMutIterator(list);
+        CxIterator iter = cxListIterator(list);
         cx_foreach(int *, elem, iter) {
             if (*elem == 8016) {
                 cxIteratorFlagRemoval(iter);
--- a/tests/test_list.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/tests/test_list.c	Fri Oct 17 16:53:24 2025 +0200
@@ -1119,8 +1119,25 @@
 
     CxIterator it1 = cxListIterator(list);
     CxIterator it2 = cxListBackwardsIterator(list);
-    CxIterator it3 = cxListMutIterator(list);
-    CxIterator it4 = cxListMutBackwardsIterator(list);
+
+    CX_TEST_DO {
+        CX_TEST_ASSERT(!cxIteratorValid(it1));
+        CX_TEST_ASSERT(!cxIteratorValid(it2));
+
+        int c = 0;
+        cx_foreach(void*, data, it1) c++;
+        cx_foreach(void*, data, it2) c++;
+        CX_TEST_ASSERT(c == 0);
+    }
+}
+
+CX_TEST(test_null_list_iterator) {
+    CxList *list = NULL;
+
+    CxIterator it1 = cxListIterator(list);
+    CxIterator it2 = cxListBackwardsIterator(list);
+    CxIterator it3 = cxListIteratorAt(list, 0);
+    CxIterator it4 = cxListBackwardsIteratorAt(list, 0);
 
     CX_TEST_DO {
         CX_TEST_ASSERT(!cxIteratorValid(it1));
@@ -1137,35 +1154,6 @@
     }
 }
 
-CX_TEST(test_null_list_iterator) {
-    CxList *list = NULL;
-
-    CxIterator it1 = cxListIterator(list);
-    CxIterator it2 = cxListBackwardsIterator(list);
-    CxIterator it3 = cxListMutIterator(list);
-    CxIterator it4 = cxListMutBackwardsIterator(list);
-    CxIterator it5 = cxListMutIteratorAt(list, 0);
-    CxIterator it6 = cxListMutBackwardsIteratorAt(list, 0);
-
-    CX_TEST_DO {
-        CX_TEST_ASSERT(!cxIteratorValid(it1));
-        CX_TEST_ASSERT(!cxIteratorValid(it2));
-        CX_TEST_ASSERT(!cxIteratorValid(it3));
-        CX_TEST_ASSERT(!cxIteratorValid(it4));
-        CX_TEST_ASSERT(!cxIteratorValid(it5));
-        CX_TEST_ASSERT(!cxIteratorValid(it6));
-
-        int c = 0;
-        cx_foreach(void*, data, it1) c++;
-        cx_foreach(void*, data, it2) c++;
-        cx_foreach(void*, data, it3) c++;
-        cx_foreach(void*, data, it4) c++;
-        cx_foreach(void*, data, it5) c++;
-        cx_foreach(void*, data, it6) c++;
-        CX_TEST_ASSERT(c == 0);
-    }
-}
-
 CX_TEST(test_empty_list_noops) {
     CX_TEST_DO {
         CxList copy = *cxEmptyList;
@@ -2279,7 +2267,7 @@
     }
     CX_TEST_ASSERT(i == 0);
     i = len / 2;
-    CxIterator mut_iter = cxListMutIteratorAt(list, i);
+    CxIterator mut_iter = cxListIteratorAt(list, i);
     CX_TEST_ASSERT(mut_iter.elem_size == list->collection.elem_size);
     CX_TEST_ASSERT(mut_iter.elem_count == list->collection.size);
     size_t j = 0;
@@ -2293,7 +2281,7 @@
     CX_TEST_ASSERT(i == len);
     i = len / 2;
     j = 0;
-    mut_iter = cxListMutBackwardsIteratorAt(list, i - 1);
+    mut_iter = cxListBackwardsIteratorAt(list, i - 1);
     cx_foreach(int*, x, mut_iter) {
         CX_TEST_ASSERT(mut_iter.index == len / 2 - 1 - j);
         CX_TEST_ASSERT(*x == testdata[i - 1]);
@@ -2318,7 +2306,7 @@
     }
     int newdata[] = array_init(10, 20, 30, 40, 50);
 
-    CxIterator iter = cxListMutIteratorAt(list, 2);
+    CxIterator iter = cxListIteratorAt(list, 2);
     CX_TEST_ASSERT(cxIteratorValid(iter));
     CX_TEST_ASSERT(iter.index == 2);
     CX_TEST_ASSERT(*(int *) cxIteratorCurrent(iter) == 2);
@@ -2334,18 +2322,18 @@
     CX_TEST_ASSERT(*(int *) cxIteratorCurrent(iter) == 2);
     CX_TEST_ASSERT(iter.elem_count == 7);
 
-    iter = cxListMutIterator(list);
+    iter = cxListIterator(list);
     cxListInsertBefore(&iter, &newdata[2]);
     CX_TEST_ASSERT(cxIteratorValid(iter));
     CX_TEST_ASSERT(iter.index == 1);
     CX_TEST_ASSERT(*(int *) cxIteratorCurrent(iter) == 0);
     CX_TEST_ASSERT(iter.elem_count == 8);
-    iter = cxListMutIteratorAt(list, cxListSize(list));
+    iter = cxListIteratorAt(list, cxListSize(list));
     cxListInsertBefore(&iter, &newdata[3]);
     CX_TEST_ASSERT(!cxIteratorValid(iter));
     CX_TEST_ASSERT(iter.index == 9);
     CX_TEST_ASSERT(iter.elem_count == 9);
-    iter = cxListMutIteratorAt(list, cxListSize(list));
+    iter = cxListIteratorAt(list, cxListSize(list));
     cxListInsertAfter(&iter, &newdata[4]);
     CX_TEST_ASSERT(!cxIteratorValid(iter));
     CX_TEST_ASSERT(iter.index == 10);
@@ -2444,7 +2432,7 @@
     CX_TEST_ASSERT(testdata[48] == destr_last_value + off);
     CX_TEST_ASSERT(testdata_len - destr_test_ctr == cxListSize(list));
 
-    CxIterator iter = cxListMutIteratorAt(list, 7);
+    CxIterator iter = cxListIteratorAt(list, 7);
     CX_TEST_ASSERT(iter.elem_count == testdata_len - 2);
     cxIteratorNext(iter);
     CX_TEST_ASSERT(2 == destr_test_ctr);
@@ -2457,7 +2445,7 @@
     CX_TEST_ASSERT(testdata[8] == destr_last_value + off);
     CX_TEST_ASSERT(testdata_len - destr_test_ctr == cxListSize(list));
 
-    iter = cxListMutBackwardsIteratorAt(list, 5);
+    iter = cxListBackwardsIteratorAt(list, 5);
     cxIteratorNext(iter);
     CX_TEST_ASSERT(3 == destr_test_ctr);
     CX_TEST_ASSERT(testdata[8] == destr_last_value + off);
--- a/tests/test_tree.c	Fri Oct 17 15:04:56 2025 +0200
+++ b/tests/test_tree.c	Fri Oct 17 16:53:24 2025 +0200
@@ -657,7 +657,7 @@
         CX_TEST_ASSERT(!iter.exiting);
         CX_TEST_ASSERT(iter.counter == 1);
         CX_TEST_ASSERT(iter.node == &root);
-        CX_TEST_ASSERT(!iter.base.mutating);
+        CX_TEST_ASSERT(!iter.base.allow_remove);
         CX_TEST_ASSERT(!iter.base.remove);
         CX_TEST_ASSERT(iter.stack != NULL);
         CX_TEST_ASSERT(iter.stack_capacity > 0);
@@ -677,7 +677,7 @@
         CX_TEST_ASSERT(!iter.exiting);
         CX_TEST_ASSERT(iter.counter == 0);
         CX_TEST_ASSERT(iter.node == NULL);
-        CX_TEST_ASSERT(!iter.base.mutating);
+        CX_TEST_ASSERT(!iter.base.allow_remove);
         CX_TEST_ASSERT(!iter.base.remove);
         CX_TEST_ASSERT(iter.stack == NULL);
         CX_TEST_ASSERT(iter.stack_capacity == 0);
@@ -1017,7 +1017,7 @@
         CxTreeVisitor iter = cx_tree_visitor(&root, tree_children(tree_node));
         CX_TEST_ASSERT(iter.counter == 1);
         CX_TEST_ASSERT(iter.node == &root);
-        CX_TEST_ASSERT(!iter.base.mutating);
+        CX_TEST_ASSERT(!iter.base.allow_remove);
         CX_TEST_ASSERT(!iter.base.remove);
         CX_TEST_ASSERT(iter.queue_next != NULL);
         CX_TEST_ASSERT(iter.queue_last != NULL);
@@ -1559,7 +1559,7 @@
         CX_TEST_ASSERT(processed == 0);
         CX_TEST_ASSERT(talloc.alloc_total == 0);
 
-        CxIterator iter = cxIterator(NULL, sizeof(void *), 0);
+        CxIterator iter = cxIterator(NULL, sizeof(void *), 0, false);
         processed = cx_tree_add_iter(
                 cxIteratorRef(iter),
                 10, // deliberately specify more than the iter has
@@ -1669,7 +1669,7 @@
                 "/usr/lib/foo.so"
         };
         // create iterator for 4 elements, but choose to insert only 3 of them
-        CxIterator iter = cxIterator(paths, sizeof(const char*), 4);
+        CxIterator iter = cxIterator(paths, sizeof(const char*), 4, false);
         size_t processed = cx_tree_add_iter(
                 cxIteratorRef(iter), 3,
                 tree_node_file_search,

mercurial