Mon, 20 Oct 2025 20:10:36 +0200
add cxListEmplaceArray() and cxListEmplaceArrayAt() as preparation for the clone implementation
CHANGELOG | file | annotate | diff | comparison | revisions | |
docs/Writerside/topics/about.md | file | annotate | diff | comparison | revisions | |
docs/Writerside/topics/list.h.md | file | annotate | diff | comparison | revisions | |
src/cx/list.h | file | annotate | diff | comparison | revisions | |
src/linked_list.c | file | annotate | diff | comparison | revisions | |
src/list.c | file | annotate | diff | comparison | revisions | |
tests/test_list.c | file | annotate | diff | comparison | revisions |
--- a/CHANGELOG Mon Oct 20 19:44:18 2025 +0200 +++ b/CHANGELOG Mon Oct 20 20:10:36 2025 +0200 @@ -11,7 +11,7 @@ * adds cxListFirst() and cxListLast() * adds cxListRemoveAndGetFirst() and cxListRemoveAndGetLast(), and corresponding macro aliases cxListPopFront() and cxListPop() - * adds cxListEmplace(), cxListEmplaceAt(), and cxMapEmplace() + * adds cxListEmplace(), cxListEmplaceAt(), cxListEmplaceArray(), cxListEmplaceArrayAt(), and cxMapEmplace() * adds cxListInsertUnique() and cxListInsertUniqueArray() * adds cx_array_insert_unique() and various convenience macros * adds cx_linked_list_insert_unique() and cx_linked_list_insert_unique_chain()
--- a/docs/Writerside/topics/about.md Mon Oct 20 19:44:18 2025 +0200 +++ b/docs/Writerside/topics/about.md Mon Oct 20 20:10:36 2025 +0200 @@ -38,7 +38,7 @@ * adds cxListFirst() and cxListLast() * adds cxListRemoveAndGetFirst() and cxListRemoveAndGetLast(), and corresponding macro aliases cxListPopFront() and cxListPop() -* adds cxListEmplace(), cxListEmplaceAt(), and cxMapEmplace() +* adds cxListEmplace(), cxListEmplaceAt(), cxListEmplaceArray(), cxListEmplaceArrayAt(), and cxMapEmplace() * adds cxListInsertUnique() and cxListInsertUniqueArray() * adds cx_array_insert_unique() and various convenience macros * adds cx_linked_list_insert_unique() and cx_linked_list_insert_unique_chain()
--- a/docs/Writerside/topics/list.h.md Mon Oct 20 19:44:18 2025 +0200 +++ b/docs/Writerside/topics/list.h.md Mon Oct 20 20:10:36 2025 +0200 @@ -120,6 +120,10 @@ void *cxListEmplaceAt(CxList *list, size_t index); +CxIterator cxListEmplaceArray(CxList *list, size_t n); + +CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n); + int cxListInsertSorted(CxList *list, const void *elem); int cxListInsertUnique(CxList *list, const void *elem); @@ -145,8 +149,15 @@ The functions `cxListEmplace()` and `cxListEmplaceAt()` behave like `cxListAdd()` and `cxListInsert()`, except that they only allocate the memory and return a pointer to it, leaving it to the callee to copy the element data into it. +The same is true for `cxListEmplaceArray()` and `cxListEmplaceArrayAt()`, which allocate memory for `n` elements and return an iterator to the first element. Be aware that when the list is storing pointers, the allocated memory will be for the pointer, not the actual element's data. +> If `cxListEmplaceArray()` or `cxListEmplaceArrayAt()` is unable to allocate memory for `n` elements, +> the iterator will iterate only over the elements that have been successfully allocated. +> The `elem_count` attribute of the iterator will be set to the number of successfully allocated elements. +> And the `index` attribute will count from zero to `elem_count - 1`. +> {style="note"} + The function `cxListInsertSorted()` inserts the element at the correct position so that the list remains sorted according to the list's compare function. It is important that the list is already sorted before calling this function. On the other hand, `cxListInsertUnique()` inserts the element only if it is not already in the list. @@ -418,8 +429,8 @@ |------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `clear` | Invoke destructor functions on all elements and remove them from the list. | | `deallocate` | Invoke destructor functions on all elements and deallocate the entire list memory. | -| `insert_element` | Insert a single element at the specified index. Return a pointer to the allocated element or `NULL` on failure. | -| `insert_array` | Insert an array of elements starting at the specified index. Return the number of elements inserted. | +| `insert_element` | Insert or allocate a single element at the specified index. Return a pointer to the allocated element or `NULL` on failure. | +| `insert_array` | Insert or allocate an array of elements starting at the specified index. Return the number of successfully inserted or allocated elements. | | `insert_sorted` | Insert an array of sorted elements into a sorted list. Return the number of elements processed (equals the number of elements inserted in this case). | | `insert_unique` | Insert an array of sorted unique elements into a sorted list. Return the number of elements processed (not the number of elements inserted, which might be lower). | | `insert_iter` | Insert a single element depending on the iterator position. The third argument to this function is zero when the element shall be inserted after the iterator position and non-zero if it shall be inserted before the iterator position. The implementation is also responsible for adjusting the iterator, respectively. |
--- a/src/cx/list.h Mon Oct 20 19:44:18 2025 +0200 +++ b/src/cx/list.h Mon Oct 20 20:10:36 2025 +0200 @@ -88,12 +88,16 @@ /** * Member function for inserting multiple elements. * + * The data pointer may be @c NULL, in which case the function shall only allocate memory. + * Returns the number of successfully inserted or allocated elements. + * * @see cx_list_default_insert_array() */ size_t (*insert_array)(struct cx_list_s *list, size_t index, const void *data, size_t n); /** * Member function for inserting sorted elements into a sorted list. + * Returns the number of successfully inserted elements. * * @see cx_list_default_insert_sorted() */ @@ -101,7 +105,8 @@ /** * Member function for inserting multiple elements if they do not exist. - * + * Implementations shall return the number of successfully processed elements + * (including those which were not added because they are already contained). * @see cx_list_default_insert_unique() */ size_t (*insert_unique)(struct cx_list_s *list, const void *sorted_data, size_t n); @@ -359,6 +364,7 @@ * @param array a pointer to the elements to add * @param n the number of elements to add * @return the number of added elements + * @see cxListEmplaceArray() */ cx_attr_nonnull CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n); @@ -389,6 +395,7 @@ * @param index the index where to emplace the element * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds * @see cxListEmplace() + * @see cxListEmplaceArrayAt() * @see cxListInsert() */ cx_attr_nonnull @@ -408,6 +415,45 @@ CX_EXPORT void *cxListEmplace(CxList *list); /** + * Allocates memory for multiple elements and returns an iterator. + * + * The iterator will only iterate over the successfully allocated elements. + * The @c elem_count attribute is set to that number, and the @c index attribute + * will range from zero to @c elem_count minus one. + * + * @remark When the list is storing pointers, the iterator will iterate over + * the @c void** elements. + * + * @param list the list + * @param index the index where to insert the new data + * @param n the number of elements for which to allocate the memory + * @return an iterator, iterating over the new memory + * @see cxListEmplaceAt() + * @see cxListInsertArray() + */ +cx_attr_nonnull +CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n); + +/** + * Allocates memory for multiple elements and returns an iterator. + * + * The iterator will only iterate over the successfully allocated elements. + * The @c elem_count attribute is set to that number, and the @c index attribute + * will range from zero to @c elem_count minus one. + * + * @remark When the list is storing pointers, the iterator will iterate over + * the @c void** elements. + * + * @param list the list + * @param n the number of elements for which to allocate the memory + * @return an iterator, iterating over the new memory + * @see cxListEmplace() + * @see cxListAddArray() + */ +cx_attr_nonnull +CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n); + +/** * Inserts an item into a sorted list. * * If the list is not sorted already, the behavior is undefined. @@ -454,6 +500,7 @@ * @param array a pointer to the elements to add * @param n the number of elements to add * @return the number of added elements + * @see cxListEmplaceArrayAt() */ cx_attr_nonnull CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n);
--- a/src/linked_list.c Mon Oct 20 19:44:18 2025 +0200 +++ b/src/linked_list.c Mon Oct 20 20:10:36 2025 +0200 @@ -752,7 +752,9 @@ // we can add the remaining nodes and immediately advance to the inserted node const char *source = array; for (size_t i = 1; i < n; i++) { - source += list->collection.elem_size; + if (source != NULL) { + source += list->collection.elem_size; + } if (0 != cx_ll_insert_at(list, node, source)) return i; node = CX_LL_PTR(node, ll->loc_next); }
--- a/src/list.c Mon Oct 20 19:44:18 2025 +0200 +++ b/src/list.c Mon Oct 20 20:10:36 2025 +0200 @@ -300,16 +300,17 @@ const void *data, size_t n ) { - size_t elem_size = list->collection.elem_size; const char *src = data; size_t i = 0; for (; i < n; i++) { if (NULL == invoke_list_func( - insert_element, list, index + i, - src + i * elem_size) + insert_element, list, index + i, src) ) { return i; // LCOV_EXCL_LINE } + if (src != NULL) { + src += list->collection.elem_size; + } } return i; } @@ -596,6 +597,32 @@ return list->cl->insert_element(list, list->collection.size, NULL); } +static bool cx_list_emplace_iterator_valid(const void *it) { + const CxIterator *iter = it; + return iter->index < iter->elem_count; +} + +CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n) { + list->collection.sorted = false; + size_t c = list->cl->insert_array(list, index, NULL, n); + CxIterator iter = list->cl->iterator(list, index, false); + // tweak the fields of this iterator + iter.elem_count = c; + iter.index = 0; + // replace the valid function to abort iteration when c is reached + iter.base.valid = cx_list_emplace_iterator_valid; + // if we are storing pointers, we want to return the pure pointers. + // therefore, we must unwrap the "current" method + if (list->collection.store_pointer) { + iter.base.current = iter.base.current_impl; + } + return iter; +} + +CxIterator cxListEmplaceArray(CxList *list, size_t n) { + return cxListEmplaceArrayAt(list, list->collection.size, n); +} + int cxListInsertSorted(CxList *list, const void *elem) { assert(cxCollectionSorted(list)); list->collection.sorted = true;
--- a/tests/test_list.c Mon Oct 20 19:44:18 2025 +0200 +++ b/tests/test_list.c Mon Oct 20 20:10:36 2025 +0200 @@ -1686,6 +1686,55 @@ CX_TEST_ASSERT(*(int *) cxListAt(list, 9) == 42); }) +roll_out_test_combos_with_defaulted_funcs(emplace_array, { + int a[5] = array_init(5, 47, 11, 13, 42); + int b[5] = array_init(9, 18, 72, 50, 7); + + CxIterator iter; + + iter = cxListEmplaceArray(list, 5); + CX_TEST_ASSERT(cxListSize(list) == 5); + CX_TEST_ASSERT(iter.elem_count == 5); + CX_TEST_ASSERT(iter.index == 0); + if (isptrlist) { + cx_foreach(int **, elem, iter) { + *elem = a + iter.index; + } + } else { + cx_foreach(int *, elem, iter) { + *elem = a[iter.index]; + } + } + CX_TEST_ASSERT(*(int *) cxListAt(list, 0) == 5); + CX_TEST_ASSERT(*(int *) cxListAt(list, 1) == 47); + CX_TEST_ASSERT(*(int *) cxListAt(list, 2) == 11); + CX_TEST_ASSERT(*(int *) cxListAt(list, 3) == 13); + CX_TEST_ASSERT(*(int *) cxListAt(list, 4) == 42); + iter = cxListEmplaceArrayAt(list, 3, 5); + CX_TEST_ASSERT(cxListSize(list) == 10); + CX_TEST_ASSERT(iter.elem_count == 5); + CX_TEST_ASSERT(iter.index == 0); + if (isptrlist) { + cx_foreach(int **, elem, iter) { + *elem = b + iter.index; + } + } else { + cx_foreach(int *, elem, iter) { + *elem = b[iter.index]; + } + } + CX_TEST_ASSERT(*(int *) cxListAt(list, 0) == 5); + CX_TEST_ASSERT(*(int *) cxListAt(list, 1) == 47); + CX_TEST_ASSERT(*(int *) cxListAt(list, 2) == 11); + CX_TEST_ASSERT(*(int *) cxListAt(list, 3) == 9); + CX_TEST_ASSERT(*(int *) cxListAt(list, 4) == 18); + CX_TEST_ASSERT(*(int *) cxListAt(list, 5) == 72); + CX_TEST_ASSERT(*(int *) cxListAt(list, 6) == 50); + CX_TEST_ASSERT(*(int *) cxListAt(list, 7) == 7); + CX_TEST_ASSERT(*(int *) cxListAt(list, 8) == 13); + CX_TEST_ASSERT(*(int *) cxListAt(list, 9) == 42); +}) + roll_out_test_combos_with_defaulted_funcs(insert_sorted, { int d1 = 50; int d2 = 80; @@ -2599,6 +2648,8 @@ cx_test_register(suite, test_list_parl_emplace); cx_test_register(suite, test_list_arl_insert_array); cx_test_register(suite, test_list_parl_insert_array); + cx_test_register(suite, test_list_arl_emplace_array); + cx_test_register(suite, test_list_parl_emplace_array); cx_test_register(suite, test_list_arl_insert_sorted); cx_test_register(suite, test_list_parl_insert_sorted); cx_test_register(suite, test_list_arl_insert_unique); @@ -2658,6 +2709,8 @@ cx_test_register(suite, test_list_arlm_insert_array); cx_test_register(suite, test_list_parlm_insert_array); + cx_test_register(suite, test_list_arlm_emplace_array); + cx_test_register(suite, test_list_parlm_emplace_array); cx_test_register(suite, test_list_arlm_insert_sorted); cx_test_register(suite, test_list_parlm_insert_sorted); cx_test_register(suite, test_list_arlm_insert_unique); @@ -2715,6 +2768,8 @@ cx_test_register(suite, test_list_pll_emplace); cx_test_register(suite, test_list_ll_insert_array); cx_test_register(suite, test_list_pll_insert_array); + cx_test_register(suite, test_list_ll_emplace_array); + cx_test_register(suite, test_list_pll_emplace_array); cx_test_register(suite, test_list_ll_insert_sorted); cx_test_register(suite, test_list_pll_insert_sorted); cx_test_register(suite, test_list_ll_insert_unique); @@ -2773,6 +2828,8 @@ cx_test_register(suite, test_list_llm_insert_array); cx_test_register(suite, test_list_pllm_insert_array); + cx_test_register(suite, test_list_llm_emplace_array); + cx_test_register(suite, test_list_pllm_emplace_array); cx_test_register(suite, test_list_llm_insert_sorted); cx_test_register(suite, test_list_pllm_insert_sorted); cx_test_register(suite, test_list_llm_insert_unique); @@ -2803,6 +2860,8 @@ cx_test_register(suite, test_list_pkvl_emplace); cx_test_register(suite, test_list_kvl_insert_array); cx_test_register(suite, test_list_pkvl_insert_array); + cx_test_register(suite, test_list_kvl_emplace_array); + cx_test_register(suite, test_list_pkvl_emplace_array); cx_test_register(suite, test_list_kvl_insert_sorted); cx_test_register(suite, test_list_pkvl_insert_sorted); cx_test_register(suite, test_list_kvl_insert_unique);