--- a/ucx/cx/array_list.h Wed Dec 17 18:31:20 2025 +0100 +++ b/ucx/cx/array_list.h Thu Dec 18 17:50:15 2025 +0100 @@ -50,624 +50,581 @@ CX_EXPORT extern const unsigned cx_array_swap_sbo_size; /** - * Declares variables for an array that can be used with the convenience macros. - * - * @par Examples - * @code - * // integer array with at most 255 elements - * CX_ARRAY_DECLARE_SIZED(int, myarray, uint8_t) - * - * // array of MyObject* pointers where size and capacity are stored as unsigned int - * CX_ARRAY_DECLARE_SIZED(MyObject*, objects, unsigned int) - * - * // initializing code - * cx_array_initialize(myarray, 16); // reserve space for 16 - * cx_array_initialize(objects, 100); // reserve space for 100 - * @endcode + * Declares a typed array with size and capacity. * - * @param type the type of the data + * @param type the type of the elements * @param name the name of the array - * @param size_type the type of the size (should be uint8_t, uint16_t, uint32_t, or size_t) + */ +#define CX_ARRAY(type, name) \ + struct { \ + type *data; \ + size_t size; \ + size_t capacity; \ + } name + +/** + * Internal structure for arrays. * - * @see cx_array_initialize() - * @see cx_array_simple_add() - * @see cx_array_simple_copy() - * @see cx_array_simple_add_sorted() - * @see cx_array_simple_insert_sorted() + * A generalization of array structures declared with CX_ARRAY(). */ -#define CX_ARRAY_DECLARE_SIZED(type, name, size_type) \ - type * name; \ - /** Array size. */ size_type name##_size; \ - /** Array capacity. */ size_type name##_capacity +typedef struct cx_array_s { + /** The array data. */ + void *data; + /** The number of elements. */ + size_t size; + /** The maximum number of elements. */ + size_t capacity; +} CxArray; /** - * Declares variables for an array that can be used with the convenience macros. - * - * The size and capacity variables will have type @c size_t. - * Use #CX_ARRAY_DECLARE_SIZED() to specify a different type. + * Initializes an array by allocating memory. * - * @par Examples - * @code - * // int array - * CX_ARRAY_DECLARE(int, myarray) + * Internal function - do not use manually. * - * // initializing code - * cx_array_initialize(myarray, 32); // reserve space for 32 - * @endcode - * - * @param type the type of the data - * @param name the name of the array - * - * @see cx_array_initialize() - * @see cx_array_simple_add() - * @see cx_array_simple_copy() - * @see cx_array_simple_add_sorted() - * @see cx_array_simple_insert_sorted() + * @param allocator the allocator for the array + * @param array a pointer to the array structure + * @param elem_size size of one element + * @param capacity the initial maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -#define CX_ARRAY_DECLARE(type, name) CX_ARRAY_DECLARE_SIZED(type, name, size_t) +cx_attr_nonnull +CX_EXPORT int cx_array_init_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); /** - * Initializes an array with the given capacity. + * Initializes an array by allocating memory. * - * The type of the capacity depends on the type used during declaration. + * The size is set to zero. + * + * @attention If the array was already initialized, this will leak memory. + * Use cx_array_reserve() to change the capacity of an initialized array. * - * @par Examples - * @code - * CX_ARRAY_DECLARE_SIZED(int, arr1, uint8_t) - * CX_ARRAY_DECLARE(int, arr2) // size and capacity are implicitly size_t + * @param allocator (@c CxAllocator*) the allocator for the array + * @param array the name of the array + * @param capacity (@c size_t) the initial maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed + */ +#define cx_array_init_a(allocator, array, capacity) cx_array_init_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) + +/** + * Initializes an array by allocating memory. * - * // initializing code - * cx_array_initialize(arr1, 500); // error: maximum for uint8_t is 255 - * cx_array_initialize(arr2, 500); // OK - * @endcode + * The size is set to zero. * - * - * The memory for the array is allocated with the cxDefaultAllocator. + * @attention If the array was already initialized, this will leak memory. * * @param array the name of the array - * @param capacity the initial capacity - * @see cx_array_initialize_a() - * @see CX_ARRAY_DECLARE_SIZED() - * @see CX_ARRAY_DECLARE() + * @param capacity (@c size_t) the initial maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -#define cx_array_initialize(array, capacity) \ - array##_capacity = capacity; \ - array##_size = 0; \ - array = cxMallocDefault(sizeof(array[0]) * capacity) +#define cx_array_init(array, capacity) cx_array_init_a(cxDefaultAllocator, array, capacity) /** - * Initializes an array with the given capacity using the specified allocator. + * Initializes an array with fixed size memory. + * + * Internal function - do not use manually. * - * @par Example - * @code - * CX_ARRAY_DECLARE(int, myarray) + * @param array a pointer to the array structure + * @param data the fixed size array + * @param capacity the capacity of the fixed size array + * @param size the number of initialized elements in the fixed size array + */ +cx_attr_nonnull +CX_EXPORT void cx_array_init_fixed_(CxArray *array, const void *data, size_t capacity, size_t size); + +/** + * Initializes an array with fixed size memory. * + * This is useful, for example, when you want to work with memory on the stack + * and only want to move to the heap when the stack memory is not enough. * - * const CxAllocator *al = // ... - * cx_array_initialize_a(al, myarray, 128); - * // ... - * cxFree(al, myarray); // remember to free with the same allocator - * @endcode + * With the @p num_initialized argument you can specify how many elements in the + * fixed size array are already correctly initialized, which determines the + * initial size of the array. + * + * The capacity is determined automatically by the compiler. + * + * @attention When you add elements to an array that was initialized with fixed + * size memory, you MUST check the capacity before adding the element and invoke + * cx_array_copy_to_new() when you intend to exceed the capacity. * - * @param allocator (@c CxAllocator*) the allocator - * @param array the name of the array - * @param capacity the initial capacity - * @see cx_array_initialize() - * @see CX_ARRAY_DECLARE_SIZED() - * @see CX_ARRAY_DECLARE() + * @attention When you pass a pointer to an array that does not have a fixed + * size, the behavior is unspecified. + * + * @param array the name of the array to initialize + * @param fixed_size_array (@c void*) the fixed size array + * @param num_initialized (@c size_t) the number of already initialized elements in the fixed size array + * @see cx_array_copy_to_new() */ -#define cx_array_initialize_a(allocator, array, capacity) \ - array##_capacity = capacity; \ - array##_size = 0; \ - array = cxMalloc(allocator, sizeof(array[0]) * capacity) +#define cx_array_init_fixed(array, fixed_size_array, num_initialized) \ + cx_array_init_fixed_((CxArray*)&(array), fixed_size_array, cx_nmemb(fixed_size_array), num_initialized) /** - * Defines a reallocation mechanism for arrays. - * You can create your own, use cx_array_reallocator(), or - * use the #cx_array_default_reallocator. + * Changes the capacity of an array. + * + * Internal function - do not use. + * + * @param allocator the allocator + * @param array a pointer to the array structure + * @param elem_size the size of one element + * @param capacity the new capacity + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -struct cx_array_reallocator_s { - /** - * Reallocates space for the given array. - * - * Implementations are not required to free the original array. - * This allows reallocation of static or stack memory by allocating heap memory - * and copying the array contents; namely when @c stack_ptr in this struct - * is not @c NULL and @p array equals @c stack_ptr. - * - * @param array the array to reallocate - * @param old_capacity the old number of elements - * @param new_capacity the new number of elements - * @param elem_size the size of each element - * @param alloc a reference to this allocator - * @return a pointer to the reallocated memory or @c NULL on failure - */ - void *(*realloc)( void *array, size_t old_capacity, size_t new_capacity, - size_t elem_size, struct cx_array_reallocator_s *alloc); - - /** - * The allocator that shall be used for the reallocations. - */ - const CxAllocator *allocator; - /** - * Optional pointer to stack memory - * if the array is originally located on the stack. - */ - const void *stack_ptr; -}; +cx_attr_nonnull +CX_EXPORT int cx_array_reserve_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); /** - * Typedef for the array reallocator struct. + * Changes the capacity of an array. + * + * If required, the size is reduced to fit into the new capacity. + * + * @param allocator (@c CxAllocator*) the allocator for the array + * @param array the name of the array + * @param capacity (@c size_t) the new maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -typedef struct cx_array_reallocator_s CxArrayReallocator; - -/** - * A default array reallocator that is based on the cxDefaultAllocator. - */ -CX_EXPORT extern CxArrayReallocator *cx_array_default_reallocator; +#define cx_array_reserve_a(allocator, array, capacity) \ + cx_array_reserve_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) /** - * Creates a new array reallocator. - * - * When @p allocator is @c NULL, the cxDefaultAllocator will be used. + * Changes the capacity of an array. * - * When @p stack_ptr is not @c NULL, the reallocator is supposed to be used - * @em only for the specific array initially located at @p stack_ptr. - * When reallocation is needed, the reallocator checks if the array is - * still located at @p stack_ptr and copies the contents to the heap. + * If required, the size is reduced to fit into the new capacity. * - * @note Invoking this function with both arguments being @c NULL will return a - * reallocator that behaves like #cx_array_default_reallocator. + * @param array the name of the array + * @param capacity (@c size_t) the new maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed + */ +#define cx_array_reserve(array, capacity) \ + cx_array_reserve_a(cxDefaultAllocator, array, capacity) + +/** + * Copies the array to a new memory region. * - * @param allocator the allocator this reallocator shall be based on - * @param stack_ptr the address of the array when the array is initially located - * on the stack or shall not reallocate in place - * @return an array reallocator + * Internal function - do not use. + * + * @param allocator the allocator for new new memory + * @param array a pointer to the array structure + * @param elem_size the size of one element + * @param capacity the new capacity + * @retval zero allocation was successful + * @retval non-zero allocation failed */ -CX_EXPORT CxArrayReallocator cx_array_reallocator( - const struct cx_allocator_s *allocator, const void *stack_ptr); +cx_attr_nonnull +CX_EXPORT int cx_array_copy_to_new_(const CxAllocator *allocator, CxArray *array, size_t elem_size, size_t capacity); /** - * Reserves memory for additional elements. - * - * This function checks if the @p capacity of the array is sufficient to hold - * at least @p size plus @p elem_count elements. If not, a reallocation is - * performed with the specified @p reallocator. - * You can create your own reallocator by hand, use #cx_array_default_reallocator, - * or use the convenience function cx_array_reallocator() to create a custom reallocator. + * Copies the array to a new memory region. * - * This function can be useful to replace subsequent calls to cx_array_copy() - * with one single cx_array_reserve() and then - after guaranteeing a - * sufficient capacity - use simple memmove() or memcpy(). + * This is useful when you have initialized the array with a fixed size memory + * using cx_array_init_fixed(), and now you want to increase the capacity. * - * The @p width in bytes refers to the size and capacity. - * Both must have the same width. - * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit - * architecture. If set to zero, the native word width is used. + * @attention When the original memory does not belong to stack memory, and + * you do not have another reference to this memory, it will leak. * - * @note This function will reserve the minimum required capacity to hold - * the additional elements and does not perform an overallocation. + * @param allocator (@c CxAllocator*) the allocator for the new memory + * @param array the name of the array + * @param capacity (@c size_t) the new maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed + * @see cx_array_init_fixed() + */ +#define cx_array_copy_to_new_a(allocator, array, capacity) \ + cx_array_copy_to_new_(allocator, (CxArray*)&(array), sizeof((array).data[0]), capacity) + +/** + * Copies the array to a new memory region. * - * @param array a pointer to the target array - * @param size a pointer to the size of the array - * @param capacity a pointer to the capacity of the array - * @param width the width in bytes for the @p size and @p capacity or zero for default - * @param elem_size the size of one element - * @param elem_count the number of expected additional elements - * @param reallocator the array reallocator to use - * (@c NULL defaults to #cx_array_default_reallocator) - * @retval zero success - * @retval non-zero failure - * @see cx_array_reallocator() + * This is useful when you have initialized the array with a fixed size memory + * using cx_array_init_fixed(), and now you want to increase the capacity. + * + * @attention When the original memory does not belong to stack memory, and + * you do not have another reference to this memory, it will leak. + * + * @param array the name of the array + * @param capacity (@c size_t) the new maximum number of elements + * @retval zero allocation was successful + * @retval non-zero allocation failed + * @see cx_array_init_fixed() */ -cx_attr_nonnull_arg(1, 2, 3) -CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity, - unsigned width, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator); +#define cx_array_copy_to_new(array, capacity) \ + cx_array_copy_to_new_a(cxDefaultAllocator, array, capacity) /** - * Copies elements from one array to another. - * - * The elements are copied to the @p target array at the specified @p index, - * overwriting possible elements. The @p index does not need to be in range of - * the current array @p size. If the new index plus the number of elements added - * extends the array's size, the remaining @p capacity is used. + * Inserts data into an array. * - * If the @p capacity is also insufficient to hold the new data, a reallocation - * attempt is made with the specified @p reallocator. - * You can create your own reallocator by hand, use #cx_array_default_reallocator, - * or use the convenience function cx_array_reallocator() to create a custom reallocator. - * - * The @p width in bytes refers to the size and capacity. - * Both must have the same width. - * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit - * architecture. If set to zero, the native word width is used. - * - * @note When this function does reallocate the array, it may allocate more - * space than required to avoid further allocations in the near future. + * Internal function - do not use. * - * @param target a pointer to the target array - * @param size a pointer to the size of the target array - * @param capacity a pointer to the capacity of the target array - * @param width the width in bytes for the @p size and @p capacity or zero for default - * @param index the index where the copied elements shall be placed - * @param src the source array + * @param allocator the allocator to use for a possible reallocation + * @param array a pointer to the array structure * @param elem_size the size of one element - * @param elem_count the number of elements to copy - * @param reallocator the array reallocator to use - * (@c NULL defaults to #cx_array_default_reallocator) + * @param index the index where to insert the @p other data + * @param other a pointer to an array of data that shall be inserted + * @param n the number of elements that shall be inserted * @retval zero success - * @retval non-zero failure - * @see cx_array_reallocator() - * @see cx_array_reserve() + * @retval non-zero a re-allocation was necessary but failed */ -cx_attr_nonnull_arg(1, 2, 3, 6) -CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width, - size_t index, const void *src, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator); +cx_attr_nonnull_arg(1, 2) +CX_EXPORT int cx_array_insert_(const CxAllocator *allocator, CxArray *array, + size_t elem_size, size_t index, const void *other, size_t n); /** - * Convenience macro that uses cx_array_copy() with a default layout and - * the specified reallocator. + * Appends an element to an array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param index (@c size_t) the index where the copied elements shall be placed - * @param src (@c void*) the source array - * @param count (@c size_t) the number of elements to copy + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the element shall be added + * @param element (@c void*) a pointer to the element that shall be added * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_copy() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_copy_a(reallocator, array, index, src, count) \ - cx_array_copy((void**)&(array), &(array##_size), &(array##_capacity), \ - sizeof(array##_size), index, src, sizeof((array)[0]), count, \ - reallocator) +#define cx_array_add_a(allocator, array, element) \ + cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, element, 1) /** - * Convenience macro that uses cx_array_copy() with a default layout and - * the default reallocator. + * Appends an element to an array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param index (@c size_t) the index where the copied elements shall be placed - * @param src (@c void*) the source array - * @param count (@c size_t) the number of elements to copy + * @param array the name of the array where the element shall be added + * @param element (@c void*) a pointer to the element that shall be added * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_copy_a() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_copy(array, index, src, count) \ - cx_array_simple_copy_a(NULL, array, index, src, count) +#define cx_array_add(array, element) \ + cx_array_add_a(cxDefaultAllocator, array, element) /** - * Convenience macro that uses cx_array_reserve() with a default layout and - * the specified reallocator. + * Inserts an element into an array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param count (@c size_t) the number of expected @em additional elements + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the element shall be inserted + * @param index (@c size_t) the index where to insert the @p element + * @param element (@c void*) a pointer to the element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_reserve() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_reserve_a(reallocator, array, count) \ - cx_array_reserve((void**)&(array), &(array##_size), &(array##_capacity), \ - sizeof(array##_size), sizeof((array)[0]), count, \ - reallocator) +#define cx_array_insert_a(allocator, array, index, element) \ + cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, element, 1) /** - * Convenience macro that uses cx_array_reserve() with a default layout and - * the default reallocator. + * Inserts an element into an array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param count (@c size_t) the number of expected additional elements + * @param array the name of the array where the element shall be inserted + * @param index (@c size_t) the index where to insert the @p element + * @param element (@c void*) a pointer to the element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_reserve_a() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_reserve(array, count) \ - cx_array_simple_reserve_a(NULL, array, count) +#define cx_array_insert(array, index, element) \ + cx_array_insert_a(cxDefaultAllocator, array, index, element) + +/** + * Inserts data into an array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. + * + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param index (@c size_t) the index where to insert the @p other data + * @param other (@c void*) a pointer to an array of data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted + * @retval zero success + * @retval non-zero a re-allocation was necessary but failed + */ +#define cx_array_insert_array_a(allocator, array, index, other, n) \ + cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), index, other, n) /** - * Adds an element to an array with the possibility of allocating more space. - * - * The element @p elem is added to the end of the @p target array which contains - * @p size elements, already. The @p capacity must point to a variable denoting - * the current maximum number of elements the array can hold. + * Inserts data into an array. * - * If the capacity is insufficient to hold the new element, an attempt to - * increase the @p capacity is made and the new capacity is written back. - * - * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. - * It is important, however, that @p size and @p capacity are pointers to - * variables of the same type. + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. * - * @param target (@c void**) a pointer to the target array - * @param size (@c SIZE_TYPE*) a pointer to the size of the target array - * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array - * @param elem_size (@c size_t) the size of one element - * @param elem (@c void*) a pointer to the element to add - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use + * @param array the name of the array where the elements shall be inserted + * @param index (@c size_t) the index where to insert the @p other data + * @param other (@c void*) a pointer to an array of data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted * @retval zero success - * @retval non-zero failure + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_add(target, size, capacity, elem_size, elem, reallocator) \ - cx_array_copy((void**)(target), size, capacity, sizeof(*(size)), \ - *(size), elem, elem_size, 1, reallocator) +#define cx_array_insert_array(array, index, other, n) \ + cx_array_insert_array_a(cxDefaultAllocator, array, index, other, n) + +/** + * Appends data to an array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. + * + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be added + * @param other (@c void*) a pointer to an array of data that shall be added + * @param n (@c size_t) the number of elements that shall be added + * @retval zero success + * @retval non-zero a re-allocation was necessary but failed + */ +#define cx_array_add_array_a(allocator, array, other, n) \ + cx_array_insert_(allocator, (CxArray*)&(array), sizeof((array).data[0]), (array).size, other, n) /** - * Convenience macro that uses cx_array_add() with a default layout and - * the specified reallocator. + * Appends data to an array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) + * @param array the name of the array where the elements shall be added + * @param other (@c void*) a pointer to an array of data that shall be added + * @param n (@c size_t) the number of elements that shall be added * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_add_a(reallocator, array, elem) \ - cx_array_simple_copy_a(reallocator, array, array##_size, &(elem), 1) +#define cx_array_add_array(array, other, n) \ + cx_array_add_array_a(cxDefaultAllocator, array, other, n) /** - * Convenience macro that uses cx_array_add() with a default layout and - * the default reallocator. + * Inserts sorted data into a sorted array. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_a() - */ -#define cx_array_simple_add(array, elem) \ - cx_array_simple_add_a(cx_array_default_reallocator, array, elem) - -/** - * Inserts a sorted array into another sorted array. - * - * If either the target or the source array is not already sorted with respect - * to the specified @p cmp_func, the behavior is undefined. + * Internal function - do not use. * - * If the capacity is insufficient to hold the new data, a reallocation - * attempt is made. - * You can create your own reallocator by hand, use #cx_array_default_reallocator, - * or use the convenience function cx_array_reallocator() to create a custom reallocator. - * - * @param target a pointer to the target array - * @param size a pointer to the size of the target array - * @param capacity a pointer to the capacity of the target array - * @param cmp_func the compare function for the elements - * @param src the source array + * @param allocator the allocator to use for a possible reallocation + * @param array a pointer to the array structure * @param elem_size the size of one element - * @param elem_count the number of elements to insert - * @param reallocator the array reallocator to use - * (@c NULL defaults to #cx_array_default_reallocator) + * @param cmp_func + * @param sorted_data a pointer to an array of data that shall be inserted + * @param n the number of elements that shall be inserted + * @param allow_duplicates @c false if duplicates shall be skipped during insertion * @retval zero success - * @retval non-zero failure + * @retval non-zero a re-allocation was necessary but failed */ -cx_attr_nonnull_arg(1, 2, 3, 5) -CX_EXPORT int cx_array_insert_sorted(void **target, size_t *size, size_t *capacity, - cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator); +cx_attr_nonnull +CX_EXPORT int cx_array_insert_sorted_(const CxAllocator *allocator, CxArray *array, + size_t elem_size, cx_compare_func cmp_func, const void *sorted_data, size_t n, + bool allow_duplicates); /** * Inserts an element into a sorted array. * - * If the target array is not already sorted with respect - * to the specified @p cmp_func, the behavior is undefined. + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * If the capacity is not enough to hold the new data, a reallocation - * attempt is made. - * - * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. - * It is important, however, that @p size and @p capacity are pointers to - * variables of the same type. + * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined. * - * @param target (@c void**) a pointer to the target array - * @param size (@c SIZE_TYPE*) a pointer to the size of the target array - * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array - * @param elem_size (@c size_t) the size of one element - * @param elem (@c void*) a pointer to the element to add - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param element (@c void*) a pointer to element that shall be inserted * @retval zero success - * @retval non-zero failure + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_add_sorted(target, size, capacity, elem_size, elem, cmp_func, reallocator) \ - cx_array_insert_sorted((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator) +#define cx_array_insert_sorted_a(allocator, array, cmp_func, element) \ + cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), cmp_func, element, 1, true) /** - * Convenience macro for cx_array_add_sorted() with a default - * layout and the specified reallocator. + * Inserts an element into a sorted array. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined. + * + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param element (@c void*) a pointer to element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_sorted() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_add_sorted_a(reallocator, array, elem, cmp_func) \ - cx_array_add_sorted(&array, &(array##_size), &(array##_capacity), \ - sizeof((array)[0]), &(elem), cmp_func, reallocator) +#define cx_array_insert_sorted(array, cmp_func, element) \ + cx_array_insert_sorted_a(cxDefaultAllocator, array, cmp_func, element) + +/** + * Inserts sorted data into a sorted array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. + * + * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined. + * + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted + * @retval zero success + * @retval non-zero a re-allocation was necessary but failed + */ +#define cx_array_insert_sorted_array_a(allocator, array, cmp_func, sorted_data, n) \ + cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), cmp_func, sorted_data, n, true) /** - * Convenience macro for cx_array_add_sorted() with a default - * layout and the default reallocator. + * Inserts sorted data into a sorted array. + * + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined. + * + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_sorted_a() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_add_sorted(array, elem, cmp_func) \ - cx_array_simple_add_sorted_a(NULL, array, elem, cmp_func) +#define cx_array_insert_sorted_array(array, cmp_func, sorted_data, n) \ + cx_array_insert_sorted_array_a(cxDefaultAllocator, array, cmp_func, sorted_data, n) /** - * Convenience macro for cx_array_insert_sorted() with a default - * layout and the specified reallocator. + * Inserts an element into a sorted array if it is not already contained. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. + * + * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param src (@c void*) pointer to the source array - * @param n (@c size_t) number of elements in the source array - * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param element (@c void*) a pointer to element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_insert_sorted() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_insert_sorted_a(reallocator, array, src, n, cmp_func) \ - cx_array_insert_sorted((void**)(&array), &(array##_size), &(array##_capacity), \ - cmp_func, src, sizeof((array)[0]), n, reallocator) +#define cx_array_insert_unique_a(allocator, array, cmp_func, element) \ + cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), cmp_func, element, 1, false) /** - * Convenience macro for cx_array_insert_sorted() with a default - * layout and the default reallocator. + * Inserts an element into a sorted array if it is not already contained. + * + * When the capacity is not enough to hold the new element, a re-allocation is attempted. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param src (@c void*) pointer to the source array - * @param n (@c size_t) number of elements in the source array - * @param cmp_func (@c cx_cmp_func) the compare function for the elements + * @attention if the array is not sorted according to the specified @p cmp_func, the behavior is undefined. + * + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param element (@c void*) a pointer to element that shall be inserted * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_insert_sorted_a() + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_simple_insert_sorted(array, src, n, cmp_func) \ - cx_array_simple_insert_sorted_a(NULL, array, src, n, cmp_func) - +#define cx_array_insert_unique(array, cmp_func, element) \ + cx_array_insert_unique_a(cxDefaultAllocator, array, cmp_func, element) /** - * Inserts a sorted array into another sorted array, avoiding duplicates. - * - * If either the target or the source array is not already sorted with respect - * to the specified @p cmp_func, the behavior is undefined. + * Inserts sorted data into a sorted array, skipping duplicates. * - * If the capacity is insufficient to hold the new data, a reallocation - * attempt is made. - * You can create your own reallocator by hand, use #cx_array_default_reallocator, - * or use the convenience function cx_array_reallocator() to create a custom reallocator. + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. + * + * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined. * - * @param target a pointer to the target array - * @param size a pointer to the size of the target array - * @param capacity a pointer to the capacity of the target array - * @param cmp_func the compare function for the elements - * @param src the source array - * @param elem_size the size of one element - * @param elem_count the number of elements to insert - * @param reallocator the array reallocator to use - * (@c NULL defaults to #cx_array_default_reallocator) + * @param allocator (@c CxAllocator*) the allocator to use for a possible reallocation + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted * @retval zero success - * @retval non-zero failure + * @retval non-zero a re-allocation was necessary but failed */ -cx_attr_nonnull_arg(1, 2, 3, 5) -CX_EXPORT int cx_array_insert_unique(void **target, size_t *size, size_t *capacity, - cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count, - CxArrayReallocator *reallocator); +#define cx_array_insert_unique_array_a(allocator, array, cmp_func, sorted_data, n) \ + cx_array_insert_sorted_(allocator, (CxArray*)&(array), sizeof((array).data[0]), cmp_func, sorted_data, n, false) /** - * Inserts an element into a sorted array if it does not exist. - * - * If the target array is not already sorted with respect - * to the specified @p cmp_func, the behavior is undefined. + * Inserts sorted data into a sorted array, skipping duplicates. * - * If the capacity is insufficient to hold the new data, a reallocation - * attempt is made. + * When the capacity is not enough to hold the new elements, a re-allocation is attempted. * - * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. - * It is important, however, that @p size and @p capacity are pointers to - * variables of the same type. + * @attention if either array is not sorted according to the specified @p cmp_func, the behavior is undefined. * - * @param target (@c void**) a pointer to the target array - * @param size (@c SIZE_TYPE*) a pointer to the size of the target array - * @param capacity (@c SIZE_TYPE*) a pointer to the capacity of the target array - * @param elem_size (@c size_t) the size of one element - * @param elem (@c void*) a pointer to the element to add - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @retval zero success (also when the element was already present) - * @retval non-zero failure + * @param array the name of the array where the elements shall be inserted + * @param cmp_func (@c cx_compare_func) the compare function that establishes the order + * @param sorted_data (@c void*) a pointer to an array of sorted data that shall be inserted + * @param n (@c size_t) the number of elements that shall be inserted + * @retval zero success + * @retval non-zero a re-allocation was necessary but failed */ -#define cx_array_add_unique(target, size, capacity, elem_size, elem, cmp_func, reallocator) \ - cx_array_insert_unique((void**)(target), size, capacity, cmp_func, elem, elem_size, 1, reallocator) +#define cx_array_insert_unique_array(array, cmp_func, sorted_data, n) \ + cx_array_insert_unique_array_a(cxDefaultAllocator, array, cmp_func, sorted_data, n) + +/** + * Creates an iterator over the elements of an array. + * + * Internal function - do not use. + * + * @param array a pointer to the array structure + * @param elem_size the size of one element + * @return an iterator over the elements + */ +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT CxIterator cx_array_iterator_(CxArray *array, size_t elem_size); /** - * Convenience macro for cx_array_add_unique() with a default - * layout and the specified reallocator. + * Creates an iterator over the elements of an array. + * + * The iterator will yield pointers to the elements. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_unique() + * @param array the name of the array + * @return an iterator over the elements + * @see cx_array_iterator_ptr() */ -#define cx_array_simple_add_unique_a(reallocator, array, elem, cmp_func) \ - cx_array_add_unique(&array, &(array##_size), &(array##_capacity), \ - sizeof((array)[0]), &(elem), cmp_func, reallocator) +#define cx_array_iterator(array) \ + cx_array_iterator_((CxArray*)&(array), sizeof((array).data[0])) /** - * Convenience macro for cx_array_add_unique() with a default - * layout and the default reallocator. + * Creates an iterator over the elements of an array containing pointers. + * + * Internal function - do not use. * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param elem the element to add (NOT a pointer, address is automatically taken) - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_add_unique_a() + * @param array the name of the array + * @return an iterator over the elements */ -#define cx_array_simple_add_unique(array, elem, cmp_func) \ - cx_array_simple_add_unique_a(NULL, array, elem, cmp_func) +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT CxIterator cx_array_iterator_ptr_(CxArray *array); /** - * Convenience macro for cx_array_insert_unique() with a default - * layout and the specified reallocator. + * Creates an iterator over the elements of an array containing pointers. + * + * The iterator will yield the elements themselves, which are supposed to + * be pointers. * - * @param reallocator (@c CxArrayReallocator*) the array reallocator to use - * @param array the name of the array (NOT a pointer or alias to the array) - * @param src (@c void*) pointer to the source array - * @param n (@c size_t) number of elements in the source array - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_insert_unique() + * @param array the name of the array + * @return an iterator over the elements + * @see cx_array_iterator() */ -#define cx_array_simple_insert_unique_a(reallocator, array, src, n, cmp_func) \ - cx_array_insert_unique((void**)(&array), &(array##_size), &(array##_capacity), \ - cmp_func, src, sizeof((array)[0]), n, reallocator) +#define cx_array_iterator_ptr(array) \ + cx_array_iterator_ptr_((CxArray*)&(array)) /** - * Convenience macro for cx_array_insert_unique() with a default - * layout and the default reallocator. + * Deallocates an array. + * + * Internal function - do not use. + * + * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array + * @param array a pointer to the array structure + */ +cx_attr_nonnull +CX_EXPORT void cx_array_free_(const CxAllocator *allocator, CxArray *array); + +/** + * Deallocates an array. + * + * The structure is reset to zero and can be re-initialized with + * cx_array_inita(). * - * @param array the name of the array (NOT a pointer or alias to the array) - * @param src (@c void*) pointer to the source array - * @param n (@c size_t) number of elements in the source array - * @param cmp_func (@c cx_cmp_func) the compare function for the elements - * @retval zero success - * @retval non-zero failure - * @see CX_ARRAY_DECLARE() - * @see cx_array_simple_insert_unique_a() + * @param array the name of the array */ -#define cx_array_simple_insert_unique(array, src, n, cmp_func) \ - cx_array_simple_insert_unique_a(NULL, array, src, n, cmp_func) +#define cx_array_free(array) cx_array_free_(cxDefaultAllocator, (CxArray*)&(array)) + +/** + * Deallocates an array. + * + * The structure is reset to zero and can be re-initialized with + * cx_array_init_a(). + * + * @param allocator (@c CxAllocator*) the allocator which was used to allocate the array + * @param array the name of the array + */ +#define cx_array_free_a(allocator, array) cx_array_free_(allocator, (CxArray*)&(array)) + /** * Searches the largest lower bound in a sorted array. @@ -750,6 +707,91 @@ CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size, size_t elem_size, const void *elem, cx_compare_func cmp_func); + +/** + * Searches the largest lower bound in a sorted array. + * + * In other words, this function returns the index of the largest element + * in @p arr that is less or equal to @p elem with respect to @p cmp_func. + * When no such element exists, @p size is returned. + * + * When such an element exists more than once, the largest index of all those + * elements is returned. + * + * If @p elem is contained in the array, this is identical to + * #cx_array_binary_search(). + * + * If the array is not sorted with respect to the @p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @param context the context for the compare function + * @return the index of the largest lower bound, or @p size + * @see cx_array_binary_search_sup() + * @see cx_array_binary_search() + */ +cx_attr_nonnull +CX_EXPORT size_t cx_array_binary_search_inf_c(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context); + +/** + * Searches an item in a sorted array. + * + * When such an element exists more than once, the largest index of all those + * elements is returned. + * + * If the array is not sorted with respect to the @p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @param context the context for the compare function + * @return the index of the element in the array, or @p size if the element + * cannot be found + * @see cx_array_binary_search_inf() + * @see cx_array_binary_search_sup() + */ +cx_attr_nonnull +CX_EXPORT size_t cx_array_binary_search_c(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context); + +/** + * Searches the smallest upper bound in a sorted array. + * + * In other words, this function returns the index of the smallest element + * in @p arr that is greater or equal to @p elem with respect to @p cmp_func. + * When no such element exists, @p size is returned. + * + * When such an element exists more than once, the smallest index of all those + * elements is returned. + * + * If @p elem is contained in the array, this is identical to + * #cx_array_binary_search(). + * + * If the array is not sorted with respect to the @p cmp_func, the behavior + * is undefined. + * + * @param arr the array to search + * @param size the size of the array + * @param elem_size the size of one element + * @param elem the element to find + * @param cmp_func the compare function + * @param context the context for the compare function + * @return the index of the smallest upper bound, or @p size + * @see cx_array_binary_search_inf() + * @see cx_array_binary_search() + */ +cx_attr_nonnull +CX_EXPORT size_t cx_array_binary_search_sup_c(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func2 cmp_func, void *context); + /** * Swaps two array elements. * @@ -766,13 +808,10 @@ * * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of * copies of the added elements, and the compare function will be automatically set - * to cx_cmp_ptr(), if none is given. + * to cx_cmp_ptr(). * * @param allocator the allocator for allocating the list memory * (if @c NULL, the cxDefaultAllocator will be used) - * @param comparator the comparator for the elements - * (if @c NULL, and the list is not storing pointers, sort and find - * functions will not work) * @param elem_size the size of each element in bytes * @param initial_capacity the initial number of elements the array can store * @return the created list @@ -781,25 +820,7 @@ cx_attr_malloc cx_attr_dealloc(cxListFree, 1) CX_EXPORT CxList *cxArrayListCreate(const CxAllocator *allocator, - cx_compare_func comparator, size_t elem_size, size_t initial_capacity); - -/** - * Allocates an array list for storing elements with @p elem_size bytes each. - * - * The list will use the cxDefaultAllocator and @em NO compare function. - * If you want to call functions that need a compare function, you have to - * set it immediately after creation or use cxArrayListCreate(). - * - * If @p elem_size is #CX_STORE_POINTERS, the created list stores pointers instead of - * copies of the added elements and the compare function will be automatically set - * to cx_cmp_ptr(). - * - * @param elem_size (@c size_t) the size of each element in bytes - * @param initial_capacity (@c size_t) the initial number of elements the array can store - * @return the created list - */ -#define cxArrayListCreateSimple(elem_size, initial_capacity) \ - cxArrayListCreate(NULL, NULL, elem_size, initial_capacity) + size_t elem_size, size_t initial_capacity); #ifdef __cplusplus } // extern "C"