Thu, 23 May 2024 15:05:24 +0200
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(),