add iterator over raw C arrays - closes #389

Thu, 23 May 2024 15:05:24 +0200

author
Mike Becker <universe@uap-core.de>
date
Thu, 23 May 2024 15:05:24 +0200
changeset 850
b2bc48c2b251
parent 849
edb9f875b7f9
child 851
adb4e0737c33

add iterator over raw C arrays - closes #389

CHANGELOG file | annotate | diff | comparison | revisions
src/Makefile file | annotate | diff | comparison | revisions
src/array_list.c file | annotate | diff | comparison | revisions
src/cx/iterator.h file | annotate | diff | comparison | revisions
src/hash_map.c file | annotate | diff | comparison | revisions
src/iterator.c file | annotate | diff | comparison | revisions
src/linked_list.c file | annotate | diff | comparison | revisions
tests/Makefile 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_list.c file | annotate | diff | comparison | revisions
tests/ucxtest.c file | annotate | diff | comparison | revisions
     1.1 --- a/CHANGELOG	Fri Apr 12 21:48:12 2024 +0200
     1.2 +++ b/CHANGELOG	Thu May 23 15:05:24 2024 +0200
     1.3 @@ -1,6 +1,7 @@
     1.4  Version 3.1 - tbd.
     1.5  ------------------------
     1.6   * adds tree.h
     1.7 + * adds cxIterator() to create iterators over raw C arrays
     1.8   * adds cx_array_default_reallocator
     1.9   * adds several new cx_array_*() functions
    1.10   * adds cx_linked_list_find_node()
     2.1 --- a/src/Makefile	Fri Apr 12 21:48:12 2024 +0200
     2.2 +++ b/src/Makefile	Thu May 23 15:05:24 2024 +0200
     2.3 @@ -24,7 +24,8 @@
     2.4  include ../config.mk
     2.5  
     2.6  SRC = allocator.c array_list.c buffer.c compare.c hash_key.c hash_map.c \
     2.7 -  linked_list.c list.c map.c tree.c mempool.c printf.c string.c utils.c
     2.8 +  iterator.c linked_list.c list.c map.c mempool.c printf.c string.c tree.c \
     2.9 +  utils.c
    2.10  
    2.11  OBJ_EXT=.o
    2.12  OBJ=$(SRC:%.c=$(build_dir)/%$(OBJ_EXT))
    2.13 @@ -89,6 +90,10 @@
    2.14  	@echo "Compiling $<"
    2.15  	$(CC) -o $@ $(CFLAGS) -c $<
    2.16  
    2.17 +$(build_dir)/iterator$(OBJ_EXT): iterator.c cx/iterator.h cx/common.h
    2.18 +	@echo "Compiling $<"
    2.19 +	$(CC) -o $@ $(CFLAGS) -c $<
    2.20 +
    2.21  $(build_dir)/linked_list$(OBJ_EXT): linked_list.c cx/linked_list.h \
    2.22   cx/common.h cx/list.h cx/collection.h cx/allocator.h cx/iterator.h \
    2.23   cx/utils.h cx/compare.h
     3.1 --- a/src/array_list.c	Fri Apr 12 21:48:12 2024 +0200
     3.2 +++ b/src/array_list.c	Thu May 23 15:05:24 2024 +0200
     3.3 @@ -503,6 +503,8 @@
     3.4      iter.index = index;
     3.5      iter.src_handle = list;
     3.6      iter.elem_handle = cx_arl_at(list, index);
     3.7 +    iter.elem_size = list->item_size;
     3.8 +    iter.elem_count = list->size;
     3.9      iter.base.valid = cx_arl_iter_valid;
    3.10      iter.base.current = cx_arl_iter_current;
    3.11      iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
     4.1 --- a/src/cx/iterator.h	Fri Apr 12 21:48:12 2024 +0200
     4.2 +++ b/src/cx/iterator.h	Thu May 23 15:05:24 2024 +0200
     4.3 @@ -127,6 +127,17 @@
     4.4       * Otherwise, this field is usually uninitialized.
     4.5       */
     4.6      size_t index;
     4.7 +
     4.8 +    /**
     4.9 +     * The size of an individual element.
    4.10 +     */
    4.11 +    size_t elem_size;
    4.12 +
    4.13 +    /**
    4.14 +     * May contain the total number of elements, if known.
    4.15 +     * Shall be set to \c SIZE_MAX when the total number is unknown during iteration.
    4.16 +     */
    4.17 +    size_t elem_count;
    4.18  };
    4.19  
    4.20  /**
    4.21 @@ -191,6 +202,17 @@
    4.22       * Otherwise, this field is usually uninitialized.
    4.23       */
    4.24      size_t index;
    4.25 +
    4.26 +    /**
    4.27 +     * The size of an individual element.
    4.28 +     */
    4.29 +    size_t elem_size;
    4.30 +
    4.31 +    /**
    4.32 +     * May contain the total number of elements, if known.
    4.33 +     * Shall be set to \c SIZE_MAX when the total number is unknown during iteration.
    4.34 +     */
    4.35 +    size_t elem_count;
    4.36  };
    4.37  
    4.38  /**
    4.39 @@ -250,4 +272,36 @@
    4.40  #define cx_foreach(type, elem, iter) \
    4.41  for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter)) != NULL ; cxIteratorNext(iter))
    4.42  
    4.43 +
    4.44 +/**
    4.45 + * Creates an iterator for the specified plain array.
    4.46 + *
    4.47 + * While the iterator is in use, the array may only be altered by removing
    4.48 + * elements through #cxIteratorFlagRemoval(). Every other change to the array
    4.49 + * will bring this iterator to an undefined state.
    4.50 + *
    4.51 + * When \p remove_keeps_order is set to \c false, removing an element will only
    4.52 + * move the last element to the position of the removed element, instead of
    4.53 + * moving all subsequent elements by one. Usually, when the order of elements is
    4.54 + * not important, this parameter should be set to \c false.
    4.55 + *
    4.56 + * The \p array can be \c NULL in which case the iterator will be immediately
    4.57 + * initialized such that #cxIteratorValid() returns \c false.
    4.58 + *
    4.59 + *
    4.60 + * @param array a pointer to the array (can be \c NULL)
    4.61 + * @param elem_size the size of one array element
    4.62 + * @param elem_count the number of elements in the array
    4.63 + * @param remove_keeps_order \c true if the order of elements must be preserved
    4.64 + * when removing an element
    4.65 + * @return an iterator for the specified array
    4.66 + */
    4.67 +__attribute__((__warn_unused_result__))
    4.68 +CxMutIterator cxIterator(  // TODO: unify the iterator types
    4.69 +        void *array,
    4.70 +        size_t elem_size,
    4.71 +        size_t elem_count,
    4.72 +        bool remove_keeps_order
    4.73 +);
    4.74 +
    4.75  #endif // UCX_ITERATOR_H
     5.1 --- a/src/hash_map.c	Fri Apr 12 21:48:12 2024 +0200
     5.2 +++ b/src/hash_map.c	Thu May 23 15:05:24 2024 +0200
     5.3 @@ -333,23 +333,27 @@
     5.4      CxIterator iter;
     5.5  
     5.6      iter.src_handle = map;
     5.7 -    iter.base.valid = cx_hash_map_iter_valid;
     5.8 -    iter.base.next = cx_hash_map_iter_next;
     5.9 +    iter.elem_count = map->size;
    5.10  
    5.11      switch (type) {
    5.12          case CX_MAP_ITERATOR_PAIRS:
    5.13 +            iter.elem_size = sizeof(CxMapEntry);
    5.14              iter.base.current = cx_hash_map_iter_current_entry;
    5.15              break;
    5.16          case CX_MAP_ITERATOR_KEYS:
    5.17 +            iter.elem_size = sizeof(CxHashKey);
    5.18              iter.base.current = cx_hash_map_iter_current_key;
    5.19              break;
    5.20          case CX_MAP_ITERATOR_VALUES:
    5.21 +            iter.elem_size = map->item_size;
    5.22              iter.base.current = cx_hash_map_iter_current_value;
    5.23              break;
    5.24          default:
    5.25              assert(false);
    5.26      }
    5.27  
    5.28 +    iter.base.valid = cx_hash_map_iter_valid;
    5.29 +    iter.base.next = cx_hash_map_iter_next;
    5.30      iter.base.remove = false;
    5.31      iter.base.mutating = false;
    5.32  
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/iterator.c	Thu May 23 15:05:24 2024 +0200
     6.3 @@ -0,0 +1,106 @@
     6.4 +/*
     6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     6.6 + *
     6.7 + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved.
     6.8 + *
     6.9 + * Redistribution and use in source and binary forms, with or without
    6.10 + * modification, are permitted provided that the following conditions are met:
    6.11 + *
    6.12 + *   1. Redistributions of source code must retain the above copyright
    6.13 + *      notice, this list of conditions and the following disclaimer.
    6.14 + *
    6.15 + *   2. Redistributions in binary form must reproduce the above copyright
    6.16 + *      notice, this list of conditions and the following disclaimer in the
    6.17 + *      documentation and/or other materials provided with the distribution.
    6.18 + *
    6.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    6.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    6.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    6.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    6.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    6.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    6.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    6.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    6.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    6.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    6.29 + * POSSIBILITY OF SUCH DAMAGE.
    6.30 + */
    6.31 +
    6.32 +#include "cx/iterator.h"
    6.33 +
    6.34 +#include <string.h>
    6.35 +
    6.36 +static bool cx_iter_valid(void const *it) {
    6.37 +    struct cx_iterator_s const *iter = it;
    6.38 +    return iter->index < iter->elem_count;
    6.39 +}
    6.40 +
    6.41 +static void *cx_iter_current(void const *it) {
    6.42 +    struct cx_iterator_s const *iter = it;
    6.43 +    return iter->elem_handle;
    6.44 +}
    6.45 +
    6.46 +static void cx_iter_next_fast(void *it) {
    6.47 +    struct cx_iterator_base_s *itbase = it;
    6.48 +    if (itbase->remove) {
    6.49 +        struct cx_mut_iterator_s *iter = it;
    6.50 +        itbase->remove = false;
    6.51 +        iter->elem_count--;
    6.52 +        // only move the last element when we are not currently aiming
    6.53 +        // at the last element already
    6.54 +        if (iter->index < iter->elem_count) {
    6.55 +            void *last = ((char *) iter->src_handle)
    6.56 +                         + iter->elem_count * iter->elem_size;
    6.57 +            memcpy(iter->elem_handle, last, iter->elem_size);
    6.58 +        }
    6.59 +    } else {
    6.60 +        struct cx_iterator_s *iter = it;
    6.61 +        iter->index++;
    6.62 +        iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size;
    6.63 +    }
    6.64 +}
    6.65 +
    6.66 +static void cx_iter_next_slow(void *it) {
    6.67 +    struct cx_iterator_base_s *itbase = it;
    6.68 +    if (itbase->remove) {
    6.69 +        struct cx_mut_iterator_s *iter = it;
    6.70 +        itbase->remove = false;
    6.71 +        iter->elem_count--;
    6.72 +
    6.73 +        // number of elements to move
    6.74 +        size_t remaining = iter->elem_count - iter->index;
    6.75 +        if (remaining > 0) {
    6.76 +            memmove(
    6.77 +                    iter->elem_handle,
    6.78 +                    ((char *) iter->elem_handle) + iter->elem_size,
    6.79 +                    remaining * iter->elem_size
    6.80 +            );
    6.81 +        }
    6.82 +    } else {
    6.83 +        struct cx_iterator_s *iter = it;
    6.84 +        iter->index++;
    6.85 +        iter->elem_handle = ((char *) iter->elem_handle) + iter->elem_size;
    6.86 +    }
    6.87 +}
    6.88 +
    6.89 +CxMutIterator cxIterator(
    6.90 +        void *array,
    6.91 +        size_t elem_size,
    6.92 +        size_t elem_count,
    6.93 +        bool remove_keeps_order
    6.94 +) {
    6.95 +    CxMutIterator iter;
    6.96 +
    6.97 +    iter.index = 0;
    6.98 +    iter.src_handle = array;
    6.99 +    iter.elem_handle = array;
   6.100 +    iter.elem_size = elem_size;
   6.101 +    iter.elem_count = array == NULL ? 0 : elem_count;
   6.102 +    iter.base.valid = cx_iter_valid;
   6.103 +    iter.base.current = cx_iter_current;
   6.104 +    iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast;
   6.105 +    iter.base.remove = false;
   6.106 +    iter.base.mutating = true;
   6.107 +
   6.108 +    return iter;
   6.109 +}
     7.1 --- a/src/linked_list.c	Fri Apr 12 21:48:12 2024 +0200
     7.2 +++ b/src/linked_list.c	Thu May 23 15:05:24 2024 +0200
     7.3 @@ -875,6 +875,8 @@
     7.4      iter.index = index;
     7.5      iter.src_handle = list;
     7.6      iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index);
     7.7 +    iter.elem_size = list->item_size;
     7.8 +    iter.elem_count = list->size;
     7.9      iter.base.valid = cx_ll_iter_valid;
    7.10      iter.base.current = cx_ll_iter_current;
    7.11      iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next;
     8.1 --- a/tests/Makefile	Fri Apr 12 21:48:12 2024 +0200
     8.2 +++ b/tests/Makefile	Thu May 23 15:05:24 2024 +0200
     8.3 @@ -28,8 +28,9 @@
     8.4  TEST_DIR=$(build_dir)/tests
     8.5  
     8.6  SRC = util_allocator.c test_utils.c test_hash_key.c test_allocator.c \
     8.7 -	test_compare.c test_string.c test_buffer.c test_list.c test_tree.c \
     8.8 -	test_printf.c test_mempool.c test_hash_map.c ucxtest.c
     8.9 +	test_compare.c test_string.c test_buffer.c test_iterator.c \
    8.10 +	test_list.c test_tree.c test_hash_map.c \
    8.11 +	test_printf.c test_mempool.c ucxtest.c
    8.12  
    8.13  OBJ_EXT=.o
    8.14  OBJ=$(SRC:%.c=$(TEST_DIR)/%$(OBJ_EXT))
    8.15 @@ -77,6 +78,11 @@
    8.16  	@echo "Compiling $<"
    8.17  	$(CC) -o $@ $(CFLAGS) -c $<
    8.18  
    8.19 +$(TEST_DIR)/test_iterator$(OBJ_EXT): test_iterator.c ../src/cx/test.h \
    8.20 + ../src/cx/iterator.h ../src/cx/common.h
    8.21 +	@echo "Compiling $<"
    8.22 +	$(CC) -o $@ $(CFLAGS) -c $<
    8.23 +
    8.24  $(TEST_DIR)/test_list$(OBJ_EXT): test_list.c ../src/cx/test.h \
    8.25   util_allocator.h ../src/cx/allocator.h ../src/cx/common.h \
    8.26   ../src/cx/compare.h ../src/cx/utils.h ../src/cx/array_list.h \
     9.1 --- a/tests/test_hash_map.c	Fri Apr 12 21:48:12 2024 +0200
     9.2 +++ b/tests/test_hash_map.c	Thu May 23 15:05:24 2024 +0200
     9.3 @@ -545,6 +545,8 @@
     9.4      {
     9.5          // collect the keys from the map iterator
     9.6          CxIterator keyiter = cxMapIteratorKeys(map);
     9.7 +        CX_TEST_ASSERT(keyiter.elem_size == sizeof(CxHashKey));
     9.8 +        CX_TEST_ASSERT(keyiter.elem_count == map->size);
     9.9          CxHashKey *keys = calloc(map->size, sizeof(CxHashKey));
    9.10          cx_foreach(CxHashKey*, elem, keyiter) {
    9.11              keys[keyiter.index] = *elem;
    9.12 @@ -564,6 +566,8 @@
    9.13          // by using that the values in our test data are unique strings
    9.14          // we can re-use a similar approach as above
    9.15          CxIterator valiter = cxMapIteratorValues(map);
    9.16 +        CX_TEST_ASSERT(valiter.elem_size == map->item_size);
    9.17 +        CX_TEST_ASSERT(valiter.elem_count == map->size);
    9.18          char const** values = calloc(map->size, sizeof(char const*));
    9.19          cx_foreach(char const*, elem, valiter) {
    9.20              values[valiter.index] = elem;
    9.21 @@ -586,6 +590,8 @@
    9.22      // verify pair iterator
    9.23      {
    9.24          CxIterator pairiter = cxMapIterator(map);
    9.25 +        CX_TEST_ASSERT(pairiter.elem_size == sizeof(CxMapEntry));
    9.26 +        CX_TEST_ASSERT(pairiter.elem_count == map->size);
    9.27          struct test_map_kv *pairs = calloc(map->size, sizeof(struct test_map_kv));
    9.28          cx_foreach(CxMapEntry*, entry, pairiter) {
    9.29              CxHashKey const *key = entry->key;
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/tests/test_iterator.c	Thu May 23 15:05:24 2024 +0200
    10.3 @@ -0,0 +1,178 @@
    10.4 +/*
    10.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    10.6 + *
    10.7 + * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved.
    10.8 + *
    10.9 + * Redistribution and use in source and binary forms, with or without
   10.10 + * modification, are permitted provided that the following conditions are met:
   10.11 + *
   10.12 + *   1. Redistributions of source code must retain the above copyright
   10.13 + *      notice, this list of conditions and the following disclaimer.
   10.14 + *
   10.15 + *   2. Redistributions in binary form must reproduce the above copyright
   10.16 + *      notice, this list of conditions and the following disclaimer in the
   10.17 + *      documentation and/or other materials provided with the distribution.
   10.18 + *
   10.19 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   10.20 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   10.21 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   10.22 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   10.23 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   10.24 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   10.25 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   10.26 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   10.27 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   10.28 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   10.29 + * POSSIBILITY OF SUCH DAMAGE.
   10.30 + */
   10.31 +
   10.32 +#include "cx/test.h"
   10.33 +
   10.34 +#include "cx/iterator.h"
   10.35 +
   10.36 +CX_TEST(test_iterator_create) {
   10.37 +    size_t size = 20;
   10.38 +    unsigned array[size];
   10.39 +    for (unsigned i = 0 ; i < size ; i++) array[i] = i;
   10.40 +
   10.41 +    CxMutIterator iter = cxIterator(array, sizeof(unsigned), size, false);
   10.42 +    CX_TEST_DO {
   10.43 +        CX_TEST_ASSERT(iter.index == 0);
   10.44 +        CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
   10.45 +        CX_TEST_ASSERT(iter.elem_count == size);
   10.46 +        CX_TEST_ASSERT(iter.src_handle == array);
   10.47 +        CX_TEST_ASSERT(iter.elem_handle == &array[0]);
   10.48 +        CX_TEST_ASSERT(cxIteratorValid(iter));
   10.49 +    }
   10.50 +}
   10.51 +
   10.52 +CX_TEST(test_iterator_create_null) {
   10.53 +    CxMutIterator iter = cxIterator(NULL, sizeof(unsigned), 47, false);
   10.54 +    CX_TEST_DO {
   10.55 +        CX_TEST_ASSERT(iter.index == 0);
   10.56 +        CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
   10.57 +        CX_TEST_ASSERT(iter.elem_count == 0);
   10.58 +        CX_TEST_ASSERT(iter.src_handle == NULL);
   10.59 +        CX_TEST_ASSERT(iter.elem_handle == NULL);
   10.60 +        CX_TEST_ASSERT(!cxIteratorValid(iter));
   10.61 +    }
   10.62 +}
   10.63 +
   10.64 +CX_TEST(test_iterator_iterate) {
   10.65 +    size_t size = 20;
   10.66 +    unsigned array[size];
   10.67 +    for (unsigned i = 0 ; i < size ; i++) array[i] = i;
   10.68 +
   10.69 +    CxMutIterator iter = cxIterator(array, sizeof(unsigned), size, false);
   10.70 +    CX_TEST_DO {
   10.71 +        unsigned expected = 0;
   10.72 +        cx_foreach(unsigned *, e, iter) {
   10.73 +            CX_TEST_ASSERT(iter.index == expected);
   10.74 +            CX_TEST_ASSERT(*e == expected);
   10.75 +            CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
   10.76 +            CX_TEST_ASSERT(iter.elem_count == size);
   10.77 +            CX_TEST_ASSERT(iter.src_handle == array);
   10.78 +            CX_TEST_ASSERT(iter.elem_handle == &array[expected]);
   10.79 +            expected++;
   10.80 +        }
   10.81 +        CX_TEST_ASSERT(expected == size);
   10.82 +    }
   10.83 +}
   10.84 +
   10.85 +CX_TEST(test_iterator_with_slow_remove) {
   10.86 +    size_t size = 20;
   10.87 +    unsigned array[size];
   10.88 +    for (unsigned i = 0 ; i < size ; i++) array[i] = i;
   10.89 +
   10.90 +    size_t elem_counts[] = {
   10.91 +            20, 20, 19, 19, 18, 18, 17, 17, 16, 16,
   10.92 +            15, 15, 14, 14, 13, 13, 12, 12, 11, 11
   10.93 +    };
   10.94 +    size_t indices[] = {
   10.95 +            0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
   10.96 +            6, 6, 7, 7, 8, 8, 9, 9, 10
   10.97 +    };
   10.98 +    unsigned expected_result[] = {
   10.99 +            0, 2, 4, 6, 8, 10, 12, 14, 16, 18
  10.100 +    };
  10.101 +
  10.102 +    CxMutIterator iter = cxIterator(array, sizeof(unsigned), size, true);
  10.103 +    CX_TEST_DO {
  10.104 +        unsigned expected = 0;
  10.105 +        cx_foreach(unsigned *, e, iter) {
  10.106 +            CX_TEST_ASSERT(*e == expected);
  10.107 +            CX_TEST_ASSERT(iter.index == indices[expected]);
  10.108 +            CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
  10.109 +            CX_TEST_ASSERT(iter.elem_count == elem_counts[expected]);
  10.110 +            CX_TEST_ASSERT(iter.src_handle == array);
  10.111 +            CX_TEST_ASSERT(iter.elem_handle == &array[indices[expected]]);
  10.112 +            expected++;
  10.113 +            if (expected % 2 == 0) {
  10.114 +                cxIteratorFlagRemoval(iter);
  10.115 +            }
  10.116 +        }
  10.117 +        CX_TEST_ASSERT(expected == 20);
  10.118 +        CX_TEST_ASSERT(iter.index == 10);
  10.119 +        CX_TEST_ASSERT(iter.elem_count == 10);
  10.120 +        for (unsigned i = 0 ; i < 9 ; i++) {
  10.121 +            CX_TEST_ASSERT(array[i] == expected_result[i]);
  10.122 +        }
  10.123 +    }
  10.124 +}
  10.125 +
  10.126 +CX_TEST(test_iterator_with_fast_remove) {
  10.127 +    size_t size = 20;
  10.128 +    unsigned array[size];
  10.129 +    for (unsigned i = 0 ; i < size ; i++) array[i] = i;
  10.130 +
  10.131 +    size_t elem_counts[] = {
  10.132 +            20, 20, 19, 19, 18, 18, 17, 17, 16, 16,
  10.133 +            15, 15, 14, 14, 13, 13, 12, 12, 11, 11
  10.134 +    };
  10.135 +    size_t indices[] = {
  10.136 +            0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
  10.137 +            6, 6, 7, 7, 8, 8, 9, 9, 10
  10.138 +    };
  10.139 +    unsigned expected_result[] = {
  10.140 +            0, 19, 18, 17, 16, 15, 14, 13, 12, 11
  10.141 +    };
  10.142 +    unsigned expected_visits[] = {
  10.143 +            0, 1, 19, 2, 18, 3, 17, 4, 16, 5,
  10.144 +            15, 6, 14, 7, 13, 8, 12, 9, 11, 10
  10.145 +    };
  10.146 +
  10.147 +    CxMutIterator iter = cxIterator(array, sizeof(unsigned), size, false);
  10.148 +    CX_TEST_DO {
  10.149 +        unsigned expected = 0;
  10.150 +        cx_foreach(unsigned *, e, iter) {
  10.151 +            CX_TEST_ASSERT(*e == expected_visits[expected]);
  10.152 +            CX_TEST_ASSERT(iter.index == indices[expected]);
  10.153 +            CX_TEST_ASSERT(iter.elem_size == sizeof(unsigned));
  10.154 +            CX_TEST_ASSERT(iter.elem_count == elem_counts[expected]);
  10.155 +            CX_TEST_ASSERT(iter.src_handle == array);
  10.156 +            CX_TEST_ASSERT(iter.elem_handle == &array[indices[expected]]);
  10.157 +            expected++;
  10.158 +            if (expected % 2 == 0) {
  10.159 +                cxIteratorFlagRemoval(iter);
  10.160 +            }
  10.161 +        }
  10.162 +        CX_TEST_ASSERT(expected == 20);
  10.163 +        CX_TEST_ASSERT(iter.index == 10);
  10.164 +        CX_TEST_ASSERT(iter.elem_count == 10);
  10.165 +        for (unsigned i = 0 ; i < 9 ; i++) {
  10.166 +            CX_TEST_ASSERT(array[i] == expected_result[i]);
  10.167 +        }
  10.168 +    }
  10.169 +}
  10.170 +
  10.171 +CxTestSuite *cx_test_suite_iterator(void) {
  10.172 +    CxTestSuite *suite = cx_test_suite_new("iterator");
  10.173 +
  10.174 +    cx_test_register(suite, test_iterator_create);
  10.175 +    cx_test_register(suite, test_iterator_iterate);
  10.176 +    cx_test_register(suite, test_iterator_with_slow_remove);
  10.177 +    cx_test_register(suite, test_iterator_with_fast_remove);
  10.178 +
  10.179 +    return suite;
  10.180 +}
  10.181 +
    11.1 --- a/tests/test_list.c	Fri Apr 12 21:48:12 2024 +0200
    11.2 +++ b/tests/test_list.c	Thu May 23 15:05:24 2024 +0200
    11.3 @@ -1207,6 +1207,8 @@
    11.4      int *testdata = int_test_data_added_to_list(list, isptrlist, len);
    11.5  
    11.6      CxIterator iter = cxListIterator(list);
    11.7 +    CX_TEST_ASSERT(iter.elem_size == list->item_size);
    11.8 +    CX_TEST_ASSERT(iter.elem_count == list->size);
    11.9      size_t i = 0;
   11.10      cx_foreach(int*, x, iter) {
   11.11          CX_TEST_ASSERT(i == iter.index);
   11.12 @@ -1223,6 +1225,8 @@
   11.13      CX_TEST_ASSERT(i == 0);
   11.14      i = len / 2;
   11.15      CxMutIterator mut_iter = cxListMutIteratorAt(list, i);
   11.16 +    CX_TEST_ASSERT(mut_iter.elem_size == list->item_size);
   11.17 +    CX_TEST_ASSERT(mut_iter.elem_count == list->size);
   11.18      size_t j = 0;
   11.19      cx_foreach(int*, x, mut_iter) {
   11.20          CX_TEST_ASSERT(mut_iter.index == len / 2 + j / 2);
    12.1 --- a/tests/ucxtest.c	Fri Apr 12 21:48:12 2024 +0200
    12.2 +++ b/tests/ucxtest.c	Thu May 23 15:05:24 2024 +0200
    12.3 @@ -36,6 +36,7 @@
    12.4  CxTestSuite *cx_test_suite_string(void);
    12.5  CxTestSuite *cx_test_suite_buffer(void);
    12.6  CxTestSuite *cx_test_suite_printf(void);
    12.7 +CxTestSuite *cx_test_suite_iterator(void);
    12.8  CxTestSuite *cx_test_suite_empty_list(void);
    12.9  CxTestSuite *cx_test_suite_array_list(void);
   12.10  CxTestSuite *cx_test_suite_linked_list(void);
   12.11 @@ -60,6 +61,7 @@
   12.12              cx_test_suite_string(),
   12.13              cx_test_suite_buffer(),
   12.14              cx_test_suite_printf(),
   12.15 +            cx_test_suite_iterator(),
   12.16              cx_test_suite_empty_list(),
   12.17              cx_test_suite_array_list(),
   12.18              cx_test_suite_linked_list(),

mercurial