Tue, 25 Feb 2025 21:12:11 +0100
update ucx
--- a/ucx/allocator.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/allocator.c Tue Feb 25 21:12:11 2025 +0100 @@ -30,7 +30,6 @@ #include <errno.h> -__attribute__((__malloc__, __alloc_size__(2))) static void *cx_malloc_stdlib( cx_attr_unused void *d, size_t n @@ -38,7 +37,6 @@ return malloc(n); } -__attribute__((__warn_unused_result__, __alloc_size__(3))) static void *cx_realloc_stdlib( cx_attr_unused void *d, void *mem, @@ -47,16 +45,14 @@ return realloc(mem, n); } -__attribute__((__malloc__, __alloc_size__(2, 3))) static void *cx_calloc_stdlib( cx_attr_unused void *d, - size_t nelem, - size_t n + size_t nmemb, + size_t size ) { - return calloc(nelem, n); + return calloc(nmemb, size); } -__attribute__((__nonnull__)) static void cx_free_stdlib( cx_attr_unused void *d, void *mem @@ -75,10 +71,9 @@ &cx_default_allocator_class, NULL }; -CxAllocator *cxDefaultAllocator = &cx_default_allocator; +const CxAllocator * const cxDefaultAllocator = &cx_default_allocator; -#undef cx_reallocate -int cx_reallocate( +int cx_reallocate_( void **mem, size_t n ) { @@ -91,8 +86,7 @@ } } -#undef cx_reallocatearray -int cx_reallocatearray( +int cx_reallocatearray_( void **mem, size_t nmemb, size_t size @@ -144,8 +138,7 @@ } } -#undef cxReallocate -int cxReallocate( +int cxReallocate_( const CxAllocator *allocator, void **mem, size_t n @@ -159,8 +152,7 @@ } } -#undef cxReallocateArray -int cxReallocateArray( +int cxReallocateArray_( const CxAllocator *allocator, void **mem, size_t nmemb, @@ -177,10 +169,10 @@ void *cxCalloc( const CxAllocator *allocator, - size_t nelem, - size_t n + size_t nmemb, + size_t size ) { - return allocator->cl->calloc(allocator->data, nelem, n); + return allocator->cl->calloc(allocator->data, nmemb, size); } void cxFree(
--- a/ucx/array_list.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/array_list.c Tue Feb 25 21:12:11 2025 +0100 @@ -126,27 +126,31 @@ assert(array != NULL); assert(size != NULL); assert(capacity != NULL); - assert(reallocator != NULL); + + // default reallocator + if (reallocator == NULL) { + reallocator = cx_array_default_reallocator; + } // determine size and capacity size_t oldcap; size_t oldsize; size_t max_size; - if (width == 0 || width == 8*sizeof(size_t)) { + if (width == 0 || width == sizeof(size_t)) { oldcap = *(size_t*) capacity; oldsize = *(size_t*) size; max_size = SIZE_MAX; - } else if (width == 16) { + } else if (width == sizeof(uint16_t)) { oldcap = *(uint16_t*) capacity; oldsize = *(uint16_t*) size; max_size = UINT16_MAX; - } else if (width == 8) { + } else if (width == sizeof(uint8_t)) { oldcap = *(uint8_t*) capacity; oldsize = *(uint8_t*) size; max_size = UINT8_MAX; } #if CX_WORDSIZE == 64 - else if (width == 32) { + else if (width == sizeof(uint32_t)) { oldcap = *(uint32_t*) capacity; oldsize = *(uint32_t*) size; max_size = UINT32_MAX; @@ -186,15 +190,15 @@ *array = newmem; // store new capacity - if (width == 0 || width == 8*sizeof(size_t)) { + if (width == 0 || width == sizeof(size_t)) { *(size_t*) capacity = newcap; - } else if (width == 16) { + } else if (width == sizeof(uint16_t)) { *(uint16_t*) capacity = (uint16_t) newcap; - } else if (width == 8) { + } else if (width == sizeof(uint8_t)) { *(uint8_t*) capacity = (uint8_t) newcap; } #if CX_WORDSIZE == 64 - else if (width == 32) { + else if (width == sizeof(uint32_t)) { *(uint32_t*) capacity = (uint32_t) newcap; } #endif @@ -219,27 +223,31 @@ assert(size != NULL); assert(capacity != NULL); assert(src != NULL); - assert(reallocator != NULL); + + // default reallocator + if (reallocator == NULL) { + reallocator = cx_array_default_reallocator; + } // determine size and capacity size_t oldcap; size_t oldsize; size_t max_size; - if (width == 0 || width == 8*sizeof(size_t)) { + if (width == 0 || width == sizeof(size_t)) { oldcap = *(size_t*) capacity; oldsize = *(size_t*) size; max_size = SIZE_MAX; - } else if (width == 16) { + } else if (width == sizeof(uint16_t)) { oldcap = *(uint16_t*) capacity; oldsize = *(uint16_t*) size; max_size = UINT16_MAX; - } else if (width == 8) { + } else if (width == sizeof(uint8_t)) { oldcap = *(uint8_t*) capacity; oldsize = *(uint8_t*) size; max_size = UINT8_MAX; } #if CX_WORDSIZE == 64 - else if (width == 32) { + else if (width == sizeof(uint32_t)) { oldcap = *(uint32_t*) capacity; oldsize = *(uint32_t*) size; max_size = UINT32_MAX; @@ -303,18 +311,18 @@ // if any of size or capacity changed, store them back if (newsize != oldsize || newcap != oldcap) { - if (width == 0 || width == 8*sizeof(size_t)) { + if (width == 0 || width == sizeof(size_t)) { *(size_t*) capacity = newcap; *(size_t*) size = newsize; - } else if (width == 16) { + } else if (width == sizeof(uint16_t)) { *(uint16_t*) capacity = (uint16_t) newcap; *(uint16_t*) size = (uint16_t) newsize; - } else if (width == 8) { + } else if (width == sizeof(uint8_t)) { *(uint8_t*) capacity = (uint8_t) newcap; *(uint8_t*) size = (uint8_t) newsize; } #if CX_WORDSIZE == 64 - else if (width == 32) { + else if (width == sizeof(uint32_t)) { *(uint32_t*) capacity = (uint32_t) newcap; *(uint32_t*) size = (uint32_t) newsize; } @@ -341,7 +349,11 @@ assert(capacity != NULL); assert(cmp_func != NULL); assert(sorted_data != NULL); - assert(reallocator != NULL); + + // default reallocator + if (reallocator == NULL) { + reallocator = cx_array_default_reallocator; + } // corner case if (elem_count == 0) return 0; @@ -844,32 +856,42 @@ } } -static ssize_t cx_arl_find_remove( +static size_t cx_arl_find_remove( struct cx_list_s *list, const void *elem, bool remove ) { + assert(list != NULL); assert(list->collection.cmpfunc != NULL); - assert(list->collection.size < SIZE_MAX / 2); + if (list->collection.size == 0) return 0; char *cur = ((const cx_array_list *) list)->data; - for (ssize_t i = 0; i < (ssize_t) list->collection.size; i++) { + // optimize with binary search, when sorted + if (list->collection.sorted) { + size_t i = cx_array_binary_search( + cur, + list->collection.size, + list->collection.elem_size, + elem, + list->collection.cmpfunc + ); + if (remove && i < list->collection.size) { + cx_arl_remove(list, i, 1, NULL); + } + return i; + } + + // fallback: linear search + for (size_t i = 0; i < list->collection.size; i++) { if (0 == list->collection.cmpfunc(elem, cur)) { if (remove) { - if (1 == cx_arl_remove(list, i, 1, NULL)) { - return i; - } else { - // should be unreachable - return -1; // LCOV_EXCL_LINE - } - } else { - return i; + cx_arl_remove(list, i, 1, NULL); } + return i; } cur += list->collection.elem_size; } - - return -1; + return list->collection.size; } static void cx_arl_sort(struct cx_list_s *list) { @@ -1001,22 +1023,13 @@ cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list)); if (list == NULL) return NULL; - - list->base.cl = &cx_array_list_class; - list->base.collection.allocator = allocator; + cx_list_init((CxList*)list, &cx_array_list_class, + allocator, comparator, elem_size); list->capacity = initial_capacity; - if (elem_size > 0) { - list->base.collection.elem_size = elem_size; - list->base.collection.cmpfunc = comparator; - } else { - elem_size = sizeof(void *); - list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; - cxListStorePointers((CxList *) list); - } - // allocate the array after the real elem_size is known - list->data = cxCalloc(allocator, initial_capacity, elem_size); + list->data = cxCalloc(allocator, initial_capacity, + list->base.collection.elem_size); if (list->data == NULL) { // LCOV_EXCL_START cxFree(allocator, list); return NULL;
--- a/ucx/buffer.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/buffer.c Tue Feb 25 21:12:11 2025 +0100 @@ -71,12 +71,18 @@ buffer->size = 0; buffer->pos = 0; - buffer->flush_func = NULL; - buffer->flush_target = NULL; - buffer->flush_blkmax = 0; - buffer->flush_blksize = 4096; - buffer->flush_threshold = SIZE_MAX; + buffer->flush = NULL; + + return 0; +} +int cxBufferEnableFlushing( + CxBuffer *buffer, + CxBufferFlushConfig config +) { + buffer->flush = malloc(sizeof(CxBufferFlushConfig)); + if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE + memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig)); return 0; } @@ -84,6 +90,7 @@ if (buffer->flags & CX_BUFFER_FREE_CONTENTS) { cxFree(buffer->allocator, buffer->bytes); } + free(buffer->flush); memset(buffer, 0, sizeof(CxBuffer)); } @@ -196,39 +203,52 @@ } } -/** - * Helps flushing data to the flush target of a buffer. - * - * @param buffer the buffer containing the config - * @param space the data to flush - * @param size the element size - * @param nitems the number of items - * @return the number of items flushed - */ -static size_t cx_buffer_write_flush_helper( - CxBuffer *buffer, - const unsigned char *space, +static size_t cx_buffer_flush_helper( + const CxBuffer *buffer, + const unsigned char *src, size_t size, size_t nitems ) { - size_t pos = 0; - size_t remaining = nitems; - size_t max_items = buffer->flush_blksize / size; - while (remaining > 0) { - size_t items = remaining > max_items ? max_items : remaining; - size_t flushed = buffer->flush_func( - space + pos, - size, items, - buffer->flush_target); + // flush data from an arbitrary source + // does not need to be the buffer's contents + size_t max_items = buffer->flush->blksize / size; + size_t fblocks = 0; + size_t flushed_total = 0; + while (nitems > 0 && fblocks < buffer->flush->blkmax) { + fblocks++; + size_t items = nitems > max_items ? max_items : nitems; + size_t flushed = buffer->flush->wfunc( + src, size, items, buffer->flush->target); if (flushed > 0) { - pos += (flushed * size); - remaining -= flushed; + flushed_total += flushed; + src += flushed * size; + nitems -= flushed; } else { // if no bytes can be flushed out anymore, we give up break; } } - return nitems - remaining; + return flushed_total; +} + +static size_t cx_buffer_flush_impl(CxBuffer *buffer, size_t size) { + // flush the current contents of the buffer + unsigned char *space = buffer->bytes; + size_t remaining = buffer->pos / size; + size_t flushed_total = cx_buffer_flush_helper( + buffer, space, size, remaining); + + // shift the buffer left after flushing + // IMPORTANT: up to this point, copy on write must have been + // performed already, because we can't do error handling here + cxBufferShiftLeft(buffer, flushed_total*size); + + return flushed_total; +} + +size_t cxBufferFlush(CxBuffer *buffer) { + if (buffer_copy_on_write(buffer)) return 0; + return cx_buffer_flush_impl(buffer, 1); } size_t cxBufferWrite( @@ -248,95 +268,98 @@ return nitems; } - size_t len; - size_t nitems_out = nitems; + size_t len, total_flushed = 0; +cx_buffer_write_retry: if (cx_szmul(size, nitems, &len)) { errno = EOVERFLOW; - return 0; + return total_flushed; } - size_t required = buffer->pos + len; - if (buffer->pos > required) { - return 0; + if (buffer->pos > SIZE_MAX - len) { + errno = EOVERFLOW; + return total_flushed; } + size_t required = buffer->pos + len; bool perform_flush = false; if (required > buffer->capacity) { if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { - if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { + if (buffer->flush != NULL && required > buffer->flush->threshold) { perform_flush = true; } else { if (cxBufferMinimumCapacity(buffer, required)) { - return 0; // LCOV_EXCL_LINE + return total_flushed; // LCOV_EXCL_LINE } } } else { - if (buffer->flush_blkmax > 0) { + if (buffer->flush != NULL) { perform_flush = true; } else { - // truncate data to be written, if we can neither extend nor flush + // truncate data, if we can neither extend nor flush len = buffer->capacity - buffer->pos; if (size > 1) { len -= len % size; } - nitems_out = len / size; + nitems = len / size; } } } + // check here and not above because of possible truncation if (len == 0) { - return len; + return total_flushed; } - if (perform_flush) { - size_t flush_max; - if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) { - errno = EOVERFLOW; - return 0; - } - size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL - ? buffer->pos - : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos); - if (flush_pos == buffer->pos) { - // entire buffer has been flushed, we can reset - buffer->size = buffer->pos = 0; - - size_t items_flush; // how many items can also be directly flushed - size_t items_keep; // how many items have to be written to the buffer + // check if we need to copy + if (buffer_copy_on_write(buffer)) return 0; - items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size; - if (items_flush > 0) { - items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size); - // in case we could not flush everything, keep the rest + // perform the operation + if (perform_flush) { + size_t items_flushed; + if (buffer->pos == 0) { + // if we don't have data in the buffer, but are instructed + // to flush, it means that we are supposed to relay the data + items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems); + if (items_flushed == 0) { + // we needed to relay data, but could not flush anything + // i.e. we have to give up to avoid endless trying + return 0; + } + nitems -= items_flushed; + total_flushed += items_flushed; + if (nitems > 0) { + ptr = ((unsigned char*)ptr) + items_flushed * size; + goto cx_buffer_write_retry; } - items_keep = nitems - items_flush; - if (items_keep > 0) { - // try again with the remaining stuff - const unsigned char *new_ptr = ptr; - new_ptr += items_flush * size; - // report the directly flushed items as written plus the remaining stuff - return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer); - } else { - // all items have been flushed - report them as written - return nitems; + return total_flushed; + } else { + items_flushed = cx_buffer_flush_impl(buffer, size); + if (items_flushed == 0) { + // flush target is full, let's try to truncate + size_t remaining_space; + if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { + remaining_space = buffer->flush->threshold > buffer->pos + ? buffer->flush->threshold - buffer->pos + : 0; + } else { + remaining_space = buffer->capacity > buffer->pos + ? buffer->capacity - buffer->pos + : 0; + } + nitems = remaining_space / size; + if (nitems == 0) { + return total_flushed; + } } - } else if (flush_pos == 0) { - // nothing could be flushed at all, we immediately give up without writing any data - return 0; - } else { - // we were partially successful, we shift left and try again - cxBufferShiftLeft(buffer, flush_pos); - return cxBufferWrite(ptr, size, nitems, buffer); + goto cx_buffer_write_retry; } } else { - if (buffer_copy_on_write(buffer)) return 0; memcpy(buffer->bytes + buffer->pos, ptr, len); buffer->pos += len; if (buffer->pos > buffer->size) { buffer->size = buffer->pos; } - return nitems_out; + return total_flushed + nitems; } - } size_t cxBufferAppend( @@ -346,9 +369,19 @@ CxBuffer *buffer ) { size_t pos = buffer->pos; - buffer->pos = buffer->size; + size_t append_pos = buffer->size; + buffer->pos = append_pos; size_t written = cxBufferWrite(ptr, size, nitems, buffer); - buffer->pos = pos; + // the buffer might have been flushed + // we must compute a possible delta for the position + // expected: pos = append_pos + written + // -> if this is not the case, there is a delta + size_t delta = append_pos + written*size - buffer->pos; + if (delta > pos) { + buffer->pos = 0; + } else { + buffer->pos = pos - delta; + } return written; }
--- a/ucx/cx/allocator.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/allocator.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,7 +26,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file allocator.h + * @file allocator.h * Interface for custom allocators. */ @@ -46,8 +46,6 @@ /** * The allocator's malloc() implementation. */ - cx_attr_nonnull - cx_attr_nodiscard cx_attr_allocsize(2) void *(*malloc)( void *data, size_t n @@ -56,8 +54,6 @@ /** * The allocator's realloc() implementation. */ - cx_attr_nodiscard - cx_attr_allocsize(3) void *(*realloc)( void *data, void *mem, @@ -67,18 +63,15 @@ /** * The allocator's calloc() implementation. */ - cx_attr_nodiscard - cx_attr_allocsize(2, 3) void *(*calloc)( void *data, - size_t nelem, - size_t n + size_t nmemb, + size_t size ); /** * The allocator's free() implementation. */ - cx_attr_nonnull_arg(1) void (*free)( void *data, void *mem @@ -107,13 +100,14 @@ /** * A default allocator using standard library malloc() etc. */ -extern CxAllocator *cxDefaultAllocator; +cx_attr_export +extern const CxAllocator * const cxDefaultAllocator; /** * Function pointer type for destructor functions. * * A destructor function deallocates possible contents and MAY free the memory - * pointed to by \p memory. Read the documentation of the respective function + * pointed to by @p memory. Read the documentation of the respective function * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in * that particular implementation. * @@ -125,7 +119,7 @@ * Function pointer type for destructor functions. * * A destructor function deallocates possible contents and MAY free the memory - * pointed to by \p memory. Read the documentation of the respective function + * pointed to by @p memory. Read the documentation of the respective function * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in * that particular implementation. * @@ -138,93 +132,103 @@ ); /** - * Re-allocate a previously allocated block and changes the pointer in-place, + * Reallocate a previously allocated block and changes the pointer in-place, * if necessary. * - * \par Error handling - * \c errno will be set by realloc() on failure. + * @par Error handling + * @c errno will be set by realloc() on failure. * * @param mem pointer to the pointer to allocated block * @param n the new size in bytes - * @return zero on success, non-zero on failure + * @retval zero success + * @retval non-zero failure + * @see cx_reallocatearray() */ cx_attr_nonnull cx_attr_nodiscard -int cx_reallocate( +cx_attr_export +int cx_reallocate_( void **mem, size_t n ); /** - * Re-allocate a previously allocated block and changes the pointer in-place, + * Reallocate a previously allocated block and changes the pointer in-place, * if necessary. * - * The size is calculated by multiplying \p nemb and \p size. + * The size is calculated by multiplying @p nemb and @p size. * - * \par Error handling - * \c errno will be set by realloc() on failure or when the multiplication of - * \p nmemb and \p size overflows. + * @par Error handling + * @c errno will be set by realloc() on failure or when the multiplication of + * @p nmemb and @p size overflows. * * @param mem pointer to the pointer to allocated block * @param nmemb the number of elements * @param size the size of each element - * @return zero on success, non-zero on failure + * @retval zero success + * @retval non-zero failure + * @see cx_reallocate() */ cx_attr_nonnull cx_attr_nodiscard -int cx_reallocatearray( +cx_attr_export +int cx_reallocatearray_( void **mem, size_t nmemb, size_t size ); /** - * Re-allocate a previously allocated block and changes the pointer in-place, + * Reallocate a previously allocated block and changes the pointer in-place, * if necessary. * - * \par Error handling - * \c errno will be set by realloc() on failure. + * @par Error handling + * @c errno will be set by realloc() on failure. * - * @param mem pointer to the pointer to allocated block - * @param n the new size in bytes - * @return zero on success, non-zero on failure + * @param mem (@c void**) pointer to the pointer to allocated block + * @param n (@c size_t) the new size in bytes + * @retval zero success + * @retval non-zero failure + * @see cx_reallocatearray() */ -#define cx_reallocate(mem, n) cx_reallocate((void**)(mem), n) +#define cx_reallocate(mem, n) cx_reallocate_((void**)(mem), n) /** - * Re-allocate a previously allocated block and changes the pointer in-place, + * Reallocate a previously allocated block and changes the pointer in-place, * if necessary. * - * The size is calculated by multiplying \p nemb and \p size. + * The size is calculated by multiplying @p nemb and @p size. + * + * @par Error handling + * @c errno will be set by realloc() on failure or when the multiplication of + * @p nmemb and @p size overflows. * - * \par Error handling - * \c errno will be set by realloc() on failure or when the multiplication of - * \p nmemb and \p size overflows. - * - * @param mem pointer to the pointer to allocated block - * @param nmemb the number of elements - * @param size the size of each element - * @return zero on success, non-zero on failure + * @param mem (@c void**) pointer to the pointer to allocated block + * @param nmemb (@c size_t) the number of elements + * @param size (@c size_t) the size of each element + * @retval zero success + * @retval non-zero failure */ #define cx_reallocatearray(mem, nmemb, size) \ - cx_reallocatearray((void**)(mem), nmemb, size) + cx_reallocatearray_((void**)(mem), nmemb, size) /** * Free a block allocated by this allocator. * - * \note Freeing a block of a different allocator is undefined. + * @note Freeing a block of a different allocator is undefined. * * @param allocator the allocator * @param mem a pointer to the block to free */ cx_attr_nonnull_arg(1) +cx_attr_export void cxFree( const CxAllocator *allocator, void *mem ); /** - * Allocate \p n bytes of memory. + * Allocate @p n bytes of memory. * * @param allocator the allocator * @param n the number of bytes @@ -235,28 +239,30 @@ cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2) +cx_attr_export void *cxMalloc( const CxAllocator *allocator, size_t n ); /** - * Re-allocate the previously allocated block in \p mem, making the new block - * \p n bytes long. + * Reallocate the previously allocated block in @p mem, making the new block + * @p n bytes long. * This function may return the same pointer that was passed to it, if moving * the memory was not necessary. * - * \note Re-allocating a block allocated by a different allocator is undefined. + * @note Re-allocating a block allocated by a different allocator is undefined. * * @param allocator the allocator * @param mem pointer to the previously allocated block * @param n the new size in bytes - * @return a pointer to the re-allocated memory + * @return a pointer to the reallocated memory */ cx_attr_nodiscard cx_attr_nonnull_arg(1) cx_attr_dealloc_ucx cx_attr_allocsize(3) +cx_attr_export void *cxRealloc( const CxAllocator *allocator, void *mem, @@ -264,27 +270,28 @@ ); /** - * Re-allocate the previously allocated block in \p mem, making the new block - * \p n bytes long. + * Reallocate the previously allocated block in @p mem, making the new block + * @p n bytes long. * This function may return the same pointer that was passed to it, if moving * the memory was not necessary. * - * The size is calculated by multiplying \p nemb and \p size. - * If that multiplication overflows, this function returns \c NULL and \c errno + * The size is calculated by multiplying @p nemb and @p size. + * If that multiplication overflows, this function returns @c NULL and @c errno * will be set. * - * \note Re-allocating a block allocated by a different allocator is undefined. + * @note Re-allocating a block allocated by a different allocator is undefined. * * @param allocator the allocator * @param mem pointer to the previously allocated block * @param nmemb the number of elements * @param size the size of each element - * @return a pointer to the re-allocated memory + * @return a pointer to the reallocated memory */ cx_attr_nodiscard cx_attr_nonnull_arg(1) cx_attr_dealloc_ucx cx_attr_allocsize(3, 4) +cx_attr_export void *cxReallocArray( const CxAllocator *allocator, void *mem, @@ -293,67 +300,72 @@ ); /** - * Re-allocate a previously allocated block and changes the pointer in-place, + * Reallocate a previously allocated block and changes the pointer in-place, * if necessary. - * This function acts like cxRealloc() using the pointer pointed to by \p mem. + * This function acts like cxRealloc() using the pointer pointed to by @p mem. * - * \note Re-allocating a block allocated by a different allocator is undefined. + * @note Re-allocating a block allocated by a different allocator is undefined. * - * \par Error handling - * \c errno will be set, if the underlying realloc function does so. + * @par Error handling + * @c errno will be set, if the underlying realloc function does so. * * @param allocator the allocator * @param mem pointer to the pointer to allocated block * @param n the new size in bytes - * @return zero on success, non-zero on failure + * @retval zero success + * @retval non-zero failure */ cx_attr_nodiscard cx_attr_nonnull -int cxReallocate( +cx_attr_export +int cxReallocate_( const CxAllocator *allocator, void **mem, size_t n ); /** - * Re-allocate a previously allocated block and changes the pointer in-place, + * Reallocate a previously allocated block and changes the pointer in-place, * if necessary. - * This function acts like cxRealloc() using the pointer pointed to by \p mem. + * This function acts like cxRealloc() using the pointer pointed to by @p mem. * - * \note Re-allocating a block allocated by a different allocator is undefined. + * @note Re-allocating a block allocated by a different allocator is undefined. * - * \par Error handling - * \c errno will be set, if the underlying realloc function does so. + * @par Error handling + * @c errno will be set, if the underlying realloc function does so. * - * @param allocator the allocator - * @param mem pointer to the pointer to allocated block - * @param n the new size in bytes - * @return zero on success, non-zero on failure + * @param allocator (@c CxAllocator*) the allocator + * @param mem (@c void**) pointer to the pointer to allocated block + * @param n (@c size_t) the new size in bytes + * @retval zero success + * @retval non-zero failure */ #define cxReallocate(allocator, mem, n) \ - cxReallocate(allocator, (void**)(mem), n) + cxReallocate_(allocator, (void**)(mem), n) /** - * Re-allocate a previously allocated block and changes the pointer in-place, + * Reallocate a previously allocated block and changes the pointer in-place, * if necessary. * This function acts like cxReallocArray() using the pointer pointed to - * by \p mem. + * by @p mem. * - * \note Re-allocating a block allocated by a different allocator is undefined. + * @note Re-allocating a block allocated by a different allocator is undefined. * - * \par Error handling - * \c errno will be set, if the underlying realloc function does so or the - * multiplication of \p nmemb and \p size overflows. + * @par Error handling + * @c errno will be set, if the underlying realloc function does so or the + * multiplication of @p nmemb and @p size overflows. * * @param allocator the allocator * @param mem pointer to the pointer to allocated block * @param nmemb the number of elements * @param size the size of each element - * @return zero on success, non-zero on failure + * @retval zero success + * @retval non-zero on failure */ cx_attr_nodiscard cx_attr_nonnull -int cxReallocateArray( +cx_attr_export +int cxReallocateArray_( const CxAllocator *allocator, void **mem, size_t nmemb, @@ -361,32 +373,33 @@ ); /** - * Re-allocate a previously allocated block and changes the pointer in-place, + * Reallocate a previously allocated block and changes the pointer in-place, * if necessary. * This function acts like cxReallocArray() using the pointer pointed to - * by \p mem. + * by @p mem. + * + * @note Re-allocating a block allocated by a different allocator is undefined. * - * \note Re-allocating a block allocated by a different allocator is undefined. + * @par Error handling + * @c errno will be set, if the underlying realloc function does so or the + * multiplication of @p nmemb and @p size overflows. * - * \par Error handling - * \c errno will be set, if the underlying realloc function does so or the - * multiplication of \p nmemb and \p size overflows. + * @param allocator (@c CxAllocator*) the allocator + * @param mem (@c void**) pointer to the pointer to allocated block + * @param nmemb (@c size_t) the number of elements + * @param size (@c size_t) the size of each element + * @retval zero success + * @retval non-zero failure + */ +#define cxReallocateArray(allocator, mem, nmemb, size) \ + cxReallocateArray_(allocator, (void**) (mem), nmemb, size) + +/** + * Allocate @p nmemb elements of @p n bytes each, all initialized to zero. * * @param allocator the allocator - * @param mem pointer to the pointer to allocated block * @param nmemb the number of elements - * @param size the size of each element - * @return zero on success, non-zero on failure - */ -#define cxReallocateArray(allocator, mem, nmemb, size) \ - cxReallocateArray(allocator, (void**) (mem), nmemb, size) - -/** - * Allocate \p nelem elements of \p n bytes each, all initialized to zero. - * - * @param allocator the allocator - * @param nelem the number of elements - * @param n the size of each element in bytes + * @param size the size of each element in bytes * @return a pointer to the allocated memory */ cx_attr_nonnull_arg(1) @@ -394,10 +407,11 @@ cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2, 3) +cx_attr_export void *cxCalloc( const CxAllocator *allocator, - size_t nelem, - size_t n + size_t nmemb, + size_t size ); #ifdef __cplusplus
--- a/ucx/cx/array_list.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/array_list.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file array_list.h - * \brief Array list implementation. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file array_list.h + * @brief Array list implementation. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ @@ -47,49 +47,88 @@ * The maximum item size in an array list that fits into stack buffer * when swapped. */ +cx_attr_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 + * * @param type the type of the data * @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) * + * @see cx_array_initialize() * @see cx_array_simple_add() * @see cx_array_simple_copy() - * @see cx_array_initialize() * @see cx_array_simple_add_sorted() * @see cx_array_simple_insert_sorted() */ #define CX_ARRAY_DECLARE_SIZED(type, name, size_type) \ - type * name; \ - /** Array size. */ size_type name##_size; \ + type * name; \ + /** Array size. */ size_type name##_size; \ /** Array capacity. */ size_type name##_capacity /** * Declares variables for an array that can be used with the convenience macros. * - * The size and capacity variables will have `size_t` type. + * The size and capacity variables will have @c size_t type. * Use #CX_ARRAY_DECLARE_SIZED() to specify a different type. * + * @par Examples + * @code + * // int array + * CX_ARRAY_DECLARE(int, myarray) + * + * // 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_initialize() * @see cx_array_simple_add_sorted() * @see cx_array_simple_insert_sorted() */ #define CX_ARRAY_DECLARE(type, name) CX_ARRAY_DECLARE_SIZED(type, name, size_t) /** - * Initializes an array declared with CX_ARRAY_DECLARE(). + * Initializes an array with the given capacity. + * + * The type of the capacity depends on the type used during declaration. + * + * @par Examples + * @code + * CX_ARRAY_DECLARE_SIZED(int, arr1, uint8_t) + * CX_ARRAY_DECLARE(int, arr2) // size and capacity are implicitly size_t + * + * // initializing code + * cx_array_initialize(arr1, 500); // error: maximum for uint8_t is 255 + * cx_array_initialize(arr2, 500); // OK + * @endcode + * * * The memory for the array is allocated with stdlib malloc(). - * @param array the array + * @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() */ #define cx_array_initialize(array, capacity) \ array##_capacity = capacity; \ @@ -97,12 +136,26 @@ array = malloc(sizeof(array[0]) * capacity) /** - * Initializes an array declared with CX_ARRAY_DECLARE(). + * Initializes an array with the given capacity using the specified allocator. + * + * @par Example + * @code + * CX_ARRAY_DECLARE(int, myarray) + * * - * The memory for the array is allocated with the specified allocator. - * @param allocator the allocator - * @param array the array + * const CxAllocator *al = // ... + * cx_array_initialize_a(al, myarray, 128); + * // ... + * cxFree(al, myarray); // don't forget to free with same allocator + * @endcode + * + * The memory for the array is allocated with stdlib malloc(). + * @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() */ #define cx_array_initialize_a(allocator, array, capacity) \ array##_capacity = capacity; \ @@ -111,6 +164,8 @@ /** * Defines a reallocation mechanism for arrays. + * You can create your own, use cx_array_reallocator(), or + * use the #cx_array_default_reallocator. */ struct cx_array_reallocator_s { /** @@ -126,7 +181,7 @@ * @param capacity the new capacity (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 + * @return a pointer to the reallocated memory or @c NULL on failure */ cx_attr_nodiscard cx_attr_nonnull_arg(4) @@ -164,24 +219,29 @@ /** * A default stdlib-based array reallocator. */ -extern struct cx_array_reallocator_s *cx_array_default_reallocator; +cx_attr_export +extern CxArrayReallocator *cx_array_default_reallocator; /** * Creates a new array reallocator. * - * When \p allocator is \c NULL, the stdlib default allocator will be used. + * When @p allocator is @c NULL, the stdlib default allocator will be used. * - * When \p stackmem is not \c NULL, the reallocator is supposed to be used - * \em only for the specific array that is initially located at \p stackmem. + * When @p stackmem is not @c NULL, the reallocator is supposed to be used + * @em only for the specific array that is initially located at @p stackmem. * When reallocation is needed, the reallocator checks, if the array is - * still located at \p stackmem and copies the contents to the heap. + * still located at @p stackmem and copies the contents to the heap. + * + * @note Invoking this function with both arguments @c NULL will return a + * reallocator that behaves like #cx_array_default_reallocator. * * @param allocator the allocator this reallocator shall be based on * @param stackmem the address of the array when the array is initially located - * on the stack + * on the stack or shall not reallocated in place * @return an array reallocator */ -struct cx_array_reallocator_s cx_array_reallocator( +cx_attr_export +CxArrayReallocator cx_array_reallocator( const struct cx_allocator_s *allocator, const void *stackmem ); @@ -189,28 +249,35 @@ /** * 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. + * 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. * * 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(). * - * The \p width refers to the size and capacity. Both must have the same width. - * Supported are 0, 8, 16, and 32, as well as 64 if running on a 64 bit + * 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. * * @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 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 - * @return zero on success, non-zero on failure + * (@c NULL defaults to #cx_array_default_reallocator) + * @retval zero success + * @retval non-zero failure + * @see cx_array_reallocator() */ -cx_attr_nonnull +cx_attr_nonnull_arg(1, 2, 3) +cx_attr_export int cx_array_reserve( void **array, void *size, @@ -218,36 +285,43 @@ unsigned width, size_t elem_size, size_t elem_count, - struct cx_array_reallocator_s *reallocator + CxArrayReallocator *reallocator ); /** * 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 - * would extend the array's size, the remaining \p capacity is used. + * 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 + * would extend the array's size, the remaining @p capacity is used. * - * If the \p capacity is also insufficient to hold the new data, a reallocation - * attempt is made with the specified \p reallocator. + * 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 refers to the size and capacity. Both must have the same width. - * Supported are 0, 8, 16, and 32, as well as 64 if running on a 64 bit + * 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. * * @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 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 elem_size the size of one element * @param elem_count the number of elements to copy * @param reallocator the array reallocator to use - * @return zero on success, non-zero on failure + * (@c NULL defaults to #cx_array_default_reallocator) + * @retval zero success + * @retval non-zero failure + * @see cx_array_reallocator() */ -cx_attr_nonnull +cx_attr_nonnull_arg(1, 2, 3, 6) +cx_attr_export int cx_array_copy( void **target, void *size, @@ -257,103 +331,111 @@ const void *src, size_t elem_size, size_t elem_count, - struct cx_array_reallocator_s *reallocator + CxArrayReallocator *reallocator ); /** * Convenience macro that uses cx_array_copy() with a default layout and * the specified reallocator. * - * @param reallocator the array reallocator to use - * @param array the name of the array (NOT a pointer to the array) - * @param index the index where the copied elements shall be placed - * @param src the source array - * @param count the number of elements to copy - * @return zero on success, non-zero on failure + * @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 + * @retval zero success + * @retval non-zero failure * @see CX_ARRAY_DECLARE() * @see cx_array_simple_copy() */ #define cx_array_simple_copy_a(reallocator, array, index, src, count) \ cx_array_copy((void**)&(array), &(array##_size), &(array##_capacity), \ - 8*sizeof(array##_size), index, src, sizeof((array)[0]), count, \ + sizeof(array##_size), index, src, sizeof((array)[0]), count, \ reallocator) /** * Convenience macro that uses cx_array_copy() with a default layout and * the default reallocator. * - * @param array the name of the array (NOT a pointer to the array) - * @param index the index where the copied elements shall be placed - * @param src the source array - * @param count the number of elements to copy - * @return zero on success, non-zero on failure + * @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 + * @retval zero success + * @retval non-zero failure * @see CX_ARRAY_DECLARE() * @see cx_array_simple_copy_a() */ #define cx_array_simple_copy(array, index, src, count) \ - cx_array_simple_copy_a(cx_array_default_reallocator, \ - array, index, src, count) + cx_array_simple_copy_a(NULL, array, index, src, count) /** * Convenience macro that uses cx_array_reserve() with a default layout and * the specified reallocator. * - * @param reallocator the array reallocator to use - * @param array the name of the array (NOT a pointer to the array) - * @param count the number of expected additional elements - * @return zero on success, non-zero on failure + * @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 + * @retval zero success + * @retval non-zero failure * @see CX_ARRAY_DECLARE() * @see cx_array_simple_reserve() */ #define cx_array_simple_reserve_a(reallocator, array, count) \ cx_array_reserve((void**)&(array), &(array##_size), &(array##_capacity), \ - 8*sizeof(array##_size), sizeof((array)[0]), count, \ + sizeof(array##_size), sizeof((array)[0]), count, \ reallocator) /** * Convenience macro that uses cx_array_reserve() with a default layout and * the default reallocator. * - * @param array the name of the array (NOT a pointer to the array) - * @param count the number of expected additional elements - * @return zero on success, non-zero on failure + * @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 + * @retval zero success + * @retval non-zero failure * @see CX_ARRAY_DECLARE() * @see cx_array_simple_reserve_a() */ #define cx_array_simple_reserve(array, count) \ - cx_array_simple_reserve_a(cx_array_default_reallocator, \ - array, count) + cx_array_simple_reserve_a(NULL, array, count) /** * 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 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. * * 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. + * 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. * - * @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 elem_size the size of one element - * @param elem a pointer to the element to add - * @param reallocator the array reallocator to use - * @return zero on success, non-zero on failure + * @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 + * @retval zero success + * @retval non-zero failure */ #define cx_array_add(target, size, capacity, elem_size, elem, reallocator) \ - cx_array_copy((void**)(target), size, capacity, 8*sizeof(*(size)), \ + cx_array_copy((void**)(target), size, capacity, sizeof(*(size)), \ *(size), elem, elem_size, 1, reallocator) /** * Convenience macro that uses cx_array_add() with a default layout and * the specified reallocator. * - * @param reallocator the array reallocator to use - * @param array the name of the array (NOT a pointer to the array) + * @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) - * @return zero on success, non-zero on failure + * @retval zero success + * @retval non-zero failure * @see CX_ARRAY_DECLARE() * @see cx_array_simple_add() */ @@ -364,9 +446,10 @@ * Convenience macro that uses cx_array_add() with a default layout and * the default reallocator. * - * @param array the name of the array (NOT a pointer to the 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) - * @return zero on success, non-zero on failure + * @retval zero success + * @retval non-zero failure * @see CX_ARRAY_DECLARE() * @see cx_array_simple_add_a() */ @@ -377,10 +460,12 @@ * 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. + * to the specified @p cmp_func, the behavior is undefined. * * 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 @@ -390,9 +475,12 @@ * @param elem_size the size of one element * @param elem_count the number of elements to insert * @param reallocator the array reallocator to use - * @return zero on success, non-zero on failure + * (@c NULL defaults to #cx_array_default_reallocator) + * @retval zero success + * @retval non-zero failure */ -cx_attr_nonnull +cx_attr_nonnull_arg(1, 2, 3, 5) +cx_attr_export int cx_array_insert_sorted( void **target, size_t *size, @@ -401,25 +489,31 @@ const void *src, size_t elem_size, size_t elem_count, - struct cx_array_reallocator_s *reallocator + CxArrayReallocator *reallocator ); /** * 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. + * to the specified @p cmp_func, the behavior is undefined. * * If the capacity is insufficient to hold the new data, a reallocation * attempt is made. * - * @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 elem_size the size of one element - * @param elem a pointer to the element to add - * @param reallocator the array reallocator to use - * @return zero on success, non-zero on failure + * 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. + * + * @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 + * @retval non-zero failure */ #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) @@ -428,11 +522,12 @@ * Convenience macro for cx_array_add_sorted() with a default * layout and the specified reallocator. * - * @param reallocator the array reallocator to use - * @param array the name of the array (NOT a pointer to the array) + * @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 the compare function for the elements - * @return zero on success, non-zero on failure + * @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_sorted() */ @@ -444,26 +539,28 @@ * Convenience macro for cx_array_add_sorted() with a default * layout and the default reallocator. * - * @param array the name of the array (NOT a pointer to the 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) - * @param cmp_func the compare function for the elements - * @return zero on success, non-zero on failure + * @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_sorted_a() */ #define cx_array_simple_add_sorted(array, elem, cmp_func) \ - cx_array_simple_add_sorted_a(cx_array_default_reallocator, array, elem, cmp_func) + cx_array_simple_add_sorted_a(NULL, array, elem, cmp_func) /** * Convenience macro for cx_array_insert_sorted() with a default * layout and the specified reallocator. * - * @param reallocator the array reallocator to use - * @param array the name of the array (NOT a pointer to the array) - * @param src pointer to the source array - * @param n number of elements in the source array - * @param cmp_func the compare function for the elements - * @return zero on success, non-zero on failure + * @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_sorted() */ @@ -475,28 +572,29 @@ * Convenience macro for cx_array_insert_sorted() with a default * layout and the default reallocator. * - * @param array the name of the array (NOT a pointer to the array) - * @param src pointer to the source array - * @param n number of elements in the source array - * @param cmp_func the compare function for the elements - * @return zero on success, non-zero on failure + * @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_sorted_a() */ #define cx_array_simple_insert_sorted(array, src, n, cmp_func) \ - cx_array_simple_insert_sorted_a(cx_array_default_reallocator, array, src, n, cmp_func) + cx_array_simple_insert_sorted_a(NULL, array, src, n, 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. + * 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. * - * If \p elem is contained in the array, this is identical to + * 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 + * If the array is not sorted with respect to the @p cmp_func, the behavior * is undefined. * * @param arr the array to search @@ -504,9 +602,12 @@ * @param elem_size the size of one element * @param elem the element to find * @param cmp_func the compare function - * @return the index of the largest lower bound, or \p size + * @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_attr_export size_t cx_array_binary_search_inf( const void *arr, size_t size, @@ -518,7 +619,7 @@ /** * Searches an item in a sorted array. * - * If the array is not sorted with respect to the \p cmp_func, the behavior + * If the array is not sorted with respect to the @p cmp_func, the behavior * is undefined. * * @param arr the array to search @@ -526,10 +627,13 @@ * @param elem_size the size of one element * @param elem the element to find * @param cmp_func the compare function - * @return the index of the element in the array, or \p size if the element + * @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_attr_export size_t cx_array_binary_search( const void *arr, size_t size, @@ -542,13 +646,13 @@ * 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. + * 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. * - * If \p elem is contained in the array, this is identical to + * 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 + * If the array is not sorted with respect to the @p cmp_func, the behavior * is undefined. * * @param arr the array to search @@ -556,9 +660,12 @@ * @param elem_size the size of one element * @param elem the element to find * @param cmp_func the compare function - * @return the index of the smallest upper bound, or \p size + * @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_attr_export size_t cx_array_binary_search_sup( const void *arr, size_t size, @@ -576,6 +683,7 @@ * @param idx2 index of second element */ cx_attr_nonnull +cx_attr_export void cx_array_swap( void *arr, size_t elem_size, @@ -584,16 +692,16 @@ ); /** - * Allocates an array list for storing elements with \p elem_size bytes each. + * Allocates an array list for storing elements with @p elem_size bytes each. * - * If \p elem_size is CX_STORE_POINTERS, the created list will be created as if - * cxListStorePointers() was called immediately after creation and the compare - * function will be automatically set to cx_cmp_ptr(), if none is given. + * 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. * * @param allocator the allocator for allocating the list memory - * (if \c NULL, a default stdlib allocator will be used) + * (if @c NULL, a default stdlib allocator will be used) * @param comparator the comparator for the elements - * (if \c NULL, and the list is not storing pointers, sort and find + * (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 @@ -602,6 +710,7 @@ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) +cx_attr_export CxList *cxArrayListCreate( const CxAllocator *allocator, cx_compare_func comparator, @@ -610,18 +719,18 @@ ); /** - * Allocates an array list for storing elements with \p elem_size bytes each. + * Allocates an array list for storing elements with @p elem_size bytes each. * - * The list will use the cxDefaultAllocator and \em NO compare function. + * 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 will be created as if - * cxListStorePointers() was called immediately after creation and the compare - * function will be automatically set to cx_cmp_ptr(). + * 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. * - * @param elem_size the size of each element in bytes - * @param initial_capacity the initial number of elements the array can store + * @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) \
--- a/ucx/cx/buffer.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/buffer.h Tue Feb 25 21:12:11 2025 +0100 @@ -27,9 +27,9 @@ */ /** - * \file buffer.h + * @file buffer.h * - * \brief Advanced buffer implementation. + * @brief Advanced buffer implementation. * * Instances of CxBuffer can be used to read from or to write to like one * would do with a stream. @@ -38,9 +38,9 @@ * can be enabled. See the documentation of the macro constants for more * information. * - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_BUFFER_H @@ -88,8 +88,73 @@ */ #define CX_BUFFER_COPY_ON_EXTEND 0x08 +/** + * Function pointer for cxBufferWrite that is compatible with cx_write_func. + * @see cx_write_func + */ +#define cxBufferWriteFunc ((cx_write_func) cxBufferWrite) +/** + * Function pointer for cxBufferRead that is compatible with cx_read_func. + * @see cx_read_func + */ +#define cxBufferReadFunc ((cx_read_func) cxBufferRead) + +/** + * Configuration for automatic flushing. + */ +struct cx_buffer_flush_config_s { + /** + * The buffer may not extend beyond this threshold before starting to flush. + * + * Only used when the buffer uses #CX_BUFFER_AUTO_EXTEND. + * The threshold will be the maximum capacity the buffer is extended to + * before flushing. + */ + size_t threshold; + /** + * The block size for the elements to flush. + */ + size_t blksize; + /** + * The maximum number of blocks to flush in one cycle. + * + * @attention while it is guaranteed that cxBufferFlush() will not flush + * more blocks, this is not necessarily the case for cxBufferWrite(). + * After performing a flush cycle, cxBufferWrite() will retry the write + * operation and potentially trigger another flush cycle, until the + * flush target accepts no more data. + */ + size_t blkmax; + + /** + * The target for write function. + */ + void *target; + + /** + * The write-function used for flushing. + * If NULL, the flushed content gets discarded. + */ + cx_write_func wfunc; +}; + +/** + * Type alias for the flush configuration struct. + * + * @code + * struct cx_buffer_flush_config_s { + * size_t threshold; + * size_t blksize; + * size_t blkmax; + * void *target; + * cx_write_func wfunc; + * }; + * @endcode + */ +typedef struct cx_buffer_flush_config_s CxBufferFlushConfig; + /** Structure for the UCX buffer data. */ -typedef struct { +struct cx_buffer_s { /** A pointer to the buffer contents. */ union { /** @@ -103,6 +168,12 @@ }; /** The allocator to use for automatic memory management. */ const CxAllocator *allocator; + /** + * Optional flush configuration + * + * @see cxBufferEnableFlushing() + */ + CxBufferFlushConfig *flush; /** Current position of the buffer. */ size_t pos; /** Current capacity (i.e. maximum size) of the buffer. */ @@ -110,40 +181,6 @@ /** Current size of the buffer content. */ size_t size; /** - * The buffer may not extend beyond this threshold before starting to flush. - * Default is \c SIZE_MAX (flushing disabled when auto extension is enabled). - */ - size_t flush_threshold; - /** - * The block size for the elements to flush. - * Default is 4096 bytes. - */ - size_t flush_blksize; - /** - * The maximum number of blocks to flush in one cycle. - * Zero disables flushing entirely (this is the default). - * Set this to \c SIZE_MAX to flush the entire buffer. - * - * @attention if the maximum number of blocks multiplied with the block size - * is smaller than the expected contents written to this buffer within one write - * operation, multiple flush cycles are performed after that write. - * That means the total number of blocks flushed after one write to this buffer may - * be larger than \c flush_blkmax. - */ - size_t flush_blkmax; - - /** - * The write function used for flushing. - * If NULL, the flushed content gets discarded. - */ - cx_write_func flush_func; - - /** - * The target for \c flush_func. - */ - void *flush_target; - - /** * Flag register for buffer features. * @see #CX_BUFFER_DEFAULT * @see #CX_BUFFER_FREE_CONTENTS @@ -151,45 +188,46 @@ * @see #CX_BUFFER_COPY_ON_WRITE */ int flags; -} cx_buffer_s; +}; /** * UCX buffer. */ -typedef cx_buffer_s CxBuffer; +typedef struct cx_buffer_s CxBuffer; /** * Initializes a fresh buffer. * - * You may also provide a read-only \p space, in which case + * You may also provide a read-only @p space, in which case * you will need to cast the pointer, and you should set the * #CX_BUFFER_COPY_ON_WRITE flag. * * You need to set the size manually after initialization, if - * you provide \p space which already contains data. + * you provide @p space which already contains data. * - * When you specify stack memory as \p space and decide to use - * the auto-extension feature, you \em must use the + * When you specify stack memory as @p space and decide to use + * the auto-extension feature, you @em must use the * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the * #CX_BUFFER_AUTO_EXTEND flag. * - * \note You may provide \c NULL as argument for \p space. + * @note You may provide @c NULL as argument for @p space. * Then this function will allocate the space and enforce * the #CX_BUFFER_FREE_CONTENTS flag. In that case, specifying * copy-on-write should be avoided, because the allocated * space will be leaking after the copy-on-write operation. * * @param buffer the buffer to initialize - * @param space pointer to the memory area, or \c NULL to allocate + * @param space pointer to the memory area, or @c NULL to allocate * new memory * @param capacity the capacity of the buffer * @param allocator the allocator this buffer shall use for automatic * memory management - * (if \c NULL, a default stdlib allocator will be used) + * (if @c NULL, a default stdlib allocator will be used) * @param flags buffer features (see cx_buffer_s.flags) * @return zero on success, non-zero if a required allocation failed */ cx_attr_nonnull_arg(1) +cx_attr_export int cxBufferInit( CxBuffer *buffer, void *space, @@ -199,6 +237,27 @@ ); /** + * Configures the buffer for flushing. + * + * Flushing can happen automatically when data is written + * to the buffer (see cxBufferWrite()) or manually when + * cxBufferFlush() is called. + * + * @param buffer the buffer + * @param config the flush configuration + * @retval zero success + * @retval non-zero failure + * @see cxBufferFlush() + * @see cxBufferWrite() + */ +cx_attr_nonnull +cx_attr_export +int cxBufferEnableFlushing( + CxBuffer *buffer, + CxBufferFlushConfig config +); + +/** * Destroys the buffer contents. * * Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled. @@ -208,49 +267,52 @@ * @see cxBufferInit() */ cx_attr_nonnull +cx_attr_export void cxBufferDestroy(CxBuffer *buffer); /** * Deallocates the buffer. * * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys - * the contents. If you \em only want to destroy the contents, use cxBufferDestroy(). + * the contents. If you @em only want to destroy the contents, use cxBufferDestroy(). * - * \remark As with all free() functions, this accepts \c NULL arguments in which + * @remark As with all free() functions, this accepts @c NULL arguments in which * case it does nothing. * * @param buffer the buffer to deallocate * @see cxBufferCreate() */ +cx_attr_export void cxBufferFree(CxBuffer *buffer); /** * Allocates and initializes a fresh buffer. * - * You may also provide a read-only \p space, in which case + * You may also provide a read-only @p space, in which case * you will need to cast the pointer, and you should set the * #CX_BUFFER_COPY_ON_WRITE flag. - * When you specify stack memory as \p space and decide to use - * the auto-extension feature, you \em must use the + * When you specify stack memory as @p space and decide to use + * the auto-extension feature, you @em must use the * #CX_BUFFER_COPY_ON_EXTEND flag, instead of the * #CX_BUFFER_AUTO_EXTEND flag. * - * \note You may provide \c NULL as argument for \p space. + * @note You may provide @c NULL as argument for @p space. * Then this function will allocate the space and enforce * the #CX_BUFFER_FREE_CONTENTS flag. * - * @param space pointer to the memory area, or \c NULL to allocate + * @param space pointer to the memory area, or @c NULL to allocate * new memory * @param capacity the capacity of the buffer * @param allocator the allocator to use for allocating the structure and the automatic * memory management within the buffer - * (if \c NULL, a default stdlib allocator will be used) + * (if @c NULL, a default stdlib allocator will be used) * @param flags buffer features (see cx_buffer_s.flags) - * @return a pointer to the buffer on success, \c NULL if a required allocation failed + * @return a pointer to the buffer on success, @c NULL if a required allocation failed */ cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard +cx_attr_export CxBuffer *cxBufferCreate( void *space, size_t capacity, @@ -269,7 +331,7 @@ * are discarded. * * If the offset is negative, the contents are shifted to the left where the - * first \p shift bytes are discarded. + * first @p shift bytes are discarded. * The new size of the buffer is the old size minus the absolute shift value. * If this value is larger than the buffer size, the buffer is emptied (but * not cleared, see the security note below). @@ -277,11 +339,11 @@ * The buffer position gets shifted alongside with the content but is kept * within the boundaries of the buffer. * - * \note For situations where \c off_t is not large enough, there are specialized cxBufferShiftLeft() and - * cxBufferShiftRight() functions using a \c size_t as parameter type. + * @note For situations where @c off_t is not large enough, there are specialized cxBufferShiftLeft() and + * cxBufferShiftRight() functions using a @c size_t as parameter type. * - * \attention - * Security Note: The shifting operation does \em not erase the previously occupied memory cells. + * @attention + * Security Note: The shifting operation does @em not erase the previously occupied memory cells. * But you can easily do that manually, e.g. by calling * <code>memset(buffer->bytes, 0, shift)</code> for a right shift or * <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code> @@ -289,9 +351,13 @@ * * @param buffer the buffer * @param shift the shift offset (negative means left shift) - * @return 0 on success, non-zero if a required auto-extension or copy-on-write fails + * @retval zero success + * @retval non-zero if a required auto-extension or copy-on-write fails + * @see cxBufferShiftLeft() + * @see cxBufferShiftRight() */ cx_attr_nonnull +cx_attr_export int cxBufferShift( CxBuffer *buffer, off_t shift @@ -303,10 +369,12 @@ * * @param buffer the buffer * @param shift the shift offset - * @return 0 on success, non-zero if a required auto-extension or copy-on-write fails + * @retval zero success + * @retval non-zero if a required auto-extension or copy-on-write fails * @see cxBufferShift() */ cx_attr_nonnull +cx_attr_export int cxBufferShiftRight( CxBuffer *buffer, size_t shift @@ -318,10 +386,12 @@ * * @param buffer the buffer * @param shift the positive shift offset - * @return usually zero, except the buffer uses copy-on-write and the allocation fails + * @retval zero success + * @retval non-zero if the buffer uses copy-on-write and the allocation fails * @see cxBufferShift() */ cx_attr_nonnull +cx_attr_export int cxBufferShiftLeft( CxBuffer *buffer, size_t shift @@ -331,23 +401,25 @@ /** * Moves the position of the buffer. * - * The new position is relative to the \p whence argument. + * The new position is relative to the @p whence argument. * - * \li \c SEEK_SET marks the start of the buffer. - * \li \c SEEK_CUR marks the current position. - * \li \c SEEK_END marks the end of the buffer. + * @li @c SEEK_SET marks the start of the buffer. + * @li @c SEEK_CUR marks the current position. + * @li @c SEEK_END marks the end of the buffer. * * With an offset of zero, this function sets the buffer position to zero - * (\c SEEK_SET), the buffer size (\c SEEK_END) or leaves the buffer position - * unchanged (\c SEEK_CUR). + * (@c SEEK_SET), the buffer size (@c SEEK_END) or leaves the buffer position + * unchanged (@c SEEK_CUR). * * @param buffer the buffer - * @param offset position offset relative to \p whence - * @param whence one of \c SEEK_SET, \c SEEK_CUR or \c SEEK_END - * @return 0 on success, non-zero if the position is invalid + * @param offset position offset relative to @p whence + * @param whence one of @c SEEK_SET, @c SEEK_CUR or @c SEEK_END + * @retval zero success + * @retval non-zero if the position is invalid * */ cx_attr_nonnull +cx_attr_export int cxBufferSeek( CxBuffer *buffer, off_t offset, @@ -360,13 +432,14 @@ * The data is deleted by zeroing it with a call to memset(). * If you do not need that, you can use the faster cxBufferReset(). * - * \note If the #CX_BUFFER_COPY_ON_WRITE flag is set, this function + * @note If the #CX_BUFFER_COPY_ON_WRITE flag is set, this function * will not erase the data and behave exactly as cxBufferReset(). * * @param buffer the buffer to be cleared * @see cxBufferReset() */ cx_attr_nonnull +cx_attr_export void cxBufferClear(CxBuffer *buffer); /** @@ -379,17 +452,20 @@ * @see cxBufferClear() */ cx_attr_nonnull +cx_attr_export void cxBufferReset(CxBuffer *buffer); /** * Tests, if the buffer position has exceeded the buffer size. * * @param buffer the buffer to test - * @return true, if the current buffer position has exceeded the last - * byte of the buffer's contents. + * @retval true if the current buffer position has exceeded the last + * byte of the buffer's contents + * @retval false otherwise */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export bool cxBufferEof(const CxBuffer *buffer); @@ -400,9 +476,11 @@ * * @param buffer the buffer * @param capacity the minimum required capacity for this buffer - * @return 0 on success or a non-zero value on failure + * @retval zero the capacity was already sufficient or successfully increased + * @retval non-zero on allocation failure */ cx_attr_nonnull +cx_attr_export int cxBufferMinimumCapacity( CxBuffer *buffer, size_t capacity @@ -411,30 +489,46 @@ /** * Writes data to a CxBuffer. * + * If automatic flushing is not enabled, the data is simply written into the + * buffer at the current position and the position of the buffer is increased + * by the number of bytes written. + * * If flushing is enabled and the buffer needs to flush, the data is flushed to * the target until the target signals that it cannot take more data by * returning zero via the respective write function. In that case, the remaining * data in this buffer is shifted to the beginning of this buffer so that the - * newly available space can be used to append as much data as possible. This - * function only stops writing more elements, when the flush target and this + * newly available space can be used to append as much data as possible. + * + * This function only stops writing more elements, when the flush target and this * buffer are both incapable of taking more data or all data has been written. - * The number returned by this function is the total number of elements that - * could be written during the process. It does not necessarily mean that those - * elements are still in this buffer, because some of them could have also be - * flushed already. + * + * If, after flushing, the number of items that shall be written still exceeds + * the capacity or flush threshold, this function tries to write all items directly + * to the flush target, if possible. * - * If automatic flushing is not enabled, the position of the buffer is increased - * by the number of bytes written. + * The number returned by this function is the number of elements from + * @c ptr that could be written to either the flush target or the buffer + * (so it does not include the number of items that had been already in the buffer + * in were flushed during the process). * - * \note The signature is compatible with the fwrite() family of functions. + * @attention + * When @p size is larger than one and the contents of the buffer are not aligned + * with @p size, flushing stops after all complete items have been flushed, leaving + * the mis-aligned part in the buffer. + * Afterward, this function only writes as many items as possible to the buffer. + * + * @note The signature is compatible with the fwrite() family of functions. * * @param ptr a pointer to the memory area containing the bytes to be written * @param size the length of one element * @param nitems the element count * @param buffer the CxBuffer to write to * @return the total count of elements written + * @see cxBufferAppend() + * @see cxBufferRead() */ cx_attr_nonnull +cx_attr_export size_t cxBufferWrite( const void *ptr, size_t size, @@ -451,7 +545,7 @@ * while additional data is added to the buffer occasionally. * Consequently, the position of the buffer is unchanged after this operation. * - * \note The signature is compatible with the fwrite() family of functions. + * @note The signature is compatible with the fwrite() family of functions. * * @param ptr a pointer to the memory area containing the bytes to be written * @param size the length of one element @@ -459,8 +553,10 @@ * @param buffer the CxBuffer to write to * @return the total count of elements written * @see cxBufferWrite() + * @see cxBufferRead() */ cx_attr_nonnull +cx_attr_export size_t cxBufferAppend( const void *ptr, size_t size, @@ -469,19 +565,79 @@ ); /** + * Performs a single flush-run on the specified buffer. + * + * Does nothing when the position in the buffer is zero. + * Otherwise, the data until the current position minus + * one is considered for flushing. + * Note carefully that flushing will never exceed the + * current @em position, even when the size of the + * buffer is larger than the current position. + * + * One flush run will try to flush @c blkmax many + * blocks of size @c blksize until either the @p buffer + * has no more data to flush or the write function + * used for flushing returns zero. + * + * The buffer is shifted left for that many bytes + * the flush operation has successfully flushed. + * + * @par Example 1 + * Assume you have a buffer with size 340 and you are + * at position 200. The flush configuration is + * @c blkmax=4 and @c blksize=64 . + * Assume that the entire flush operation is successful. + * All 200 bytes on the left hand-side from the current + * position are written. + * That means, the size of the buffer is now 140 and the + * position is zero. + * + * @par Example 2 + * Same as Example 1, but now the @c blkmax is 1. + * The size of the buffer is now 276 and the position is 136. + * + * @par Example 3 + * Same as Example 1, but now assume the flush target + * only accepts 100 bytes before returning zero. + * That means, the flush operations manages to flush + * one complete block and one partial block, ending + * up with a buffer with size 240 and position 100. + * + * @remark Just returns zero when flushing was not enabled with + * cxBufferEnableFlushing(). + * + * @remark When the buffer uses copy-on-write, the memory + * is copied first, before attempting any flush. + * This is, however, considered an erroneous use of the + * buffer, because it does not make much sense to put + * readonly data into an UCX buffer for flushing, instead + * of writing it directly to the target. + * + * @param buffer the buffer + * @return the number of successfully flushed bytes + * @see cxBufferEnableFlushing() + */ +cx_attr_nonnull +cx_attr_export +size_t cxBufferFlush(CxBuffer *buffer); + +/** * Reads data from a CxBuffer. * * The position of the buffer is increased by the number of bytes read. * - * \note The signature is compatible with the fread() family of functions. + * @note The signature is compatible with the fread() family of functions. * * @param ptr a pointer to the memory area where to store the read data * @param size the length of one element * @param nitems the element count * @param buffer the CxBuffer to read from * @return the total number of elements read + * @see cxBufferWrite() + * @see cxBufferAppend() */ cx_attr_nonnull +cx_attr_export size_t cxBufferRead( void *ptr, size_t size, @@ -495,25 +651,30 @@ * The least significant byte of the argument is written to the buffer. If the * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled, * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature - * is disabled or buffer extension fails, \c EOF is returned. + * is disabled or buffer extension fails, @c EOF is returned. * * On successful write, the position of the buffer is increased. * + * If you just want to write a null-terminator at the current position, you + * should use cxBufferTerminate() instead. + * * @param buffer the buffer to write to * @param c the character to write - * @return the byte that has been written or \c EOF when the end of the stream is + * @return the byte that has been written or @c EOF when the end of the stream is * reached and automatic extension is not enabled or not possible + * @see cxBufferTerminate() */ cx_attr_nonnull +cx_attr_export int cxBufferPut( CxBuffer *buffer, int c ); /** - * Writes a terminating zero to a buffer. + * Writes a terminating zero to a buffer at the current position. * - * On successful write, \em neither the position \em nor the size of the buffer is + * On successful write, @em neither the position @em nor the size of the buffer is * increased. * * The purpose of this function is to have the written data ready to be used as @@ -523,17 +684,21 @@ * @return zero, if the terminator could be written, non-zero otherwise */ cx_attr_nonnull +cx_attr_export int cxBufferTerminate(CxBuffer *buffer); /** * Writes a string to a buffer. * + * This is a convenience function for <code>cxBufferWrite(str, 1, strlen(str), buffer)</code>. + * * @param buffer the buffer * @param str the zero-terminated string * @return the number of bytes written */ cx_attr_nonnull cx_attr_cstr_arg(2) +cx_attr_export size_t cxBufferPutString( CxBuffer *buffer, const char *str @@ -545,9 +710,10 @@ * The current position of the buffer is increased after a successful read. * * @param buffer the buffer to read from - * @return the character or \c EOF, if the end of the buffer is reached + * @return the character or @c EOF, if the end of the buffer is reached */ cx_attr_nonnull +cx_attr_export int cxBufferGet(CxBuffer *buffer); #ifdef __cplusplus
--- a/ucx/cx/collection.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/collection.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file collection.h - * \brief Common definitions for various collection implementations. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file collection.h + * @brief Common definitions for various collection implementations. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_COLLECTION_H @@ -92,17 +92,69 @@ * instead of copies of the actual objects. */ bool store_pointer; + /** + * Indicates if this collection is guaranteed to be sorted. + * Note that the elements can still be sorted, even when the collection is not aware of that. + */ + bool sorted; }; /** * Use this macro to declare common members for a collection structure. + * + * @par Example Use + * @code + * struct MyCustomSet { + * CX_COLLECTION_BASE; + * MySetElements *data; + * } + * @endcode */ #define CX_COLLECTION_BASE struct cx_collection_s collection /** + * Returns the number of elements currently stored. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @return (@c size_t) the number of currently stored elements + */ +#define cxCollectionSize(c) ((c)->collection.size) + +/** + * Returns the size of one element. + * + * If #cxCollectionStoresPointers() returns true, this is the size of a pointer. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @return (@c size_t) the size of one element in bytes + */ +#define cxCollectionElementSize(c) ((c)->collection.elem_size) + +/** + * Indicates whether this collection only stores pointers instead of the actual data. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @retval true if this collection stores only pointers to data + * @retval false if this collection stores the actual element's data + */ +#define cxCollectionStoresPointers(c) ((c)->collection.store_pointer) + +/** + * Indicates whether the collection can guarantee that the stored elements are currently sorted. + * + * This may return false even when the elements are sorted. + * It is totally up to the implementation of the collection whether it keeps track of the order of its elements. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @retval true if the elements are currently sorted wrt. the collection's compare function + * @retval false if the order of elements is unknown + */ +#define cxCollectionSorted(c) ((c)->collection.sorted) + +/** * Sets a simple destructor function for this collection. * - * @param c the collection + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE * @param destr the destructor function */ #define cxDefineDestructor(c, destr) \ @@ -111,7 +163,7 @@ /** * Sets a simple destructor function for this collection. * - * @param c the collection + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE * @param destr the destructor function */ #define cxDefineAdvancedDestructor(c, destr, data) \ @@ -124,8 +176,11 @@ * Usually only used by collection implementations. There should be no need * to invoke this macro manually. * - * @param c the collection - * @param e the element + * When the collection stores pointers, those pointers are directly passed + * to the destructor. Otherwise, a pointer to the element is passed. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param e the element (the type is @c void* or @c void** depending on context) */ #define cx_invoke_simple_destructor(c, e) \ (c)->collection.simple_destructor((c)->collection.store_pointer ? (*((void **) (e))) : (e)) @@ -136,8 +191,11 @@ * Usually only used by collection implementations. There should be no need * to invoke this macro manually. * - * @param c the collection - * @param e the element + * When the collection stores pointers, those pointers are directly passed + * to the destructor. Otherwise, a pointer to the element is passed. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param e the element (the type is @c void* or @c void** depending on context) */ #define cx_invoke_advanced_destructor(c, e) \ (c)->collection.advanced_destructor((c)->collection.destructor_data, \ @@ -150,8 +208,11 @@ * Usually only used by collection implementations. There should be no need * to invoke this macro manually. * - * @param c the collection - * @param e the element + * When the collection stores pointers, those pointers are directly passed + * to the destructor. Otherwise, a pointer to the element is passed. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param e the element (the type is @c void* or @c void** depending on context) */ #define cx_invoke_destructor(c, e) \ if ((c)->collection.simple_destructor) cx_invoke_simple_destructor(c,e); \
--- a/ucx/cx/common.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/common.h Tue Feb 25 21:12:11 2025 +0100 @@ -27,15 +27,15 @@ */ /** - * \file common.h + * @file common.h * - * \brief Common definitions and feature checks. + * @brief Common definitions and feature checks. * - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License * - * \mainpage UAP Common Extensions + * @mainpage UAP Common Extensions * Library with common and useful functions, macros and data structures. * <p> * Latest available source:<br> @@ -102,6 +102,9 @@ // Architecture Detection // --------------------------------------------------------------------------- +#ifndef INTPTR_MAX +#error Missing INTPTR_MAX definition +#endif #if INTPTR_MAX == INT64_MAX /** * The address width in bits on this platform. @@ -117,19 +120,6 @@ #endif // --------------------------------------------------------------------------- -// Missing Defines -// --------------------------------------------------------------------------- - -#ifndef SSIZE_MAX // not defined in glibc since C23 and MSVC -#if CX_WORDSIZE == 64 -#define SSIZE_MAX 0x7fffffffffffffffll -#else -#define SSIZE_MAX 0x7fffffffl -#endif -#endif - - -// --------------------------------------------------------------------------- // Attribute definitions // --------------------------------------------------------------------------- @@ -163,10 +153,10 @@ #ifndef __clang__ /** * The pointer returned by the attributed function is supposed to be freed - * by \p freefunc. + * by @p freefunc. * * @param freefunc the function that shall be used to free the memory - * @param freefunc_arg the index of the pointer argument in \p freefunc + * @param freefunc_arg the index of the pointer argument in @p freefunc */ #define cx_attr_dealloc(freefunc, freefunc_arg) \ __attribute__((__malloc__(freefunc, freefunc_arg))) @@ -190,7 +180,7 @@ #ifdef __clang__ /** - * No support for \c null_terminated_string_arg in clang or GCC below 14. + * No support for @c null_terminated_string_arg in clang or GCC below 14. */ #define cx_attr_cstr_arg(idx) /** @@ -211,7 +201,7 @@ #endif // __GNUC__ < 10 #if __GNUC__ < 14 /** - * No support for \c null_terminated_string_arg in clang or GCC below 14. + * No support for @c null_terminated_string_arg in clang or GCC below 14. */ #define cx_attr_cstr_arg(idx) #else @@ -276,6 +266,25 @@ #endif // __STDC_VERSION__ + +// --------------------------------------------------------------------------- +// MSVC specifics +// --------------------------------------------------------------------------- + +#ifdef _MSC_VER +// fix missing _Thread_local support +#define _Thread_local __declspec(thread) +#endif // _MSC_VER + +#if defined(CX_WINDLL_EXPORT) +#define cx_attr_export __declspec(dllexport) +#elif defined(CX_WINDLL) +#define cx_attr_export __declspec(dllimport) +#else +/** Only used for building Windows DLLs. */ +#define cx_attr_export +#endif // CX_WINDLL / CX_WINDLL_EXPORT + // --------------------------------------------------------------------------- // Useful function pointers // --------------------------------------------------------------------------- @@ -288,7 +297,7 @@ size_t, size_t, void * -) cx_attr_nonnull; +); /** * Function pointer compatible with fread-like functions. @@ -298,7 +307,7 @@ size_t, size_t, void * -) cx_attr_nonnull; +); // --------------------------------------------------------------------------- // Utility macros @@ -321,66 +330,38 @@ #if (__GNUC__ >= 5 || defined(__clang__)) && !defined(CX_NO_SZMUL_BUILTIN) #define CX_SZMUL_BUILTIN - -/** - * Alias for \c __builtin_mul_overflow. - * - * Performs a multiplication of size_t values and checks for overflow. - * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t, where the result should - * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ #define cx_szmul(a, b, result) __builtin_mul_overflow(a, b, result) - #else // no GNUC or clang bultin - /** * Performs a multiplication of size_t values and checks for overflow. * - * @param a first operand - * @param b second operand - * @param result a pointer to a size_t, where the result should + * @param a (@c size_t) first operand + * @param b (@c size_t) second operand + * @param result (@c size_t*) a pointer to a variable, where the result should * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise + * @retval zero success + * @retval non-zero the multiplication would overflow */ #define cx_szmul(a, b, result) cx_szmul_impl(a, b, result) /** - * Performs a multiplication of size_t values and checks for overflow. + * Implementation of cx_szmul() when no compiler builtin is available. * - * This is a custom implementation in case there is no compiler builtin - * available. + * Do not use in application code. * * @param a first operand * @param b second operand - * @param result a pointer to a size_t where the result should be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise + * @param result a pointer to a variable, where the result should + * be stored + * @retval zero success + * @retval non-zero the multiplication would overflow */ #if __cplusplus extern "C" #endif -int cx_szmul_impl(size_t a, size_t b, size_t *result); - +cx_attr_export int cx_szmul_impl(size_t a, size_t b, size_t *result); #endif // cx_szmul -// --------------------------------------------------------------------------- -// Fixes for MSVC incompatibilities -// --------------------------------------------------------------------------- - -#ifdef _MSC_VER -// fix missing ssize_t definition -#include <BaseTsd.h> -typedef SSIZE_T ssize_t; - -// fix missing _Thread_local support -#define _Thread_local __declspec(thread) -#endif // _MSC_VER #endif // UCX_COMMON_H
--- a/ucx/cx/compare.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/compare.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file compare.h - * \brief A collection of simple compare functions. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file compare.h + * @brief A collection of simple compare functions. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_COMPARE_H @@ -56,90 +56,117 @@ */ cx_attr_nonnull cx_attr_nodiscard -typedef int(*cx_compare_func)( - const void *left, - const void *right +cx_attr_export +typedef int (*cx_compare_func)( + const void *left, + const void *right ); /** * Compares two integers of type int. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to integer one * @param i2 pointer to integer two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_int(const void *i1, const void *i2); /** - * Compares two ints. + * Compares two integers of type int. * * @param i1 integer one * @param i2 integer two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_int(int i1, int i2); /** * Compares two integers of type long int. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to long integer one * @param i2 pointer to long integer two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_longint(const void *i1, const void *i2); /** - * Compares two long ints. + * Compares two integers of type long int. * * @param i1 long integer one * @param i2 long integer two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_longint(long int i1, long int i2); /** * Compares two integers of type long long. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to long long one * @param i2 pointer to long long two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_longlong(const void *i1, const void *i2); /** - * Compares twolong long ints. + * Compares two integers of type long long. * * @param i1 long long int one * @param i2 long long int two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_longlong(long long int i1, long long int i2); /** * Compares two integers of type int16_t. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to int16_t one * @param i2 pointer to int16_t two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_int16(const void *i1, const void *i2); /** @@ -147,22 +174,29 @@ * * @param i1 int16_t one * @param i2 int16_t two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_int16(int16_t i1, int16_t i2); /** * Compares two integers of type int32_t. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to int32_t one * @param i2 pointer to int32_t two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_int32(const void *i1, const void *i2); /** @@ -170,22 +204,29 @@ * * @param i1 int32_t one * @param i2 int32_t two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_int32(int32_t i1, int32_t i2); /** * Compares two integers of type int64_t. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to int64_t one * @param i2 pointer to int64_t two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_int64(const void *i1, const void *i2); /** @@ -193,91 +234,119 @@ * * @param i1 int64_t one * @param i2 int64_t two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_int64(int64_t i1, int64_t i2); /** * Compares two integers of type unsigned int. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to unsigned integer one * @param i2 pointer to unsigned integer two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_uint(const void *i1, const void *i2); /** - * Compares two unsigned ints. + * Compares two integers of type unsigned int. * * @param i1 unsigned integer one * @param i2 unsigned integer two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_uint(unsigned int i1, unsigned int i2); /** * Compares two integers of type unsigned long int. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to unsigned long integer one * @param i2 pointer to unsigned long integer two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_ulongint(const void *i1, const void *i2); /** - * Compares two unsigned long ints. + * Compares two integers of type unsigned long int. * * @param i1 unsigned long integer one * @param i2 unsigned long integer two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2); /** * Compares two integers of type unsigned long long. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to unsigned long long one * @param i2 pointer to unsigned long long two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_ulonglong(const void *i1, const void *i2); /** - * Compares two unsigned long long ints. + * Compares two integers of type unsigned long long. * * @param i1 unsigned long long one * @param i2 unsigned long long two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2); /** * Compares two integers of type uint16_t. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to uint16_t one * @param i2 pointer to uint16_t two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_uint16(const void *i1, const void *i2); /** @@ -285,22 +354,29 @@ * * @param i1 uint16_t one * @param i2 uint16_t two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_uint16(uint16_t i1, uint16_t i2); /** * Compares two integers of type uint32_t. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to uint32_t one * @param i2 pointer to uint32_t two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_uint32(const void *i1, const void *i2); /** @@ -308,22 +384,29 @@ * * @param i1 uint32_t one * @param i2 uint32_t two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_uint32(uint32_t i1, uint32_t i2); /** * Compares two integers of type uint64_t. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param i1 pointer to uint64_t one * @param i2 pointer to uint64_t two - * @return -1, if *i1 is less than *i2, 0 if both are equal, - * 1 if *i1 is greater than *i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_uint64(const void *i1, const void *i2); /** @@ -331,22 +414,29 @@ * * @param i1 uint64_t one * @param i2 uint64_t two - * @return -1, if i1 is less than i2, 0 if both are equal, - * 1 if i1 is greater than i2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_uint64(uint64_t i1, uint64_t i2); /** * Compares two real numbers of type float with precision 1e-6f. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param f1 pointer to float one * @param f2 pointer to float two - * @return -1, if *f1 is less than *f2, 0 if both are equal, - * 1 if *f1 is greater than *f2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_float(const void *f1, const void *f2); /** @@ -354,45 +444,59 @@ * * @param f1 float one * @param f2 float two - * @return -1, if f1 is less than f2, 0 if both are equal, - * 1 if f1 is greater than f2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_float(float f1, float f2); /** * Compares two real numbers of type double with precision 1e-14. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param d1 pointer to double one * @param d2 pointer to double two - * @return -1, if *d1 is less than *d2, 0 if both are equal, - * 1 if *d1 is greater than *d2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_double(const void *d1, const void *d2); /** - * Convenience function + * Compares two real numbers of type double with precision 1e-14. * * @param d1 double one * @param d2 double two - * @return -1, if d1 is less than d2, 0 if both are equal, - * 1 if d1 is greater than d2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_double(double d1, double d2); /** * Compares the integer representation of two pointers. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param ptr1 pointer to pointer one (const intptr_t*) * @param ptr2 pointer to pointer two (const intptr_t*) - * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal, - * 1 if *ptr1 is greater than *ptr2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_intptr(const void *ptr1, const void *ptr2); /** @@ -400,22 +504,29 @@ * * @param ptr1 pointer one * @param ptr2 pointer two - * @return -1 if ptr1 is less than ptr2, 0 if both are equal, - * 1 if ptr1 is greater than ptr2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2); /** * Compares the unsigned integer representation of two pointers. * + * @note the parameters deliberately have type @c void* to be + * compatible with #cx_compare_func without the need of a cast. + * * @param ptr1 pointer to pointer one (const uintptr_t*) * @param ptr2 pointer to pointer two (const uintptr_t*) - * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal, - * 1 if *ptr1 is greater than *ptr2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_uintptr(const void *ptr1, const void *ptr2); /** @@ -423,22 +534,26 @@ * * @param ptr1 pointer one * @param ptr2 pointer two - * @return -1 if ptr1 is less than ptr2, 0 if both are equal, - * 1 if ptr1 is greater than ptr2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard +cx_attr_export int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2); /** - * Compares the pointers specified in the arguments without de-referencing. + * Compares the pointers specified in the arguments without dereferencing. * * @param ptr1 pointer one * @param ptr2 pointer two - * @return -1 if ptr1 is less than ptr2, 0 if both are equal, - * 1 if ptr1 is greater than ptr2 + * @retval -1 if the left argument is less than the right argument + * @retval 0 if both arguments are equal + * @retval 1 if the left argument is greater than the right argument */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cx_cmp_ptr(const void *ptr1, const void *ptr2); #ifdef __cplusplus
--- a/ucx/cx/hash_key.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/hash_key.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file hash_key.h - * \brief Interface for map implementations. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file hash_key.h + * @brief Interface for map implementations. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ @@ -62,16 +62,21 @@ typedef struct cx_hash_key_s CxHashKey; /** - * Computes a murmur2 32 bit hash. + * Computes a murmur2 32-bit hash. * - * You need to initialize \c data and \c len in the key struct. + * You need to initialize @c data and @c len in the key struct. * The hash is then directly written to that struct. * - * \note If \c data is \c NULL, the hash is defined as 1574210520. + * Usually you should not need this function. + * Use cx_hash_key(), instead. + * + * @note If @c data is @c NULL, the hash is defined as 1574210520. * * @param key the key, the hash shall be computed for + * @see cx_hash_key() */ cx_attr_nonnull +cx_attr_export void cx_hash_murmur(CxHashKey *key); /** @@ -84,6 +89,7 @@ */ cx_attr_nodiscard cx_attr_cstr_arg(1) +cx_attr_export CxHashKey cx_hash_key_str(const char *str); /** @@ -95,6 +101,7 @@ */ cx_attr_nodiscard cx_attr_access_r(1, 2) +cx_attr_export CxHashKey cx_hash_key_bytes( const unsigned char *bytes, size_t len @@ -113,6 +120,7 @@ */ cx_attr_nodiscard cx_attr_access_r(1, 2) +cx_attr_export CxHashKey cx_hash_key( const void *obj, size_t len @@ -132,8 +140,8 @@ /** * Computes a hash key from a UCX string. * - * @param str the string - * @return the hash key + * @param str (@c cxstring or @c cxmutstr) the string + * @return (@c CxHashKey) the hash key */ #define cx_hash_key_cxstr(str) cx_hash_key_cxstr(cx_strcast(str))
--- a/ucx/cx/hash_map.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/hash_map.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file hash_map.h - * \brief Hash map implementation. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file hash_map.h + * @brief Hash map implementation. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_HASH_MAP_H @@ -67,17 +67,17 @@ /** * Creates a new hash map with the specified number of buckets. * - * If \p buckets is zero, an implementation defined default will be used. + * If @p buckets is zero, an implementation defined default will be used. * - * If \p elem_size is CX_STORE_POINTERS, the created map will be created as if - * cxMapStorePointers() was called immediately after creation. + * If @p elem_size is #CX_STORE_POINTERS, the created map stores pointers instead of + * copies of the added elements. * * @note Iterators provided by this hash map implementation provide the remove operation. * The index value of an iterator is incremented when the iterator advanced without removal. - * In other words, when the iterator is finished, \c index==size . + * In other words, when the iterator is finished, @c index==size . * * @param allocator the allocator to use - * (if \c NULL, a default stdlib allocator will be used) + * (if @c NULL, a default stdlib allocator will be used) * @param itemsize the size of one element * @param buckets the initial number of buckets in this hash map * @return a pointer to the new hash map @@ -85,6 +85,7 @@ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1) +cx_attr_export CxMap *cxHashMapCreate( const CxAllocator *allocator, size_t itemsize, @@ -94,23 +95,22 @@ /** * Creates a new hash map with a default number of buckets. * - * If \p elem_size is CX_STORE_POINTERS, the created map will be created as if - * cxMapStorePointers() was called immediately after creation. + * If @p elem_size is #CX_STORE_POINTERS, the created map stores pointers instead of + * copies of the added elements. * * @note Iterators provided by this hash map implementation provide the remove operation. * The index value of an iterator is incremented when the iterator advanced without removal. - * In other words, when the iterator is finished, \c index==size . + * In other words, when the iterator is finished, @c index==size . * - * @param itemsize the size of one element - * @return a pointer to the new hash map + * @param itemsize (@c size_t) the size of one element + * @return (@c CxMap*) a pointer to the new hash map */ -#define cxHashMapCreateSimple(itemsize) \ - cxHashMapCreate(cxDefaultAllocator, itemsize, 0) +#define cxHashMapCreateSimple(itemsize) cxHashMapCreate(NULL, itemsize, 0) /** * Increases the number of buckets, if necessary. * - * The load threshold is \c 0.75*buckets. If the element count exceeds the load + * The load threshold is @c 0.75*buckets. If the element count exceeds the load * threshold, the map will be rehashed. Otherwise, no action is performed and * this function simply returns 0. * @@ -123,9 +123,11 @@ * @note If the specified map is not a hash map, the behavior is undefined. * * @param map the map to rehash - * @return zero on success, non-zero if a memory allocation error occurred + * @retval zero success + * @retval non-zero if a memory allocation error occurred */ cx_attr_nonnull +cx_attr_export int cxMapRehash(CxMap *map);
--- a/ucx/cx/iterator.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/iterator.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file iterator.h - * \brief Interface for iterator implementations. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file iterator.h + * @brief Interface for iterator implementations. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_ITERATOR_H @@ -47,9 +47,8 @@ */ struct cx_iterator_base_s { /** - * True iff the iterator points to valid data. + * True if the iterator points to valid data. */ - cx_attr_nonnull bool (*valid)(const void *); /** @@ -57,15 +56,11 @@ * * When valid returns false, the behavior of this function is undefined. */ - cx_attr_nonnull - cx_attr_nodiscard void *(*current)(const void *); /** * Original implementation in case the function needs to be wrapped. */ - cx_attr_nonnull - cx_attr_nodiscard void *(*current_impl)(const void *); /** @@ -73,7 +68,6 @@ * * When valid returns false, the behavior of this function is undefined. */ - cx_attr_nonnull void (*next)(void *); /** * Indicates whether this iterator may remove elements. @@ -86,6 +80,12 @@ }; /** + * Convenience type definition for the base structure of an iterator. + * @see #CX_ITERATOR_BASE + */ +typedef struct cx_iterator_base_s CxIteratorBase; + +/** * Declares base attributes for an iterator. * Must be the first member of an iterator structure. */ @@ -120,27 +120,6 @@ } src_handle; /** - * Field for storing a key-value pair. - * May be used by iterators that iterate over k/v-collections. - */ - struct { - /** - * A pointer to the key. - */ - const void *key; - /** - * A pointer to the value. - */ - void *value; - } kv_data; - - /** - * Field for storing a slot number. - * May be used by iterators that iterate over multi-bucket collections. - */ - size_t slot; - - /** * If the iterator is position-aware, contains the index of the element in the underlying collection. * Otherwise, this field is usually uninitialized. */ @@ -153,7 +132,7 @@ /** * May contain the total number of elements, if known. - * Shall be set to \c SIZE_MAX when the total number is unknown during iteration. + * Shall be set to @c SIZE_MAX when the total number is unknown during iteration. */ size_t elem_count; }; @@ -166,7 +145,7 @@ * to be "position-aware", which means that they keep track of the current index within the collection. * * @note Objects that are pointed to by an iterator are always mutable through that iterator. However, - * any concurrent mutation of the collection other than by this iterator makes this iterator invalid + * any concurrent mutation of the collection other than by this iterator makes this iterator invalid, * and it must not be used anymore. */ typedef struct cx_iterator_s CxIterator; @@ -174,10 +153,9 @@ /** * Checks if the iterator points to valid data. * - * This is especially false for past-the-end iterators. - * * @param iter the iterator - * @return true iff the iterator points to valid data + * @retval true if the iterator points to valid data + * @retval false if the iterator already moved past the end */ #define cxIteratorValid(iter) (iter).base.valid(&(iter)) @@ -188,6 +166,7 @@ * * @param iter the iterator * @return a pointer to the current element + * @see cxIteratorValid() */ #define cxIteratorCurrent(iter) (iter).base.current(&iter) @@ -201,6 +180,8 @@ /** * Flags the current element for removal, if this iterator is mutating. * + * Does nothing for non-mutating iterators. + * * @param iter the iterator */ #define cxIteratorFlagRemoval(iter) (iter).base.remove |= (iter).base.mutating @@ -211,11 +192,13 @@ * This is useful for APIs that expect some iterator as an argument. * * @param iter the iterator + * @return (@c struct @c cx_iterator_base_s*) a pointer to the iterator */ #define cxIteratorRef(iter) &((iter).base) /** * Loops over an iterator. + * * @param type the type of the elements * @param elem the name of the iteration variable * @param iter the iterator @@ -227,21 +210,22 @@ /** * Creates an iterator for the specified plain array. * - * The \p array can be \c NULL in which case the iterator will be immediately - * initialized such that #cxIteratorValid() returns \c false. + * The @p array can be @c NULL in which case the iterator will be immediately + * initialized such that #cxIteratorValid() returns @c false. * * This iterator yields the addresses of the array elements. * If you want to iterator over an array of pointers, you can * use cxIteratorPtr() to create an iterator which directly * yields the stored pointers. * - * @param array a pointer to the array (can be \c NULL) + * @param array a pointer to the array (can be @c NULL) * @param elem_size the size of one array element * @param elem_count the number of elements in the array * @return an iterator for the specified array * @see cxIteratorPtr() */ cx_attr_nodiscard +cx_attr_export CxIterator cxIterator( const void *array, size_t elem_size, @@ -255,23 +239,24 @@ * elements through #cxIteratorFlagRemoval(). Every other change to the array * will bring this iterator to an undefined state. * - * When \p remove_keeps_order is set to \c false, removing an element will only + * When @p remove_keeps_order is set to @c false, removing an element will only * move the last element to the position of the removed element, instead of * moving all subsequent elements by one. Usually, when the order of elements is - * not important, this parameter should be set to \c false. + * not important, this parameter should be set to @c false. * - * The \p array can be \c NULL in which case the iterator will be immediately - * initialized such that #cxIteratorValid() returns \c false. + * The @p array can be @c NULL in which case the iterator will be immediately + * initialized such that #cxIteratorValid() returns @c false. * * - * @param array a pointer to the array (can be \c NULL) + * @param array a pointer to the array (can be @c NULL) * @param elem_size the size of one array element * @param elem_count the number of elements in the array - * @param remove_keeps_order \c true if the order of elements must be preserved + * @param remove_keeps_order @c true if the order of elements must be preserved * when removing an element * @return an iterator for the specified array */ cx_attr_nodiscard +cx_attr_export CxIterator cxMutIterator( void *array, size_t elem_size, @@ -287,12 +272,13 @@ * an iterator created with cxIterator() would return the addresses * of those pointers within the array). * - * @param array a pointer to the array (can be \c NULL) + * @param array a pointer to the array (can be @c NULL) * @param elem_count the number of elements in the array * @return an iterator for the specified array * @see cxIterator() */ cx_attr_nodiscard +cx_attr_export CxIterator cxIteratorPtr( const void *array, size_t elem_count @@ -304,15 +290,16 @@ * This is the mutating variant of cxIteratorPtr(). See also * cxMutIterator(). * - * @param array a pointer to the array (can be \c NULL) + * @param array a pointer to the array (can be @c NULL) * @param elem_count the number of elements in the array - * @param remove_keeps_order \c true if the order of elements must be preserved + * @param remove_keeps_order @c true if the order of elements must be preserved * when removing an element * @return an iterator for the specified array * @see cxMutIterator() * @see cxIteratorPtr() */ cx_attr_nodiscard +cx_attr_export CxIterator cxMutIteratorPtr( void *array, size_t elem_count,
--- a/ucx/cx/json.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/json.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file json.h - * \brief Interface for parsing data from JSON files. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file json.h + * @brief Interface for parsing data from JSON files. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_JSON_H @@ -146,15 +146,15 @@ */ enum cx_json_literal { /** - * The \c null literal. + * The @c null literal. */ CX_JSON_NULL, /** - * The \c true literal. + * The @c true literal. */ CX_JSON_TRUE, /** - * The \c false literal. + * The @c false literal. */ CX_JSON_FALSE }; @@ -264,7 +264,7 @@ /** * The type of this value. * - * Specifies how the \c value union shall be resolved. + * Specifies how the @c value union shall be resolved. */ CxJsonValueType type; /** @@ -309,7 +309,7 @@ */ CxJsonTokenType tokentype; /** - * True, iff the \c content must be passed to cx_strfree(). + * True, if the @c content must be passed to cx_strfree(). */ bool allocated; /** @@ -374,11 +374,6 @@ * Internally reserved memory for the value buffer stack. */ CxJsonValue* vbuf_internal[8]; - - /** - * Used internally. - */ - bool tokenizer_escape; // TODO: check if it can be replaced with look-behind }; /** @@ -403,7 +398,7 @@ * Not used as a status and never returned by any function. * * You can use this enumerator to check for all "good" status results - * by checking if the status is less than \c CX_JSON_OK. + * by checking if the status is less than @c CX_JSON_OK. * * A "good" status means, that you can refill data and continue parsing. */ @@ -449,6 +444,8 @@ bool sort_members; /** * The maximum number of fractional digits in a number value. + * The default value is 6 and values larger than 15 are reduced to 15. + * Note, that the actual number of digits may be lower, depending on the concrete number. */ uint8_t frac_max_digits; /** @@ -457,10 +454,14 @@ */ bool indent_space; /** - * If \c indent_space is true, this is the number of spaces per tab. + * If @c indent_space is true, this is the number of spaces per tab. * Indentation is only used in pretty output. */ uint8_t indent; + /** + * Set true to enable escaping of the slash character (solidus). + */ + bool escape_slash; }; /** @@ -474,6 +475,7 @@ * @return new JSON writer settings */ cx_attr_nodiscard +cx_attr_export CxJsonWriter cxJsonWriterCompact(void); /** @@ -483,17 +485,17 @@ * @return new JSON writer settings */ cx_attr_nodiscard +cx_attr_export CxJsonWriter cxJsonWriterPretty(bool use_spaces); /** * Writes a JSON value to a buffer or stream. * - * This function blocks until all data is written or an error when trying - * to write data occurs. + * This function blocks until either all data is written, or an error occurs. * The write operation is not atomic in the sense that it might happen * that the data is only partially written when an error occurs with no * way to indicate how much data was written. - * To avoid this problem, you can use a CxBuffer as \p target which is + * To avoid this problem, you can use a CxBuffer as @p target which is * unlikely to fail a write operation and either use the buffer's flush * feature to relay the data or use the data in the buffer manually to * write it to the actual target. @@ -501,10 +503,12 @@ * @param target the buffer or stream where to write to * @param value the value that shall be written * @param wfunc the write function to use - * @param settings formatting settings (or \c NULL to use a compact default) - * @return zero on success, non-zero when no or not all data could be written + * @param settings formatting settings (or @c NULL to use a compact default) + * @retval zero success + * @retval non-zero when no or not all data could be written */ cx_attr_nonnull_arg(1, 2, 3) +cx_attr_export int cxJsonWrite( void* target, const CxJsonValue* value, @@ -520,6 +524,7 @@ * @see cxJsonDestroy() */ cx_attr_nonnull_arg(1) +cx_attr_export void cxJsonInit(CxJson *json, const CxAllocator *allocator); /** @@ -529,6 +534,7 @@ * @see cxJsonInit() */ cx_attr_nonnull +cx_attr_export void cxJsonDestroy(CxJson *json); /** @@ -560,11 +566,13 @@ * @param json the json interface * @param buf the source buffer * @param len the length of the source buffer - * @return zero on success, non-zero on internal allocation error + * @retval zero success + * @retval non-zero internal allocation error * @see cxJsonFill() */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export int cxJsonFilln(CxJson *json, const char *buf, size_t len); #ifdef __cplusplus @@ -600,7 +608,7 @@ /** * Fills the input buffer. * - * @remark The JSON interface tries to avoid copying the input data. + * The JSON interface tries to avoid copying the input data. * When you use this function and cxJsonNext() interleaving, * no copies are performed. However, you must not free the * pointer to the data in that case. When you invoke the fill @@ -609,8 +617,9 @@ * an allocation of a new buffer and copying the previous contents. * * @param json the json interface - * @param buf the source string - * @return zero on success, non-zero on internal allocation error + * @param str the source string + * @retval zero success + * @retval non-zero internal allocation error * @see cxJsonFilln() */ #define cxJsonFill(json, str) _Generic((str), \ @@ -659,18 +668,24 @@ * Creates a new (empty) JSON object. * * @param allocator the allocator to use - * @return the new JSON object or \c NULL if allocation fails + * @return the new JSON object or @c NULL if allocation fails + * @see cxJsonObjPutObj() + * @see cxJsonArrAddValues() */ cx_attr_nodiscard +cx_attr_export CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); /** * Creates a new (empty) JSON array. * * @param allocator the allocator to use - * @return the new JSON array or \c NULL if allocation fails + * @return the new JSON array or @c NULL if allocation fails + * @see cxJsonObjPutArr() + * @see cxJsonArrAddValues() */ cx_attr_nodiscard +cx_attr_export CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); /** @@ -678,11 +693,12 @@ * * @param allocator the allocator to use * @param num the numeric value - * @return the new JSON value or \c NULL if allocation fails + * @return the new JSON value or @c NULL if allocation fails * @see cxJsonObjPutNumber() * @see cxJsonArrAddNumbers() */ cx_attr_nodiscard +cx_attr_export CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num); /** @@ -690,11 +706,12 @@ * * @param allocator the allocator to use * @param num the numeric value - * @return the new JSON value or \c NULL if allocation fails + * @return the new JSON value or @c NULL if allocation fails * @see cxJsonObjPutInteger() * @see cxJsonArrAddIntegers() */ cx_attr_nodiscard +cx_attr_export CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num); /** @@ -702,14 +719,15 @@ * * @param allocator the allocator to use * @param str the string data - * @return the new JSON value or \c NULL if allocation fails - * @see cxJsonCreateCxString() + * @return the new JSON value or @c NULL if allocation fails + * @see cxJsonCreateString() * @see cxJsonObjPutString() * @see cxJsonArrAddStrings() */ cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2) +cx_attr_export CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str); /** @@ -717,12 +735,13 @@ * * @param allocator the allocator to use * @param str the string data - * @return the new JSON value or \c NULL if allocation fails - * @see cxJsonCreateString() + * @return the new JSON value or @c NULL if allocation fails + * @see cxJsonCreateCxString() * @see cxJsonObjPutCxString() * @see cxJsonArrAddCxStrings() */ cx_attr_nodiscard +cx_attr_export CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); /** @@ -730,11 +749,12 @@ * * @param allocator the allocator to use * @param lit the type of literal - * @return the new JSON value or \c NULL if allocation fails + * @return the new JSON value or @c NULL if allocation fails * @see cxJsonObjPutLiteral() * @see cxJsonArrAddLiterals() */ cx_attr_nodiscard +cx_attr_export CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit); /** @@ -743,10 +763,12 @@ * @param arr the JSON array * @param num the array of values * @param count the number of elements - * @return zero on success, non-zero on allocation failure + * @retval zero success + * @retval non-zero allocation failure */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count); /** @@ -755,10 +777,12 @@ * @param arr the JSON array * @param num the array of values * @param count the number of elements - * @return zero on success, non-zero on allocation failure + * @retval zero success + * @retval non-zero allocation failure */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count); /** @@ -769,11 +793,13 @@ * @param arr the JSON array * @param str the array of strings * @param count the number of elements - * @return zero on success, non-zero on allocation failure + * @retval zero success + * @retval non-zero allocation failure * @see cxJsonArrAddCxStrings() */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count); /** @@ -784,11 +810,13 @@ * @param arr the JSON array * @param str the array of strings * @param count the number of elements - * @return zero on success, non-zero on allocation failure + * @retval zero success + * @retval non-zero allocation failure * @see cxJsonArrAddStrings() */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count); /** @@ -797,25 +825,29 @@ * @param arr the JSON array * @param lit the array of literal types * @param count the number of elements - * @return zero on success, non-zero on allocation failure + * @retval zero success + * @retval non-zero allocation failure */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count); /** * Add arbitrary values to a JSON array. * - * \note In contrast to all other add functions, this function adds the values + * @attention In contrast to all other add functions, this function adds the values * directly to the array instead of copying them. * * @param arr the JSON array * @param val the values * @param count the number of elements - * @return zero on success, non-zero on allocation failure + * @retval zero success + * @retval non-zero allocation failure */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count); /** @@ -823,15 +855,17 @@ * * The value will be directly added and not copied. * - * \note If a value with the specified \p name already exists, + * @note If a value with the specified @p name already exists, * it will be (recursively) freed with its own allocator. * * @param obj the JSON object * @param name the name of the value * @param child the value - * @return zero on success, non-zero on allocation failure + * @retval zero success + * @retval non-zero allocation failure */ cx_attr_nonnull +cx_attr_export int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); /** @@ -839,11 +873,12 @@ * * @param obj the target JSON object * @param name the name of the new value - * @return the new value or \c NULL if allocation fails + * @return the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateObj() */ cx_attr_nonnull +cx_attr_export CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); /** @@ -851,11 +886,12 @@ * * @param obj the target JSON object * @param name the name of the new value - * @return the new value or \c NULL if allocation fails + * @return the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateArr() */ cx_attr_nonnull +cx_attr_export CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); /** @@ -864,11 +900,12 @@ * @param obj the target JSON object * @param name the name of the new value * @param num the numeric value - * @return the new value or \c NULL if allocation fails + * @return the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateNumber() */ cx_attr_nonnull +cx_attr_export CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); /** @@ -877,11 +914,12 @@ * @param obj the target JSON object * @param name the name of the new value * @param num the numeric value - * @return the new value or \c NULL if allocation fails + * @return the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateInteger() */ cx_attr_nonnull +cx_attr_export CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); /** @@ -892,12 +930,13 @@ * @param obj the target JSON object * @param name the name of the new value * @param str the string data - * @return the new value or \c NULL if allocation fails + * @return the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateString() */ cx_attr_nonnull cx_attr_cstr_arg(3) +cx_attr_export CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); /** @@ -908,11 +947,12 @@ * @param obj the target JSON object * @param name the name of the new value * @param str the string data - * @return the new value or \c NULL if allocation fails + * @return the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateCxString() */ cx_attr_nonnull +cx_attr_export CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); /** @@ -921,17 +961,18 @@ * @param obj the target JSON object * @param name the name of the new value * @param lit the type of literal - * @return the new value or \c NULL if allocation fails + * @return the new value or @c NULL if allocation fails * @see cxJsonObjPut() * @see cxJsonCreateLiteral() */ cx_attr_nonnull +cx_attr_export CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); /** * Recursively deallocates the memory of a JSON value. * - * \remark The type of each deallocated value will be changed + * @remark The type of each deallocated value will be changed * to #CX_JSON_NOTHING and values of such type will be skipped * by the de-allocation. That means, this function protects * you from double-frees when you are accidentally freeing @@ -939,25 +980,42 @@ * * @param value the value */ +cx_attr_export void cxJsonValueFree(CxJsonValue *value); /** * Tries to obtain the next JSON value. * + * Before this function can be called, the input buffer needs + * to be filled with cxJsonFill(). + * + * When this function returns #CX_JSON_INCOMPLETE_DATA, you can + * add the missing data with another invocation of cxJsonFill() + * and then repeat the call to cxJsonNext(). * * @param json the json interface * @param value a pointer where the next value shall be stored - * @return a status code + * @retval CX_JSON_NO_ERROR successfully retrieve the @p value + * @retval CX_JSON_NO_DATA there is no (more) data in the buffer to read from + * @retval CX_JSON_INCOMPLETE_DATA an incomplete value was read + * and more data needs to be filled + * @retval CX_JSON_NULL_DATA the buffer was never initialized + * @retval CX_JSON_BUFFER_ALLOC_FAILED allocating internal buffer space failed + * @retval CX_JSON_VALUE_ALLOC_FAILED allocating memory for a CxJsonValue failed + * @retval CX_JSON_FORMAT_ERROR_NUMBER the JSON text contains an illegally formatted number + * @retval CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN JSON syntax error */ cx_attr_nonnull cx_attr_access_w(2) +cx_attr_export CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); /** * Checks if the specified value is a JSON object. * * @param value a pointer to the value - * @return true if the value is a JSON object, false otherwise + * @retval true the value is a JSON object + * @retval false otherwise */ cx_attr_nonnull static inline bool cxJsonIsObject(const CxJsonValue *value) { @@ -968,7 +1026,8 @@ * Checks if the specified value is a JSON array. * * @param value a pointer to the value - * @return true if the value is a JSON array, false otherwise + * @retval true the value is a JSON array + * @retval false otherwise */ cx_attr_nonnull static inline bool cxJsonIsArray(const CxJsonValue *value) { @@ -979,7 +1038,8 @@ * Checks if the specified value is a string. * * @param value a pointer to the value - * @return true if the value is a string, false otherwise + * @retval true the value is a string + * @retval false otherwise */ cx_attr_nonnull static inline bool cxJsonIsString(const CxJsonValue *value) { @@ -993,7 +1053,8 @@ * integer numbers. * * @param value a pointer to the value - * @return true if the value is a JSON number, false otherwise + * @retval true the value is a JSON number + * @retval false otherwise * @see cxJsonIsInteger() */ cx_attr_nonnull @@ -1005,7 +1066,8 @@ * Checks if the specified value is an integer number. * * @param value a pointer to the value - * @return true if the value is an integer number, false otherwise + * @retval true the value is an integer number + * @retval false otherwise * @see cxJsonIsNumber() */ cx_attr_nonnull @@ -1016,10 +1078,11 @@ /** * Checks if the specified value is a JSON literal. * - * JSON literals are \c true, \c false, and \c null. + * JSON literals are @c true, @c false, and @c null. * * @param value a pointer to the value - * @return true if the value is a JSON literal, false otherwise + * @retval true the value is a JSON literal + * @retval false otherwise * @see cxJsonIsTrue() * @see cxJsonIsFalse() * @see cxJsonIsNull() @@ -1033,7 +1096,8 @@ * Checks if the specified value is a Boolean literal. * * @param value a pointer to the value - * @return true if the value is either \c true or \c false, false otherwise + * @retval true the value is either @c true or @c false + * @retval false otherwise * @see cxJsonIsTrue() * @see cxJsonIsFalse() */ @@ -1043,13 +1107,14 @@ } /** - * Checks if the specified value is \c true. + * Checks if the specified value is @c true. * - * \remark Be advised, that this is not the same as - * testing \c !cxJsonIsFalse(v). + * @remark Be advised, that this is not the same as + * testing @c !cxJsonIsFalse(v). * * @param value a pointer to the value - * @return true if the value is \c true, false otherwise + * @retval true the value is @c true + * @retval false otherwise * @see cxJsonIsBool() * @see cxJsonIsFalse() */ @@ -1059,13 +1124,14 @@ } /** - * Checks if the specified value is \c false. + * Checks if the specified value is @c false. * - * \remark Be advised, that this is not the same as - * testing \c !cxJsonIsTrue(v). + * @remark Be advised, that this is not the same as + * testing @c !cxJsonIsTrue(v). * * @param value a pointer to the value - * @return true if the value is \c false, false otherwise + * @retval true the value is @c false + * @retval false otherwise * @see cxJsonIsBool() * @see cxJsonIsTrue() */ @@ -1075,10 +1141,11 @@ } /** - * Checks if the specified value is \c null. + * Checks if the specified value is @c null. * * @param value a pointer to the value - * @return true if the value is \c null, false otherwise + * @retval true the value is @c null + * @retval false otherwise * @see cxJsonIsLiteral() */ cx_attr_nonnull @@ -1089,7 +1156,7 @@ /** * Obtains a C string from the given JSON value. * - * If the \p value is not a string, the behavior is undefined. + * If the @p value is not a string, the behavior is undefined. * * @param value the JSON value * @return the value represented as C string @@ -1104,7 +1171,7 @@ /** * Obtains a UCX string from the given JSON value. * - * If the \p value is not a string, the behavior is undefined. + * If the @p value is not a string, the behavior is undefined. * * @param value the JSON value * @return the value represented as UCX string @@ -1118,7 +1185,7 @@ /** * Obtains a mutable UCX string from the given JSON value. * - * If the \p value is not a string, the behavior is undefined. + * If the @p value is not a string, the behavior is undefined. * * @param value the JSON value * @return the value represented as mutable UCX string @@ -1132,7 +1199,7 @@ /** * Obtains a double-precision floating point value from the given JSON value. * - * If the \p value is not a JSON number, the behavior is undefined. + * If the @p value is not a JSON number, the behavior is undefined. * * @param value the JSON value * @return the value represented as double @@ -1150,7 +1217,7 @@ /** * Obtains a 64-bit signed integer from the given JSON value. * - * If the \p value is not a JSON number, the behavior is undefined. + * If the @p value is not a JSON number, the behavior is undefined. * If it is a JSON number, but not an integer, the value will be * converted to an integer, possibly losing precision. * @@ -1171,8 +1238,8 @@ /** * Obtains a Boolean value from the given JSON value. * - * If the \p value is not a JSON literal, the behavior is undefined. - * The \c null literal is interpreted as \c false. + * If the @p value is not a JSON literal, the behavior is undefined. + * The @c null literal is interpreted as @c false. * * @param value the JSON value * @return the value represented as double @@ -1186,7 +1253,7 @@ /** * Returns the size of a JSON array. * - * If the \p value is not a JSON array, the behavior is undefined. + * If the @p value is not a JSON array, the behavior is undefined. * * @param value the JSON value * @return the size of the array @@ -1200,11 +1267,11 @@ /** * Returns an element from a JSON array. * - * If the \p value is not a JSON array, the behavior is undefined. + * If the @p value is not a JSON array, the behavior is undefined. * * This function guarantees to return a value. If the index is * out of bounds, the returned value will be of type - * #CX_JSON_NOTHING, but never \c NULL. + * #CX_JSON_NOTHING, but never @c NULL. * * @param value the JSON value * @param index the index in the array @@ -1213,14 +1280,15 @@ */ cx_attr_nonnull cx_attr_returns_nonnull +cx_attr_export CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); /** * Returns an iterator over the JSON array elements. * - * The iterator yields values of type \c CxJsonValue* . + * The iterator yields values of type @c CxJsonValue* . * - * If the \p value is not a JSON array, the behavior is undefined. + * If the @p value is not a JSON array, the behavior is undefined. * * @param value the JSON value * @return an iterator over the array elements @@ -1228,15 +1296,16 @@ */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export CxIterator cxJsonArrIter(const CxJsonValue *value); /** * Returns an iterator over the JSON object members. * - * The iterator yields values of type \c CxJsonObjValue* which + * The iterator yields values of type @c CxJsonObjValue* which * contain the name and value of the member. * - * If the \p value is not a JSON object, the behavior is undefined. + * If the @p value is not a JSON object, the behavior is undefined. * * @param value the JSON value * @return an iterator over the object members @@ -1244,6 +1313,7 @@ */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export CxIterator cxJsonObjIter(const CxJsonValue *value); /** @@ -1251,20 +1321,21 @@ */ cx_attr_nonnull cx_attr_returns_nonnull +cx_attr_export CxJsonValue *cx_json_obj_get_cxstr(const CxJsonValue *value, cxstring name); #ifdef __cplusplus } // extern "C" -CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) { +static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxstring name) { return cx_json_obj_get_cxstr(value, name); } -CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) { +static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, cxmutstr name) { return cx_json_obj_get_cxstr(value, cx_strcast(name)); } -CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) { +static inline CxJsonValue *cxJsonObjGet(const CxJsonValue *value, const char *name) { return cx_json_obj_get_cxstr(value, cx_str(name)); } @@ -1273,11 +1344,11 @@ /** * Returns a value corresponding to a key in a JSON object. * - * If the \p value is not a JSON object, the behavior is undefined. + * If the @p value is not a JSON object, the behavior is undefined. * * This function guarantees to return a JSON value. If the - * object does not contain \p name, the returned JSON value - * will be of type #CX_JSON_NOTHING, but never \c NULL. + * object does not contain @p name, the returned JSON value + * will be of type #CX_JSON_NOTHING, but never @c NULL. * * @param value the JSON object * @param name the key to look up
--- a/ucx/cx/linked_list.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/linked_list.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,12 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file linked_list.h - * \brief Linked list implementation. - * \details Also provides several low-level functions for custom linked list implementations. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file linked_list.h + * @brief Linked list implementation. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_LINKED_LIST_H @@ -45,21 +44,16 @@ #endif /** - * The maximum item size that uses SBO swap instead of relinking. - */ -extern const unsigned cx_linked_list_swap_sbo_size; - -/** - * Allocates a linked list for storing elements with \p elem_size bytes each. + * Allocates a linked list for storing elements with @p elem_size bytes each. * - * If \p elem_size is CX_STORE_POINTERS, the created list will be created as if - * cxListStorePointers() was called immediately after creation and the compare - * function will be automatically set to cx_cmp_ptr(), if none is given. + * 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. * * @param allocator the allocator for allocating the list nodes - * (if \c NULL, a default stdlib allocator will be used) + * (if @c NULL, a default stdlib allocator will be used) * @param comparator the comparator for the elements - * (if \c NULL, and the list is not storing pointers, sort and find + * (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 * @return the created list @@ -67,6 +61,7 @@ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) +cx_attr_export CxList *cxLinkedListCreate( const CxAllocator *allocator, cx_compare_func comparator, @@ -74,18 +69,18 @@ ); /** - * Allocates a linked list for storing elements with \p elem_size bytes each. + * Allocates a linked list for storing elements with @p elem_size bytes each. * * The list will use cxDefaultAllocator and no comparator function. If you want * to call functions that need a comparator, you must either set one immediately * after list creation or use cxLinkedListCreate(). * - * If \p elem_size is CX_STORE_POINTERS, the created list will be created as if - * cxListStorePointers() was called immediately after creation and the compare - * function will be automatically set to cx_cmp_ptr(). + * 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. * - * @param elem_size the size of each element in bytes - * @return the created list + * @param elem_size (@c size_t) the size of each element in bytes + * @return (@c CxList*) the created list */ #define cxLinkedListCreateSimple(elem_size) \ cxLinkedListCreate(NULL, NULL, elem_size) @@ -94,11 +89,11 @@ * Finds the node at a certain index. * * This function can be used to start at an arbitrary position within the list. - * If the search index is large than the start index, \p loc_advance must denote - * the location of some sort of \c next pointer (i.e. a pointer to the next node). + * If the search index is large than the start index, @p loc_advance must denote + * the location of some sort of @c next pointer (i.e. a pointer to the next node). * But it is also possible that the search index is smaller than the start index * (e.g. in cases where traversing a list backwards is faster) in which case - * \p loc_advance must denote the location of some sort of \c prev pointer + * @p loc_advance must denote the location of some sort of @c prev pointer * (i.e. a pointer to the previous node). * * @param start a pointer to the start node @@ -109,6 +104,7 @@ */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export void *cx_linked_list_at( const void *start, size_t start_index, @@ -117,59 +113,42 @@ ); /** - * Finds the index of an element within a linked list. + * Finds the node containing an element within a linked list. * * @param start a pointer to the start node * @param loc_advance the location of the pointer to advance - * @param loc_data the location of the \c data pointer within your node struct - * @param cmp_func a compare function to compare \p elem against the node data + * @param loc_data the location of the @c data pointer within your node struct + * @param cmp_func a compare function to compare @p elem against the node data * @param elem a pointer to the element to find - * @return the index of the element or a negative value if it could not be found + * @param found_index an optional pointer where the index of the found node + * (given that @p start has index 0) is stored + * @return the index of the element, if found - unspecified if not found */ -cx_attr_nonnull -ssize_t cx_linked_list_find( +cx_attr_nonnull_arg(1, 4, 5) +cx_attr_export +void *cx_linked_list_find( const void *start, ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func, - const void *elem -); - -/** - * Finds the node containing an element within a linked list. - * - * @param result a pointer to the memory where the node pointer (or \c NULL if the element - * could not be found) shall be stored to - * @param start a pointer to the start node - * @param loc_advance the location of the pointer to advance - * @param loc_data the location of the \c data pointer within your node struct - * @param cmp_func a compare function to compare \p elem against the node data - * @param elem a pointer to the element to find - * @return the index of the element or a negative value if it could not be found - */ -cx_attr_nonnull -ssize_t cx_linked_list_find_node( - void **result, - const void *start, - ptrdiff_t loc_advance, - ptrdiff_t loc_data, - cx_compare_func cmp_func, - const void *elem + const void *elem, + size_t *found_index ); /** * Finds the first node in a linked list. * - * The function starts with the pointer denoted by \p node and traverses the list + * The function starts with the pointer denoted by @p node and traverses the list * along a prev pointer whose location within the node struct is - * denoted by \p loc_prev. + * denoted by @p loc_prev. * * @param node a pointer to a node in the list - * @param loc_prev the location of the \c prev pointer + * @param loc_prev the location of the @c prev pointer * @return a pointer to the first node */ cx_attr_nonnull cx_attr_returns_nonnull +cx_attr_export void *cx_linked_list_first( const void *node, ptrdiff_t loc_prev @@ -178,16 +157,17 @@ /** * Finds the last node in a linked list. * - * The function starts with the pointer denoted by \p node and traverses the list + * The function starts with the pointer denoted by @p node and traverses the list * along a next pointer whose location within the node struct is - * denoted by \p loc_next. + * denoted by @p loc_next. * * @param node a pointer to a node in the list - * @param loc_next the location of the \c next pointer + * @param loc_next the location of the @c next pointer * @return a pointer to the last node */ cx_attr_nonnull cx_attr_returns_nonnull +cx_attr_export void *cx_linked_list_last( const void *node, ptrdiff_t loc_next @@ -196,14 +176,15 @@ /** * Finds the predecessor of a node in case it is not linked. * - * \remark If \p node is not contained in the list starting with \p begin, the behavior is undefined. + * @remark If @p node is not contained in the list starting with @p begin, the behavior is undefined. * * @param begin the node where to start the search - * @param loc_next the location of the \c next pointer + * @param loc_next the location of the @c next pointer * @param node the successor of the node to find - * @return the node or \c NULL if \p node has no predecessor + * @return the node or @c NULL if @p node has no predecessor */ cx_attr_nonnull +cx_attr_export void *cx_linked_list_prev( const void *begin, ptrdiff_t loc_next, @@ -214,15 +195,16 @@ * Adds a new node to a linked list. * The node must not be part of any list already. * - * \remark One of the pointers \p begin or \p end may be \c NULL, but not both. + * @remark One of the pointers @p begin or @p end may be @c NULL, but not both. * - * @param begin a pointer to the begin node pointer (if your list has one) + * @param begin a pointer to the beginning node pointer (if your list has one) * @param end a pointer to the end node pointer (if your list has one) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) * @param new_node a pointer to the node that shall be appended */ cx_attr_nonnull_arg(5) +cx_attr_export void cx_linked_list_add( void **begin, void **end, @@ -235,15 +217,16 @@ * Prepends a new node to a linked list. * The node must not be part of any list already. * - * \remark One of the pointers \p begin or \p end may be \c NULL, but not both. + * @remark One of the pointers @p begin or @p end may be @c NULL, but not both. * - * @param begin a pointer to the begin node pointer (if your list has one) + * @param begin a pointer to the beginning node pointer (if your list has one) * @param end a pointer to the end node pointer (if your list has one) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) * @param new_node a pointer to the node that shall be prepended */ cx_attr_nonnull_arg(5) +cx_attr_export void cx_linked_list_prepend( void **begin, void **end, @@ -255,12 +238,13 @@ /** * Links two nodes. * - * @param left the new predecessor of \p right - * @param right the new successor of \p left - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param left the new predecessor of @p right + * @param right the new successor of @p left + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) */ cx_attr_nonnull +cx_attr_export void cx_linked_list_link( void *left, void *right, @@ -273,12 +257,13 @@ * * If right is not the successor of left, the behavior is undefined. * - * @param left the predecessor of \p right - * @param right the successor of \p left - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param left the predecessor of @p right + * @param right the successor of @p left + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) */ cx_attr_nonnull +cx_attr_export void cx_linked_list_unlink( void *left, void *right, @@ -290,17 +275,18 @@ * Inserts a new node after a given node of a linked list. * The new node must not be part of any list already. * - * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or - * the \p end pointer to determine the start of the list. Then the new node will be prepended to the list. + * @note If you specify @c NULL as the @p node to insert after, this function needs either the @p begin or + * the @p end pointer to determine the start of the list. Then the new node will be prepended to the list. * - * @param begin a pointer to the begin node pointer (if your list has one) + * @param begin a pointer to the beginning node pointer (if your list has one) * @param end a pointer to the end node pointer (if your list has one) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) - * @param node the node after which to insert (\c NULL if you want to prepend the node to the list) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) + * @param node the node after which to insert (@c NULL if you want to prepend the node to the list) * @param new_node a pointer to the node that shall be inserted */ cx_attr_nonnull_arg(6) +cx_attr_export void cx_linked_list_insert( void **begin, void **end, @@ -315,22 +301,23 @@ * The chain must not be part of any list already. * * If you do not explicitly specify the end of the chain, it will be determined by traversing - * the \c next pointer. + * the @c next pointer. * - * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or - * the \p end pointer to determine the start of the list. If only the \p end pointer is specified, you also need - * to provide a valid \p loc_prev location. + * @note If you specify @c NULL as the @p node to insert after, this function needs either the @p begin or + * the @p end pointer to determine the start of the list. If only the @p end pointer is specified, you also need + * to provide a valid @p loc_prev location. * Then the chain will be prepended to the list. * - * @param begin a pointer to the begin node pointer (if your list has one) + * @param begin a pointer to the beginning node pointer (if your list has one) * @param end a pointer to the end node pointer (if your list has one) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) - * @param node the node after which to insert (\c NULL to prepend the chain to the list) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) + * @param node the node after which to insert (@c NULL to prepend the chain to the list) * @param insert_begin a pointer to the first node of the chain that shall be inserted * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined) */ cx_attr_nonnull_arg(6) +cx_attr_export void cx_linked_list_insert_chain( void **begin, void **end, @@ -345,17 +332,18 @@ * Inserts a node into a sorted linked list. * The new node must not be part of any list already. * - * If the list starting with the node pointed to by \p begin is not sorted + * If the list starting with the node pointed to by @p begin is not sorted * already, the behavior is undefined. * - * @param begin a pointer to the begin node pointer (required) + * @param begin a pointer to the beginning node pointer (required) * @param end a pointer to the end node pointer (if your list has one) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) * @param new_node a pointer to the node that shall be inserted * @param cmp_func a compare function that will receive the node pointers */ cx_attr_nonnull_arg(1, 5, 6) +cx_attr_export void cx_linked_list_insert_sorted( void **begin, void **end, @@ -369,22 +357,23 @@ * Inserts a chain of nodes into a sorted linked list. * The chain must not be part of any list already. * - * If either the list starting with the node pointed to by \p begin or the list - * starting with \p insert_begin is not sorted, the behavior is undefined. + * If either the list starting with the node pointed to by @p begin or the list + * starting with @p insert_begin is not sorted, the behavior is undefined. * - * \attention In contrast to cx_linked_list_insert_chain(), the source chain + * @attention In contrast to cx_linked_list_insert_chain(), the source chain * will be broken and inserted into the target list so that the resulting list - * will be sorted according to \p cmp_func. That means, each node in the source + * will be sorted according to @p cmp_func. That means, each node in the source * chain may be re-linked with nodes from the target list. * - * @param begin a pointer to the begin node pointer (required) + * @param begin a pointer to the beginning node pointer (required) * @param end a pointer to the end node pointer (if your list has one) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) * @param insert_begin a pointer to the first node of the chain that shall be inserted * @param cmp_func a compare function that will receive the node pointers */ cx_attr_nonnull_arg(1, 5, 6) +cx_attr_export void cx_linked_list_insert_sorted_chain( void **begin, void **end, @@ -397,25 +386,26 @@ /** * Removes a chain of nodes from the linked list. * - * If one of the nodes to remove is the begin (resp. end) node of the list and if \p begin (resp. \p end) + * If one of the nodes to remove is the beginning (resp. end) node of the list and if @p begin (resp. @p end) * addresses are provided, the pointers are adjusted accordingly. * * The following combinations of arguments are valid (more arguments are optional): - * \li \p loc_next and \p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance) - * \li \p loc_next and \p begin (ancestor node is determined by list traversal, overall O(n) performance) + * @li @p loc_next and @p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance) + * @li @p loc_next and @p begin (ancestor node is determined by list traversal, overall O(n) performance) * - * \remark The \c next and \c prev pointers of the removed node are not cleared by this function and may still be used + * @remark The @c next and @c prev pointers of the removed node are not cleared by this function and may still be used * to traverse to a former adjacent node in the list, or within the chain. * - * @param begin a pointer to the begin node pointer (optional) + * @param begin a pointer to the beginning node pointer (optional) * @param end a pointer to the end node pointer (optional) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) * @param node the start node of the chain * @param num the number of nodes to remove - * @return the actual number of nodes that were removed (may be less when the list did not have enough nodes) + * @return the actual number of nodes that were removed (can be less when the list did not have enough nodes) */ cx_attr_nonnull_arg(5) +cx_attr_export size_t cx_linked_list_remove_chain( void **begin, void **end, @@ -428,20 +418,20 @@ /** * Removes a node from the linked list. * - * If the node to remove is the begin (resp. end) node of the list and if \p begin (resp. \p end) + * If the node to remove is the beginning (resp. end) node of the list and if @p begin (resp. @p end) * addresses are provided, the pointers are adjusted accordingly. * * The following combinations of arguments are valid (more arguments are optional): - * \li \p loc_next and \p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance) - * \li \p loc_next and \p begin (ancestor node is determined by list traversal, overall O(n) performance) + * @li @p loc_next and @p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance) + * @li @p loc_next and @p begin (ancestor node is determined by list traversal, overall O(n) performance) * - * \remark The \c next and \c prev pointers of the removed node are not cleared by this function and may still be used + * @remark The @c next and @c prev pointers of the removed node are not cleared by this function and may still be used * to traverse to a former adjacent node in the list. * - * @param begin a pointer to the begin node pointer (optional) + * @param begin a pointer to the beginning node pointer (optional) * @param end a pointer to the end node pointer (optional) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) * @param node the node to remove */ cx_attr_nonnull_arg(5) @@ -456,11 +446,14 @@ } /** - * Determines the size of a linked list starting with \p node. + * Determines the size of a linked list starting with @p node. + * * @param node the first node - * @param loc_next the location of the \c next pointer within the node struct - * @return the size of the list or zero if \p node is \c NULL + * @param loc_next the location of the @c next pointer within the node struct + * @return the size of the list or zero if @p node is @c NULL */ +cx_attr_nodiscard +cx_attr_export size_t cx_linked_list_size( const void *node, ptrdiff_t loc_next @@ -470,25 +463,26 @@ * Sorts a linked list based on a comparison function. * * This function can work with linked lists of the following structure: - * \code + * @code * typedef struct node node; * struct node { * node* prev; * node* next; * my_payload data; * } - * \endcode + * @endcode * * @note This is a recursive function with at most logarithmic recursion depth. * - * @param begin a pointer to the begin node pointer (required) + * @param begin a pointer to the beginning node pointer (required) * @param end a pointer to the end node pointer (optional) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if not present) - * @param loc_next the location of a \c next pointer within your node struct (required) - * @param loc_data the location of the \c data pointer within your node struct + * @param loc_prev the location of a @c prev pointer within your node struct (negative if not present) + * @param loc_next the location of a @c next pointer within your node struct (required) + * @param loc_data the location of the @c data pointer within your node struct * @param cmp_func the compare function defining the sort order */ cx_attr_nonnull_arg(1, 6) +cx_attr_export void cx_linked_list_sort( void **begin, void **end, @@ -502,17 +496,18 @@ /** * Compares two lists element wise. * - * \note Both list must have the same structure. + * @attention Both list must have the same structure. * - * @param begin_left the begin of the left list (\c NULL denotes an empty list) - * @param begin_right the begin of the right list (\c NULL denotes an empty list) + * @param begin_left the beginning of the left list (@c NULL denotes an empty list) + * @param begin_right the beginning of the right list (@c NULL denotes an empty list) * @param loc_advance the location of the pointer to advance - * @param loc_data the location of the \c data pointer within your node struct + * @param loc_data the location of the @c data pointer within your node struct * @param cmp_func the function to compare the elements - * @return the first non-zero result of invoking \p cmp_func or: negative if the left list is smaller than the + * @return the first non-zero result of invoking @p cmp_func or: negative if the left list is smaller than the * right list, positive if the left list is larger than the right list, zero if both lists are equal. */ cx_attr_nonnull_arg(5) +cx_attr_export int cx_linked_list_compare( const void *begin_left, const void *begin_right, @@ -524,12 +519,13 @@ /** * Reverses the order of the nodes in a linked list. * - * @param begin a pointer to the begin node pointer (required) + * @param begin a pointer to the beginning node pointer (required) * @param end a pointer to the end node pointer (optional) - * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one) - * @param loc_next the location of a \c next pointer within your node struct (required) + * @param loc_prev the location of a @c prev pointer within your node struct (negative if your struct does not have one) + * @param loc_next the location of a @c next pointer within your node struct (required) */ cx_attr_nonnull_arg(1) +cx_attr_export void cx_linked_list_reverse( void **begin, void **end,
--- a/ucx/cx/list.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/list.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file list.h - * \brief Interface for list implementations. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file list.h + * @brief Interface for list implementations. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_LIST_H @@ -76,14 +76,11 @@ * Implementations SHALL invoke the content destructor functions if provided * and SHALL deallocate the entire list memory. */ - cx_attr_nonnull void (*deallocate)(struct cx_list_s *list); /** * Member function for inserting a single element. - * Implementors SHOULD see to performant implementations for corner cases. */ - cx_attr_nonnull int (*insert_element)( struct cx_list_s *list, size_t index, @@ -92,10 +89,9 @@ /** * Member function for inserting multiple elements. - * Implementors SHOULD see to performant implementations for corner cases. + * * @see cx_list_default_insert_array() */ - cx_attr_nonnull size_t (*insert_array)( struct cx_list_s *list, size_t index, @@ -108,7 +104,6 @@ * * @see cx_list_default_insert_sorted() */ - cx_attr_nonnull size_t (*insert_sorted)( struct cx_list_s *list, const void *sorted_data, @@ -118,7 +113,6 @@ /** * Member function for inserting an element relative to an iterator position. */ - cx_attr_nonnull int (*insert_iter)( struct cx_iterator_s *iter, const void *elem, @@ -128,15 +122,13 @@ /** * Member function for removing elements. * - * Implementations SHALL check if \p targetbuf is set and copy the elements + * Implementations SHALL check if @p targetbuf is set and copy the elements * to the buffer without invoking any destructor. - * When \p targetbuf is not set, the destructors SHALL be invoked. + * When @p targetbuf is not set, the destructors SHALL be invoked. * * The function SHALL return the actual number of elements removed, which - * might be lower than \p num when going out of bounds. + * might be lower than @p num when going out of bounds. */ - cx_attr_nonnull_arg(1) - cx_attr_access_w(4) size_t (*remove)( struct cx_list_s *list, size_t index, @@ -147,14 +139,13 @@ /** * Member function for removing all elements. */ - cx_attr_nonnull void (*clear)(struct cx_list_s *list); /** * Member function for swapping two elements. + * * @see cx_list_default_swap() */ - cx_attr_nonnull int (*swap)( struct cx_list_s *list, size_t i, @@ -164,8 +155,6 @@ /** * Member function for element lookup. */ - cx_attr_nonnull - cx_attr_nodiscard void *(*at)( const struct cx_list_s *list, size_t index @@ -174,25 +163,23 @@ /** * Member function for finding and optionally removing an element. */ - cx_attr_nonnull - cx_attr_nodiscard - ssize_t (*find_remove)( + size_t (*find_remove)( struct cx_list_s *list, const void *elem, bool remove ); /** - * Member function for sorting the list in-place. + * Member function for sorting the list. + * * @see cx_list_default_sort() */ - cx_attr_nonnull void (*sort)(struct cx_list_s *list); /** * Optional member function for comparing this list * to another list of the same type. - * If set to \c NULL, comparison won't be optimized. + * If set to @c NULL, comparison won't be optimized. */ cx_attr_nonnull int (*compare)( @@ -203,13 +190,11 @@ /** * Member function for reversing the order of the items. */ - cx_attr_nonnull void (*reverse)(struct cx_list_s *list); /** * Member function for returning an iterator pointing to the specified index. */ - cx_attr_nonnull struct cx_iterator_s (*iterator)( const struct cx_list_s *list, size_t index, @@ -232,6 +217,7 @@ * @return the number of elements actually inserted */ cx_attr_nonnull +cx_attr_export size_t cx_list_default_insert_array( struct cx_list_s *list, size_t index, @@ -245,7 +231,7 @@ * This function uses the array insert function to insert consecutive groups * of sorted data. * - * The source data \em must already be sorted wrt. the list's compare function. + * The source data @em must already be sorted wrt. the list's compare function. * * Use this in your own list class if you do not want to implement an optimized * version for your list. @@ -256,6 +242,7 @@ * @return the number of elements actually inserted */ cx_attr_nonnull +cx_attr_export size_t cx_list_default_insert_sorted( struct cx_list_s *list, const void *sorted_data, @@ -274,6 +261,7 @@ * @param list the list that shall be sorted */ cx_attr_nonnull +cx_attr_export void cx_list_default_sort(struct cx_list_s *list); /** @@ -285,57 +273,74 @@ * @param list the list in which to swap * @param i index of one element * @param j index of the other element - * @return zero on success, non-zero when indices are out of bounds or memory + * @retval zero success + * @retval non-zero when indices are out of bounds or memory * allocation for the temporary buffer fails */ cx_attr_nonnull +cx_attr_export int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j); /** + * Initializes a list struct. + * + * Only use this function if you are creating your own list implementation. + * The purpose of this function is to be called in the initialization code + * of your list, to set certain members correctly. + * + * This is particularly important when you want your list to support + * #CX_STORE_POINTERS as @p elem_size. This function will wrap the list + * class accordingly and make sure that you can implement your list as if + * it was only storing objects and the wrapper will automatically enable + * the feature of storing pointers. + * + * @par Example + * + * @code + * CxList *myCustomListCreate( + * const CxAllocator *allocator, + * cx_compare_func comparator, + * size_t elem_size + * ) { + * if (allocator == NULL) { + * allocator = cxDefaultAllocator; + * } + * + * MyCustomList *list = cxCalloc(allocator, 1, sizeof(MyCustomList)); + * if (list == NULL) return NULL; + * + * // initialize + * cx_list_init((CxList*)list, &my_custom_list_class, + * allocator, comparator, elem_size); + * + * // ... some more custom stuff ... + * + * return (CxList *) list; + * } + * @endcode + * + * @param list the list to initialize + * @param cl the list class + * @param allocator the allocator for the elements + * @param comparator a compare function for the elements + * @param elem_size the size of one element + */ +cx_attr_nonnull_arg(1, 2, 3) +cx_attr_export +void cx_list_init( + struct cx_list_s *list, + struct cx_list_class_s *cl, + const struct cx_allocator_s *allocator, + cx_compare_func comparator, + size_t elem_size +); + +/** * Common type for all list implementations. */ typedef struct cx_list_s CxList; /** - * Advises the list to store copies of the objects (default mode of operation). - * - * Retrieving objects from this list will yield pointers to the copies stored - * within this list. - * - * @param list the list - * @see cxListStorePointers() - */ -cx_attr_nonnull -void cxListStoreObjects(CxList *list); - -/** - * Advises the list to only store pointers to the objects. - * - * Retrieving objects from this list will yield the original pointers stored. - * - * @note This function forcibly sets the element size to the size of a pointer. - * Invoking this function on a non-empty list that already stores copies of - * objects is undefined. - * - * @param list the list - * @see cxListStoreObjects() - */ -cx_attr_nonnull -void cxListStorePointers(CxList *list); - -/** - * Returns true, if this list is storing pointers instead of the actual data. - * - * @param list - * @return true, if this list is storing pointers - * @see cxListStorePointers() - */ -cx_attr_nonnull -static inline bool cxListIsStoringPointers(const CxList *list) { - return list->collection.store_pointer; -} - -/** * Returns the number of elements currently stored in the list. * * @param list the list @@ -351,7 +356,8 @@ * * @param list the list * @param elem a pointer to the element to add - * @return zero on success, non-zero on memory allocation failure + * @retval zero success + * @retval non-zero memory allocation failure * @see cxListAddArray() */ cx_attr_nonnull @@ -359,6 +365,7 @@ CxList *list, const void *elem ) { + list->collection.sorted = false; return list->cl->insert_element(list, list->collection.size, elem); } @@ -368,9 +375,9 @@ * This method is more efficient than invoking cxListAdd() multiple times. * * If there is not enough memory to add all elements, the returned value is - * less than \p n. + * less than @p n. * - * If this list is storing pointers instead of objects \p array is expected to + * If this list is storing pointers instead of objects @p array is expected to * be an array of pointers. * * @param list the list @@ -384,19 +391,20 @@ const void *array, size_t n ) { + list->collection.sorted = false; return list->cl->insert_array(list, list->collection.size, array, n); } /** * Inserts an item at the specified index. * - * If \p index equals the list \c size, this is effectively cxListAdd(). + * If @p index equals the list @c size, this is effectively cxListAdd(). * * @param list the list * @param index the index the element shall have * @param elem a pointer to the element to add - * @return zero on success, non-zero on memory allocation failure - * or when the index is out of bounds + * @retval zero success + * @retval non-zero memory allocation failure or the index is out of bounds * @see cxListInsertAfter() * @see cxListInsertBefore() */ @@ -406,36 +414,41 @@ size_t index, const void *elem ) { + list->collection.sorted = false; return list->cl->insert_element(list, index, elem); } /** * Inserts an item into a sorted list. * + * If the list is not sorted already, the behavior is undefined. + * * @param list the list * @param elem a pointer to the element to add - * @return zero on success, non-zero on memory allocation failure + * @retval zero success + * @retval non-zero memory allocation failure */ cx_attr_nonnull static inline int cxListInsertSorted( CxList *list, const void *elem ) { + list->collection.sorted = true; // guaranteed by definition const void *data = list->collection.store_pointer ? &elem : elem; return list->cl->insert_sorted(list, data, 1) == 0; } /** * Inserts multiple items to the list at the specified index. - * If \p index equals the list size, this is effectively cxListAddArray(). + * If @p index equals the list size, this is effectively cxListAddArray(). * * This method is usually more efficient than invoking cxListInsert() * multiple times. * * If there is not enough memory to add all elements, the returned value is - * less than \p n. + * less than @p n. * - * If this list is storing pointers instead of objects \p array is expected to + * If this list is storing pointers instead of objects @p array is expected to * be an array of pointers. * * @param list the list @@ -451,6 +464,7 @@ const void *array, size_t n ) { + list->collection.sorted = false; return list->cl->insert_array(list, index, array, n); } @@ -461,11 +475,13 @@ * because consecutive chunks of sorted data are inserted in one pass. * * If there is not enough memory to add all elements, the returned value is - * less than \p n. + * less than @p n. * - * If this list is storing pointers instead of objects \p array is expected to + * If this list is storing pointers instead of objects @p array is expected to * be an array of pointers. * + * If the list is not sorted already, the behavior is undefined. + * * @param list the list * @param array a pointer to the elements to add * @param n the number of elements to add @@ -477,6 +493,7 @@ const void *array, size_t n ) { + list->collection.sorted = true; // guaranteed by definition return list->cl->insert_sorted(list, array, n); } @@ -486,12 +503,13 @@ * The used iterator remains operational, but all other active iterators should * be considered invalidated. * - * If \p iter is not a list iterator, the behavior is undefined. - * If \p iter is a past-the-end iterator, the new element gets appended to the list. + * If @p iter is not a list iterator, the behavior is undefined. + * If @p iter is a past-the-end iterator, the new element gets appended to the list. * * @param iter an iterator * @param elem the element to insert - * @return zero on success, non-zero on memory allocation failure + * @retval zero success + * @retval non-zero memory allocation failure * @see cxListInsert() * @see cxListInsertBefore() */ @@ -500,7 +518,9 @@ CxIterator *iter, const void *elem ) { - return ((struct cx_list_s *) iter->src_handle.m)->cl->insert_iter(iter, elem, 0); + CxList* list = (CxList*)iter->src_handle.m; + list->collection.sorted = false; + return list->cl->insert_iter(iter, elem, 0); } /** @@ -509,12 +529,13 @@ * The used iterator remains operational, but all other active iterators should * be considered invalidated. * - * If \p iter is not a list iterator, the behavior is undefined. - * If \p iter is a past-the-end iterator, the new element gets appended to the list. + * If @p iter is not a list iterator, the behavior is undefined. + * If @p iter is a past-the-end iterator, the new element gets appended to the list. * * @param iter an iterator * @param elem the element to insert - * @return zero on success, non-zero on memory allocation failure + * @retval zero success + * @retval non-zero memory allocation failure * @see cxListInsert() * @see cxListInsertAfter() */ @@ -523,7 +544,9 @@ CxIterator *iter, const void *elem ) { - return ((struct cx_list_s *) iter->src_handle.m)->cl->insert_iter(iter, elem, 1); + CxList* list = (CxList*)iter->src_handle.m; + list->collection.sorted = false; + return list->cl->insert_iter(iter, elem, 1); } /** @@ -534,7 +557,8 @@ * * @param list the list * @param index the index of the element - * @return zero on success, non-zero if the index is out of bounds + * @retval zero success + * @retval non-zero index out of bounds */ cx_attr_nonnull static inline int cxListRemove( @@ -548,12 +572,13 @@ * Removes and returns the element at the specified index. * * No destructor is called and instead the element is copied to the - * \p targetbuf which MUST be large enough to hold the removed element. + * @p targetbuf which MUST be large enough to hold the removed element. * * @param list the list * @param index the index of the element * @param targetbuf a buffer where to copy the element - * @return zero on success, non-zero if the index is out of bounds + * @retval zero success + * @retval non-zero index out of bounds */ cx_attr_nonnull cx_attr_access_w(3) @@ -593,7 +618,7 @@ * Removes and returns multiple element starting at the specified index. * * No destructor is called and instead the elements are copied to the - * \p targetbuf which MUST be large enough to hold all removed elements. + * @p targetbuf which MUST be large enough to hold all removed elements. * * @param list the list * @param index the index of the element @@ -615,13 +640,14 @@ /** * Removes all elements from this list. * - * If an element destructor function is specified, it is called for each + * If element destructor functions are specified, they are called for each * element before removing them. * * @param list the list */ cx_attr_nonnull static inline void cxListClear(CxList *list) { + list->collection.sorted = true; // empty lists are always sorted list->cl->clear(list); } @@ -634,7 +660,9 @@ * @param list the list * @param i the index of the first element * @param j the index of the second element - * @return zero on success, non-zero if one of the indices is out of bounds + * @retval zero success + * @retval non-zero one of the indices is out of bounds + * or the swap needed extra memory but allocation failed */ cx_attr_nonnull static inline int cxListSwap( @@ -642,6 +670,7 @@ size_t i, size_t j ) { + list->collection.sorted = false; return list->cl->swap(list, i, j); } @@ -650,7 +679,7 @@ * * @param list the list * @param index the index of the element - * @return a pointer to the element or \c NULL if the index is out of bounds + * @return a pointer to the element or @c NULL if the index is out of bounds */ cx_attr_nonnull static inline void *cxListAt( @@ -713,6 +742,7 @@ */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export CxIterator cxListMutIteratorAt( CxList *list, size_t index @@ -732,6 +762,7 @@ */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export CxIterator cxListMutBackwardsIteratorAt( CxList *list, size_t index @@ -803,18 +834,18 @@ } /** - * Returns the index of the first element that equals \p elem. + * Returns the index of the first element that equals @p elem. * * Determining equality is performed by the list's comparator function. * * @param list the list * @param elem the element to find - * @return the index of the element or a negative - * value when the element is not found + * @return the index of the element or the size of the list when the element is not found + * @see cxListIndexValid() */ cx_attr_nonnull cx_attr_nodiscard -static inline ssize_t cxListFind( +static inline size_t cxListFind( const CxList *list, const void *elem ) { @@ -822,17 +853,32 @@ } /** - * Removes and returns the index of the first element that equals \p elem. + * Checks if the specified index is within bounds. + * + * @param list the list + * @param index the index + * @retval true if the index is within bounds + * @retval false if the index is out of bounds + */ +cx_attr_nonnull +cx_attr_nodiscard +static inline bool cxListIndexValid(const CxList *list, size_t index) { + return index < list->collection.size; +} + +/** + * Removes and returns the index of the first element that equals @p elem. * * Determining equality is performed by the list's comparator function. * * @param list the list * @param elem the element to find and remove - * @return the index of the now removed element or a negative - * value when the element is not found or could not be removed + * @return the index of the now removed element or the list size + * when the element is not found or could not be removed + * @see cxListIndexValid() */ cx_attr_nonnull -static inline ssize_t cxListFindRemove( +static inline size_t cxListFindRemove( CxList *list, const void *elem ) { @@ -840,15 +886,16 @@ } /** - * Sorts the list in-place. + * Sorts the list. * - * \remark The underlying sort algorithm is implementation defined. + * @remark The underlying sort algorithm is implementation defined. * * @param list the list */ cx_attr_nonnull static inline void cxListSort(CxList *list) { list->cl->sort(list); + list->collection.sorted = true; } /** @@ -858,6 +905,8 @@ */ cx_attr_nonnull static inline void cxListReverse(CxList *list) { + // still sorted, but not according to the cmp_func + list->collection.sorted = false; list->cl->reverse(list); } @@ -869,11 +918,15 @@ * * @param list the list * @param other the list to compare to - * @return zero, if both lists are equal element wise, - * negative if the first list is smaller, positive if the first list is larger + * @retval zero both lists are equal element wise + * @retval negative the first list is smaller + * or the first non-equal element in the first list is smaller + * @retval positive the first list is larger + * or the first non-equal element in the first list is larger */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export int cxListCompare( const CxList *list, const CxList *other @@ -882,20 +935,22 @@ /** * Deallocates the memory of the specified list structure. * - * Also calls the content destructor function for each element, if specified. + * Also calls the content destructor functions for each element, if specified. * * @param list the list which shall be freed */ -static inline void cxListFree(CxList *list) { - if (list == NULL) return; - list->cl->deallocate(list); -} +cx_attr_export +void cxListFree(CxList *list); /** * A shared instance of an empty list. * * Writing to that list is not allowed. + * + * You can use this is a placeholder for initializing CxList pointers + * for which you do not want to reserve memory right from the beginning. */ +cx_attr_export extern CxList *const cxEmptyList;
--- a/ucx/cx/map.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/map.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file map.h - * \brief Interface for map implementations. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file map.h + * @brief Interface for map implementations. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_MAP_H @@ -51,6 +51,9 @@ /** Type for a map entry. */ typedef struct cx_map_entry_s CxMapEntry; +/** Type for a map iterator. */ +typedef struct cx_map_iterator_s CxMapIterator; + /** Type for map class definitions. */ typedef struct cx_map_class_s cx_map_class; @@ -65,6 +68,20 @@ }; /** + * A map entry. + */ +struct cx_map_entry_s { + /** + * A pointer to the key. + */ + const CxHashKey *key; + /** + * A pointer to the value. + */ + void *value; +}; + +/** * The type of iterator for a map. */ enum cx_map_iterator_type { @@ -83,25 +100,92 @@ }; /** + * Internal iterator struct - use CxMapIterator. + */ +struct cx_map_iterator_s { + /** + * Inherited common data for all iterators. + */ + CX_ITERATOR_BASE; + + /** + * Handle for the source map. + */ + union { + /** + * Access for mutating iterators. + */ + CxMap *m; + /** + * Access for normal iterators. + */ + const CxMap *c; + } map; + + /** + * Handle for the current element. + * + * @attention Depends on the map implementation, do not assume a type (better: do not use!). + */ + void *elem; + + /** + * Reserved memory for a map entry. + * + * If a map implementation uses an incompatible layout, the iterator needs something + * to point to during iteration which @em is compatible. + */ + CxMapEntry entry; + + /** + * Field for storing the current slot number. + * + * (Used internally) + */ + size_t slot; + + /** + * Counts the elements successfully. + * It usually does not denote a stable index within the map as it would be for arrays. + */ + size_t index; + + /** + * The size of a value stored in this map. + */ + size_t elem_size; + + /** + * May contain the total number of elements, if known. + * Set to @c SIZE_MAX when the total number is unknown during iteration. + * + * @remark The UCX implementations of #CxMap always know the number of elements they store. + */ + size_t elem_count; + + /** + * The type of this iterator. + */ + enum cx_map_iterator_type type; +}; + +/** * The class definition for arbitrary maps. */ struct cx_map_class_s { /** * Deallocates the entire memory. */ - cx_attr_nonnull void (*deallocate)(struct cx_map_s *map); /** * Removes all elements. */ - cx_attr_nonnull void (*clear)(struct cx_map_s *map); /** * Add or overwrite an element. */ - cx_attr_nonnull int (*put)( CxMap *map, CxHashKey key, @@ -111,8 +195,6 @@ /** * Returns an element. */ - cx_attr_nonnull - cx_attr_nodiscard void *(*get)( const CxMap *map, CxHashKey key @@ -121,15 +203,13 @@ /** * Removes an element. * - * Implementations SHALL check if \p targetbuf is set and copy the elements + * Implementations SHALL check if @p targetbuf is set and copy the elements * to the buffer without invoking any destructor. - * When \p targetbuf is not set, the destructors SHALL be invoked. + * When @p targetbuf is not set, the destructors SHALL be invoked. * - * The function SHALL return zero when the \p key was found and + * The function SHALL return zero when the @p key was found and * non-zero, otherwise. */ - cx_attr_nonnull_arg(1) - cx_attr_access_w(3) int (*remove)( CxMap *map, CxHashKey key, @@ -139,90 +219,36 @@ /** * Creates an iterator for this map. */ - cx_attr_nonnull - cx_attr_nodiscard - CxIterator (*iterator)(const CxMap *map, enum cx_map_iterator_type type); -}; - -/** - * A map entry. - */ -struct cx_map_entry_s { - /** - * A pointer to the key. - */ - const CxHashKey *key; - /** - * A pointer to the value. - */ - void *value; + CxMapIterator (*iterator)(const CxMap *map, enum cx_map_iterator_type type); }; /** * A shared instance of an empty map. * * Writing to that map is not allowed. - */ -extern CxMap *const cxEmptyMap; - -/** - * Advises the map to store copies of the objects (default mode of operation). * - * Retrieving objects from this map will yield pointers to the copies stored - * within this list. - * - * @param map the map - * @see cxMapStorePointers() + * You can use this is a placeholder for initializing CxMap pointers + * for which you do not want to reserve memory right from the beginning. */ -cx_attr_nonnull -static inline void cxMapStoreObjects(CxMap *map) { - map->collection.store_pointer = false; -} - -/** - * Advises the map to only store pointers to the objects. - * - * Retrieving objects from this list will yield the original pointers stored. - * - * @note This function forcibly sets the element size to the size of a pointer. - * Invoking this function on a non-empty map that already stores copies of - * objects is undefined. - * - * @param map the map - * @see cxMapStoreObjects() - */ -cx_attr_nonnull -static inline void cxMapStorePointers(CxMap *map) { - map->collection.store_pointer = true; - map->collection.elem_size = sizeof(void *); -} - -/** - * Returns true, if this map is storing pointers instead of the actual data. - * - * @param map - * @return true, if this map is storing pointers - * @see cxMapStorePointers() - */ -cx_attr_nonnull -static inline bool cxMapIsStoringPointers(const CxMap *map) { - return map->collection.store_pointer; -} +cx_attr_export +extern CxMap *const cxEmptyMap; /** * Deallocates the memory of the specified map. * + * Also calls the content destructor functions for each element, if specified. + * * @param map the map to be freed */ -static inline void cxMapFree(CxMap *map) { - if (map == NULL) return; - map->cl->deallocate(map); -} +cx_attr_export +void cxMapFree(CxMap *map); /** * Clears a map by removing all elements. * + * Also calls the content destructor functions for each element, if specified. + * * @param map the map to be cleared */ cx_attr_nonnull @@ -241,13 +267,14 @@ return map->collection.size; } - -// TODO: set-like map operations (union, intersect, difference) - /** * Creates a value iterator for a map. * - * \note An iterator iterates over all elements successively. Therefore, the order + * When the map is storing pointers, those pointers are returned. + * Otherwise, the iterator iterates over pointers to the memory within the map where the + * respective elements are stored. + * + * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * * @param map the map to create the iterator for @@ -255,16 +282,17 @@ */ cx_attr_nonnull cx_attr_nodiscard -static inline CxIterator cxMapIteratorValues(const CxMap *map) { +static inline CxMapIterator cxMapIteratorValues(const CxMap *map) { return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); } /** * Creates a key iterator for a map. * - * The elements of the iterator are keys of type CxHashKey. + * The elements of the iterator are keys of type CxHashKey and the pointer returned + * during iterator shall be treated as @c const @c CxHashKey* . * - * \note An iterator iterates over all elements successively. Therefore, the order + * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * * @param map the map to create the iterator for @@ -272,16 +300,17 @@ */ cx_attr_nonnull cx_attr_nodiscard -static inline CxIterator cxMapIteratorKeys(const CxMap *map) { +static inline CxMapIterator cxMapIteratorKeys(const CxMap *map) { return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); } /** * Creates an iterator for a map. * - * The elements of the iterator are key/value pairs of type CxMapEntry. + * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned + * during iterator shall be treated as @c const @c CxMapEntry* . * - * \note An iterator iterates over all elements successively. Therefore, the order + * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * * @param map the map to create the iterator for @@ -291,7 +320,7 @@ */ cx_attr_nonnull cx_attr_nodiscard -static inline CxIterator cxMapIterator(const CxMap *map) { +static inline CxMapIterator cxMapIterator(const CxMap *map) { return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); } @@ -299,7 +328,11 @@ /** * Creates a mutating iterator over the values of a map. * - * \note An iterator iterates over all elements successively. Therefore, the order + * When the map is storing pointers, those pointers are returned. + * Otherwise, the iterator iterates over pointers to the memory within the map where the + * respective elements are stored. + * + * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * * @param map the map to create the iterator for @@ -307,14 +340,16 @@ */ cx_attr_nonnull cx_attr_nodiscard -CxIterator cxMapMutIteratorValues(CxMap *map); +cx_attr_export +CxMapIterator cxMapMutIteratorValues(CxMap *map); /** * Creates a mutating iterator over the keys of a map. * - * The elements of the iterator are keys of type CxHashKey. + * The elements of the iterator are keys of type CxHashKey and the pointer returned + * during iterator shall be treated as @c const @c CxHashKey* . * - * \note An iterator iterates over all elements successively. Therefore, the order + * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * * @param map the map to create the iterator for @@ -322,14 +357,16 @@ */ cx_attr_nonnull cx_attr_nodiscard -CxIterator cxMapMutIteratorKeys(CxMap *map); +cx_attr_export +CxMapIterator cxMapMutIteratorKeys(CxMap *map); /** * Creates a mutating iterator for a map. * - * The elements of the iterator are key/value pairs of type CxMapEntry. + * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned + * during iterator shall be treated as @c const @c CxMapEntry* . * - * \note An iterator iterates over all elements successively. Therefore, the order + * @note An iterator iterates over all elements successively. Therefore, the order * highly depends on the map implementation and may change arbitrarily when the contents change. * * @param map the map to create the iterator for @@ -339,19 +376,11 @@ */ cx_attr_nonnull cx_attr_nodiscard -CxIterator cxMapMutIterator(CxMap *map); +cx_attr_export +CxMapIterator cxMapMutIterator(CxMap *map); #ifdef __cplusplus } // end the extern "C" block here, because we want to start overloading - -/** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - */ cx_attr_nonnull static inline int cxMapPut( CxMap *map, @@ -361,15 +390,6 @@ return map->cl->put(map, key, value); } - -/** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - */ cx_attr_nonnull static inline int cxMapPut( CxMap *map, @@ -379,14 +399,6 @@ return map->cl->put(map, cx_hash_key_cxstr(key), value); } -/** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - */ cx_attr_nonnull static inline int cxMapPut( CxMap *map, @@ -396,14 +408,6 @@ return map->cl->put(map, cx_hash_key_cxstr(key), value); } -/** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure - */ cx_attr_nonnull cx_attr_cstr_arg(2) static inline int cxMapPut( @@ -414,13 +418,6 @@ return map->cl->put(map, cx_hash_key_str(key), value); } -/** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value - */ cx_attr_nonnull cx_attr_nodiscard static inline void *cxMapGet( @@ -430,13 +427,6 @@ return map->cl->get(map, key); } -/** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value - */ cx_attr_nonnull cx_attr_nodiscard static inline void *cxMapGet( @@ -446,13 +436,6 @@ return map->cl->get(map, cx_hash_key_cxstr(key)); } -/** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value - */ cx_attr_nonnull cx_attr_nodiscard static inline void *cxMapGet( @@ -462,13 +445,6 @@ return map->cl->get(map, cx_hash_key_cxstr(key)); } -/** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value - */ cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(2) @@ -479,17 +455,6 @@ return map->cl->get(map, cx_hash_key_str(key)); } -/** - * Removes a key/value-pair from the map by using the key. - * - * Always invokes the destructors functions, if any, on the removed element. - * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found - * - * @see cxMapRemoveAndGet() - */ cx_attr_nonnull static inline int cxMapRemove( CxMap *map, @@ -498,17 +463,6 @@ return map->cl->remove(map, key, nullptr); } -/** - * Removes a key/value-pair from the map by using the key. - * - * Always invokes the destructors functions, if any, on the removed element. - * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found - * - * @see cxMapRemoveAndGet() - */ cx_attr_nonnull static inline int cxMapRemove( CxMap *map, @@ -517,17 +471,6 @@ return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr); } -/** - * Removes a key/value-pair from the map by using the key. - * - * Always invokes the destructors functions, if any, on the removed element. - * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found - * - * @see cxMapRemoveAndGet() - */ cx_attr_nonnull static inline int cxMapRemove( CxMap *map, @@ -536,17 +479,6 @@ return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr); } -/** - * Removes a key/value-pair from the map by using the key. - * - * Always invokes the destructors functions, if any, on the removed element. - * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found - * - * @see cxMapRemoveAndGet() - */ cx_attr_nonnull cx_attr_cstr_arg(2) static inline int cxMapRemove( @@ -556,24 +488,6 @@ return map->cl->remove(map, cx_hash_key_str(key), nullptr); } -/** - * Removes a key/value-pair from the map by using the key. - * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. - * - * If this map is storing pointers, the element is the pointer itself - * and not the object it points to. - * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found - * - * @see cxMapStorePointers() - * @see cxMapRemove() - */ cx_attr_nonnull cx_attr_access_w(3) static inline int cxMapRemoveAndGet( @@ -584,24 +498,6 @@ return map->cl->remove(map, key, targetbuf); } -/** - * Removes a key/value-pair from the map by using the key. - * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. - * - * If this map is storing pointers, the element is the pointer itself - * and not the object it points to. - * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found - * - * @see cxMapStorePointers() - * @see cxMapRemove() - */ cx_attr_nonnull cx_attr_access_w(3) static inline int cxMapRemoveAndGet( @@ -612,24 +508,6 @@ return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf); } -/** - * Removes a key/value-pair from the map by using the key. - * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. - * - * If this map is storing pointers, the element is the pointer itself - * and not the object it points to. - * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found - * - * @see cxMapStorePointers() - * @see cxMapRemove() - */ cx_attr_nonnull cx_attr_access_w(3) static inline int cxMapRemoveAndGet( @@ -640,24 +518,6 @@ return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf); } -/** - * Removes a key/value-pair from the map by using the key. - * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. - * - * If this map is storing pointers, the element is the pointer itself - * and not the object it points to. - * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found - * - * @see cxMapStorePointers() - * @see cxMapRemove() - */ cx_attr_nonnull cx_attr_access_w(3) cx_attr_cstr_arg(2) @@ -672,12 +532,7 @@ #else // __cplusplus /** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure + * @copydoc cxMapPut() */ cx_attr_nonnull static inline int cx_map_put( @@ -689,12 +544,7 @@ } /** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure + * @copydoc cxMapPut() */ cx_attr_nonnull static inline int cx_map_put_cxstr( @@ -706,12 +556,7 @@ } /** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure + * @copydoc cxMapPut() */ cx_attr_nonnull static inline int cx_map_put_mustr( @@ -723,12 +568,7 @@ } /** - * Puts a key/value-pair into the map. - * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure + * @copydoc cxMapPut() */ cx_attr_nonnull cx_attr_cstr_arg(2) @@ -743,10 +583,21 @@ /** * Puts a key/value-pair into the map. * - * @param map the map - * @param key the key - * @param value the value - * @return 0 on success, non-zero value on failure + * A possible existing value will be overwritten. + * If destructor functions are specified, they are called for + * the overwritten element. + * + * If this map is storing pointers, the @p value pointer is written + * to the map. Otherwise, the memory is copied from @p value with + * memcpy(). + * + * The @p key is always copied. + * + * @param map (@c CxMap*) the map + * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @param value (@c void*) the value + * @retval zero success + * @retval non-zero value on memory allocation failure */ #define cxMapPut(map, key, value) _Generic((key), \ CxHashKey: cx_map_put, \ @@ -757,11 +608,7 @@ (map, key, value) /** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value + * @copydoc cxMapGet() */ cx_attr_nonnull cx_attr_nodiscard @@ -773,11 +620,7 @@ } /** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value + * @copydoc cxMapGet() */ cx_attr_nonnull cx_attr_nodiscard @@ -789,11 +632,7 @@ } /** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value + * @copydoc cxMapGet() */ cx_attr_nonnull cx_attr_nodiscard @@ -805,11 +644,7 @@ } /** - * Retrieves a value by using a key. - * - * @param map the map - * @param key the key - * @return the value + * @copydoc cxMapGet() */ cx_attr_nonnull cx_attr_nodiscard @@ -824,9 +659,13 @@ /** * Retrieves a value by using a key. * - * @param map the map - * @param key the key - * @return the value + * If this map is storing pointers, the stored pointer is returned. + * Otherwise, a pointer to the element within the map's memory + * is returned (which is valid as long as the element stays in the map). + * + * @param map (@c CxMap*) the map + * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @return (@c void*) the value */ #define cxMapGet(map, key) _Generic((key), \ CxHashKey: cx_map_get, \ @@ -837,13 +676,7 @@ (map, key) /** - * Removes a key/value-pair from the map by using the key. - * - * Always invokes the destructors functions, if any, on the removed element. - * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found + * @copydoc cxMapRemove() */ cx_attr_nonnull static inline int cx_map_remove( @@ -854,13 +687,7 @@ } /** - * Removes a key/value-pair from the map by using the key. - * - * Always invokes the destructors functions, if any, on the removed element. - * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found + * @copydoc cxMapRemove() */ cx_attr_nonnull static inline int cx_map_remove_cxstr( @@ -871,13 +698,7 @@ } /** - * Removes a key/value-pair from the map by using the key. - * - * Always invokes the destructors functions, if any, on the removed element. - * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found + * @copydoc cxMapRemove() */ cx_attr_nonnull static inline int cx_map_remove_mustr( @@ -888,13 +709,7 @@ } /** - * Removes a key/value-pair from the map by using the key. - * - * Always invokes the destructors functions, if any, on the removed element. - * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found + * @copydoc cxMapRemove() */ cx_attr_nonnull cx_attr_cstr_arg(2) @@ -910,9 +725,10 @@ * * Always invokes the destructors functions, if any, on the removed element. * - * @param map the map - * @param key the key - * @return zero on success, non-zero if the key was not found + * @param map (@c CxMap*) the map + * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @retval zero success + * @retval non-zero the key was not found * * @see cxMapRemoveAndGet() */ @@ -925,19 +741,7 @@ (map, key) /** - * Removes a key/value-pair from the map by using the key. - * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. - * - * If this map is storing pointers, the element is the pointer itself - * and not the object it points to. - * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found + * @copydoc cxMapRemoveAndGet() */ cx_attr_nonnull cx_attr_access_w(3) @@ -950,19 +754,7 @@ } /** - * Removes a key/value-pair from the map by using the key. - * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. - * - * If this map is storing pointers, the element is the pointer itself - * and not the object it points to. - * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found + * @copydoc cxMapRemoveAndGet() */ cx_attr_nonnull cx_attr_access_w(3) @@ -975,19 +767,7 @@ } /** - * Removes a key/value-pair from the map by using the key. - * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. - * - * If this map is storing pointers, the element is the pointer itself - * and not the object it points to. - * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found + * @copydoc cxMapRemoveAndGet() */ cx_attr_nonnull cx_attr_access_w(3) @@ -1000,19 +780,7 @@ } /** - * Removes a key/value-pair from the map by using the key. - * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. - * - * If this map is storing pointers, the element is the pointer itself - * and not the object it points to. - * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found + * @copydoc cxMapRemoveAndGet() */ cx_attr_nonnull cx_attr_access_w(3) @@ -1028,19 +796,20 @@ /** * Removes a key/value-pair from the map by using the key. * - * This function will copy the contents to the target buffer - * which must be guaranteed to be large enough to hold the element. - * The destructor functions, if any, will \em not be called. + * This function will copy the contents of the removed element + * to the target buffer, which must be guaranteed to be large enough + * to hold the element (the map's element size). + * The destructor functions, if any, will @em not be called. * * If this map is storing pointers, the element is the pointer itself * and not the object it points to. * - * @param map the map - * @param key the key - * @param targetbuf the buffer where the element shall be copied to - * @return zero on success, non-zero if the key was not found - * - * @see cxMapStorePointers() + * @param map (@c CxMap*) the map + * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key + * @param targetbuf (@c void*) the buffer where the element shall be copied to + * @retval zero success + * @retval non-zero the key was not found + * * @see cxMapRemove() */ #define cxMapRemoveAndGet(map, key, targetbuf) _Generic((key), \
--- a/ucx/cx/mempool.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/mempool.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file mempool.h - * \brief Interface for memory pool implementations. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file mempool.h + * @brief Interface for memory pool implementations. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_MEMPOOL_H @@ -80,6 +80,7 @@ * * @param pool the memory pool to free */ +cx_attr_export void cxMempoolFree(CxMempool *pool); /** @@ -89,20 +90,21 @@ * * @param capacity the initial capacity of the pool * @param destr optional destructor function to use for allocated memory - * @return the created memory pool or \c NULL if allocation failed + * @return the created memory pool or @c NULL if allocation failed */ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMempoolFree, 1) +cx_attr_export CxMempool *cxMempoolCreate(size_t capacity, cx_destructor_func destr); /** * Creates a basic array-based memory pool. * - * @param capacity the initial capacity of the pool - * @return the created memory pool or \c NULL if allocation failed + * @param capacity (@c size_t) the initial capacity of the pool + * @return (@c CxMempool*) the created memory pool or @c NULL if allocation failed */ -#define cxBasicMempoolCreate(capacity) cxMempoolCreate(capacity, NULL) +#define cxMempoolCreateSimple(capacity) cxMempoolCreate(capacity, NULL) /** * Sets the destructor function for a specific allocated memory object. @@ -114,6 +116,7 @@ * @param fnc the destructor function */ cx_attr_nonnull +cx_attr_export void cxMempoolSetDestructor( void *memory, cx_destructor_func fnc @@ -128,6 +131,7 @@ * @param memory the object allocated in the pool */ cx_attr_nonnull +cx_attr_export void cxMempoolRemoveDestructor(void *memory); /** @@ -141,9 +145,11 @@ * @param pool the pool * @param memory the object to register (MUST NOT be already allocated in the pool) * @param destr the destructor function - * @return zero on success, non-zero on failure + * @retval zero success + * @retval non-zero failure */ cx_attr_nonnull +cx_attr_export int cxMempoolRegister( CxMempool *pool, void *memory,
--- a/ucx/cx/printf.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/printf.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file printf.h - * \brief Wrapper for write functions with a printf-like interface. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file printf.h + * @brief Wrapper for write functions with a printf-like interface. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_PRINTF_H @@ -56,21 +56,23 @@ /** * The maximum string length that fits into stack memory. */ +cx_attr_export extern const unsigned cx_printf_sbo_size; /** - * A \c fprintf like function which writes the output to a stream by + * A @c fprintf like function which writes the output to a stream by * using a write_func. * * @param stream the stream the data is written to * @param wfc the write function * @param fmt format string * @param ... additional arguments - * @return the total number of bytes written + * @return the total number of bytes written or an error code from stdlib printf implementation */ cx_attr_nonnull_arg(1, 2, 3) cx_attr_printf(3, 4) cx_attr_cstr_arg(3) +cx_attr_export int cx_fprintf( void *stream, cx_write_func wfc, @@ -79,18 +81,19 @@ ); /** - * A \c vfprintf like function which writes the output to a stream by + * A @c vfprintf like function which writes the output to a stream by * using a write_func. * * @param stream the stream the data is written to * @param wfc the write function * @param fmt format string * @param ap argument list - * @return the total number of bytes written + * @return the total number of bytes written or an error code from stdlib printf implementation * @see cx_fprintf() */ cx_attr_nonnull cx_attr_cstr_arg(3) +cx_attr_export int cx_vfprintf( void *stream, cx_write_func wfc, @@ -99,10 +102,12 @@ ); /** - * A \c asprintf like function which allocates space for a string + * A @c asprintf like function which allocates space for a string * the result is written to. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string is guaranteed to be zero-terminated, + * unless there was an error, in which case the string's pointer + * will be @c NULL. * * @param allocator the CxAllocator used for allocating the string * @param fmt format string @@ -113,6 +118,7 @@ cx_attr_nonnull_arg(1, 2) cx_attr_printf(2, 3) cx_attr_cstr_arg(2) +cx_attr_export cxmutstr cx_asprintf_a( const CxAllocator *allocator, const char *fmt, @@ -120,24 +126,28 @@ ); /** - * A \c asprintf like function which allocates space for a string + * A @c asprintf like function which allocates space for a string * the result is written to. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string is guaranteed to be zero-terminated, + * unless there was an error, in which case the string's pointer + * will be @c NULL. * - * @param fmt format string + * @param fmt (@c char*) format string * @param ... additional arguments - * @return the formatted string + * @return (@c cxmutstr) the formatted string * @see cx_strfree() */ #define cx_asprintf(fmt, ...) \ cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__) /** -* A \c vasprintf like function which allocates space for a string +* A @c vasprintf like function which allocates space for a string * the result is written to. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string is guaranteed to be zero-terminated, + * unless there was an error, in which case the string's pointer + * will be @c NULL. * * @param allocator the CxAllocator used for allocating the string * @param fmt format string @@ -147,6 +157,7 @@ */ cx_attr_nonnull cx_attr_cstr_arg(2) +cx_attr_export cxmutstr cx_vasprintf_a( const CxAllocator *allocator, const char *fmt, @@ -154,65 +165,69 @@ ); /** -* A \c vasprintf like function which allocates space for a string +* A @c vasprintf like function which allocates space for a string * the result is written to. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string is guaranteed to be zero-terminated, + * unless there was in error, in which case the string's pointer + * will be @c NULL. * - * @param fmt format string - * @param ap argument list - * @return the formatted string + * @param fmt (@c char*) format string + * @param ap (@c va_list) argument list + * @return (@c cxmutstr) the formatted string * @see cx_asprintf() */ #define cx_vasprintf(fmt, ap) cx_vasprintf_a(cxDefaultAllocator, fmt, ap) /** - * A \c printf like function which writes the output to a CxBuffer. + * A @c printf like function which writes the output to a CxBuffer. * - * @param buffer a pointer to the buffer the data is written to - * @param fmt the format string + * @param buffer (@c CxBuffer*) a pointer to the buffer the data is written to + * @param fmt (@c char*) the format string * @param ... additional arguments - * @return the total number of bytes written - * @see ucx_fprintf() + * @return (@c int) the total number of bytes written or an error code from stdlib printf implementation + * @see cx_fprintf() + * @see cxBufferWrite() */ -#define cx_bprintf(buffer, fmt, ...) cx_fprintf((CxBuffer*)buffer, \ - (cx_write_func) cxBufferWrite, fmt, __VA_ARGS__) +#define cx_bprintf(buffer, fmt, ...) cx_fprintf((void*)buffer, \ + cxBufferWriteFunc, fmt, __VA_ARGS__) /** - * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * An @c sprintf like function which reallocates the string when the buffer is not large enough. * - * The size of the buffer will be updated in \p len when necessary. + * The size of the buffer will be updated in @p len when necessary. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string, if successful, is guaranteed to be zero-terminated. * - * @param str a pointer to the string buffer - * @param len a pointer to the length of the buffer - * @param fmt the format string + * @param str (@c char**) a pointer to the string buffer + * @param len (@c size_t*) a pointer to the length of the buffer + * @param fmt (@c char*) the format string * @param ... additional arguments - * @return the length of produced string + * @return (@c int) the length of produced string or an error code from stdlib printf implementation */ #define cx_sprintf(str, len, fmt, ...) cx_sprintf_a(cxDefaultAllocator, str, len, fmt, __VA_ARGS__) /** - * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * An @c sprintf like function which reallocates the string when the buffer is not large enough. * - * The size of the buffer will be updated in \p len when necessary. + * The size of the buffer will be updated in @p len when necessary. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string, if successful, is guaranteed to be zero-terminated. * - * \attention The original buffer MUST have been allocated with the same allocator! + * @attention The original buffer MUST have been allocated with the same allocator! * * @param alloc the allocator to use * @param str a pointer to the string buffer * @param len a pointer to the length of the buffer * @param fmt the format string * @param ... additional arguments - * @return the length of produced string + * @return the length of produced string or an error code from stdlib printf implementation */ cx_attr_nonnull_arg(1, 2, 3, 4) cx_attr_printf(4, 5) cx_attr_cstr_arg(4) +cx_attr_export int cx_sprintf_a( CxAllocator *alloc, char **str, @@ -223,38 +238,41 @@ /** - * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * An @c sprintf like function which reallocates the string when the buffer is not large enough. * - * The size of the buffer will be updated in \p len when necessary. + * The size of the buffer will be updated in @p len when necessary. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string, if successful, is guaranteed to be zero-terminated. * - * @param str a pointer to the string buffer - * @param len a pointer to the length of the buffer - * @param fmt the format string - * @param ap argument list - * @return the length of produced string + * @param str (@c char**) a pointer to the string buffer + * @param len (@c size_t*) a pointer to the length of the buffer + * @param fmt (@c char*) the format string + * @param ap (@c va_list) argument list + * @return (@c int) the length of produced string or an error code from stdlib printf implementation */ #define cx_vsprintf(str, len, fmt, ap) cx_vsprintf_a(cxDefaultAllocator, str, len, fmt, ap) /** - * An \c sprintf like function which reallocates the string when the buffer is not large enough. + * An @c sprintf like function which reallocates the string when the buffer is not large enough. * - * The size of the buffer will be updated in \p len when necessary. + * The size of the buffer will be updated in @p len when necessary. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string is guaranteed to be zero-terminated. * - * \attention The original buffer MUST have been allocated with the same allocator! + * @attention The original buffer MUST have been allocated with the same allocator! * * @param alloc the allocator to use * @param str a pointer to the string buffer * @param len a pointer to the length of the buffer * @param fmt the format string * @param ap argument list - * @return the length of produced string + * @return the length of produced string or an error code from stdlib printf implementation */ cx_attr_nonnull cx_attr_cstr_arg(4) +cx_attr_access_rw(2) +cx_attr_access_rw(3) +cx_attr_export int cx_vsprintf_a( CxAllocator *alloc, char **str, @@ -265,52 +283,55 @@ /** - * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * An @c sprintf like function which allocates a new string when the buffer is not large enough. * - * The size of the buffer will be updated in \p len when necessary. + * The size of the buffer will be updated in @p len when necessary. * - * The location of the resulting string will \em always be stored to \p str. When the buffer - * was sufficiently large, \p buf itself will be stored to the location of \p str. + * The location of the resulting string will @em always be stored to @p str. When the buffer + * was sufficiently large, @p buf itself will be stored to the location of @p str. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string, if successful, is guaranteed to be zero-terminated. * - * \remark When a new string needed to be allocated, the contents of \p buf will be - * poisoned after the call, because this function tries to produce the string in \p buf, first. + * @remark When a new string needed to be allocated, the contents of @p buf will be + * poisoned after the call, because this function tries to produce the string in @p buf, first. * - * @param buf a pointer to the buffer - * @param len a pointer to the length of the buffer - * @param str a pointer to the location - * @param fmt the format string + * @param buf (@c char*) a pointer to the buffer + * @param len (@c size_t*) a pointer to the length of the buffer + * @param str (@c char**) a pointer where the location of the result shall be stored + * @param fmt (@c char*) the format string * @param ... additional arguments - * @return the length of produced string + * @return (@c int) the length of produced string or an error code from stdlib printf implementation */ #define cx_sprintf_s(buf, len, str, fmt, ...) cx_sprintf_sa(cxDefaultAllocator, buf, len, str, fmt, __VA_ARGS__) /** - * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * An @c sprintf like function which allocates a new string when the buffer is not large enough. * - * The size of the buffer will be updated in \p len when necessary. + * The size of the buffer will be updated in @p len when necessary. * - * The location of the resulting string will \em always be stored to \p str. When the buffer - * was sufficiently large, \p buf itself will be stored to the location of \p str. + * The location of the resulting string will @em always be stored to @p str. When the buffer + * was sufficiently large, @p buf itself will be stored to the location of @p str. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string, if successful, is guaranteed to be zero-terminated. * - * \remark When a new string needed to be allocated, the contents of \p buf will be - * poisoned after the call, because this function tries to produce the string in \p buf, first. + * @remark When a new string needed to be allocated, the contents of @p buf will be + * poisoned after the call, because this function tries to produce the string in @p buf, first. * * @param alloc the allocator to use * @param buf a pointer to the buffer * @param len a pointer to the length of the buffer - * @param str a pointer to the location + * @param str a pointer where the location of the result shall be stored * @param fmt the format string * @param ... additional arguments - * @return the length of produced string + * @return the length of produced string or an error code from stdlib printf implementation */ -__attribute__((__nonnull__(1, 2, 4, 5), __format__(printf, 5, 6))) cx_attr_nonnull_arg(1, 2, 4, 5) cx_attr_printf(5, 6) cx_attr_cstr_arg(5) +cx_attr_access_rw(2) +cx_attr_access_rw(3) +cx_attr_access_rw(4) +cx_attr_export int cx_sprintf_sa( CxAllocator *alloc, char *buf, @@ -321,50 +342,51 @@ ); /** - * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * An @c sprintf like function which allocates a new string when the buffer is not large enough. * - * The size of the buffer will be updated in \p len when necessary. + * The size of the buffer will be updated in @p len when necessary. * - * The location of the resulting string will \em always be stored to \p str. When the buffer - * was sufficiently large, \p buf itself will be stored to the location of \p str. + * The location of the resulting string will @em always be stored to @p str. When the buffer + * was sufficiently large, @p buf itself will be stored to the location of @p str. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string is guaranteed to be zero-terminated. * - * \remark When a new string needed to be allocated, the contents of \p buf will be - * poisoned after the call, because this function tries to produce the string in \p buf, first. + * @remark When a new string needed to be allocated, the contents of @p buf will be + * poisoned after the call, because this function tries to produce the string in @p buf, first. * - * @param buf a pointer to the buffer - * @param len a pointer to the length of the buffer - * @param str a pointer to the location - * @param fmt the format string - * @param ap argument list - * @return the length of produced string + * @param buf (@c char*) a pointer to the buffer + * @param len (@c size_t*) a pointer to the length of the buffer + * @param str (@c char**) a pointer where the location of the result shall be stored + * @param fmt (@c char*) the format string + * @param ap (@c va_list) argument list + * @return (@c int) the length of produced string or an error code from stdlib printf implementation */ #define cx_vsprintf_s(buf, len, str, fmt, ap) cx_vsprintf_sa(cxDefaultAllocator, buf, len, str, fmt, ap) /** - * An \c sprintf like function which allocates a new string when the buffer is not large enough. + * An @c sprintf like function which allocates a new string when the buffer is not large enough. * - * The size of the buffer will be updated in \p len when necessary. + * The size of the buffer will be updated in @p len when necessary. * - * The location of the resulting string will \em always be stored to \p str. When the buffer - * was sufficiently large, \p buf itself will be stored to the location of \p str. + * The location of the resulting string will @em always be stored to @p str. When the buffer + * was sufficiently large, @p buf itself will be stored to the location of @p str. * - * \note The resulting string is guaranteed to be zero-terminated. + * @note The resulting string is guaranteed to be zero-terminated. * - * \remark When a new string needed to be allocated, the contents of \p buf will be - * poisoned after the call, because this function tries to produce the string in \p buf, first. + * @remark When a new string needed to be allocated, the contents of @p buf will be + * poisoned after the call, because this function tries to produce the string in @p buf, first. * * @param alloc the allocator to use * @param buf a pointer to the buffer * @param len a pointer to the length of the buffer - * @param str a pointer to the location + * @param str a pointer where the location of the result shall be stored * @param fmt the format string * @param ap argument list - * @return the length of produced string + * @return the length of produced string or an error code from stdlib printf implementation */ cx_attr_nonnull cx_attr_cstr_arg(5) +cx_attr_export int cx_vsprintf_sa( CxAllocator *alloc, char *buf,
--- a/ucx/cx/properties.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/properties.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file properties.h - * \brief Interface for parsing data from properties files. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file properties.h + * @brief Interface for parsing data from properties files. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_PROPERTIES_H @@ -59,12 +59,6 @@ char delimiter; /** - * The character, when appearing at the end of a line, continues that line. - * This is '\' by default. - */ - // char continuation; // TODO: line continuation in properties - - /** * The first comment character. * This is '#' by default. */ @@ -81,6 +75,15 @@ * This is not set by default. */ char comment3; + + /* + * The character, when appearing at the end of a line, continues that line. + * This is '\' by default. + */ + /** + * Reserved for future use. + */ + char continuation; }; /** @@ -91,6 +94,7 @@ /** * Default properties configuration. */ +cx_attr_export extern const CxPropertiesConfig cx_properties_config_default; /** @@ -116,13 +120,13 @@ * Not used as a status and never returned by any function. * * You can use this enumerator to check for all "good" status results - * by checking if the status is less than \c CX_PROPERTIES_OK. + * by checking if the status is less than @c CX_PROPERTIES_OK. * * A "good" status means, that you can refill data and continue parsing. */ CX_PROPERTIES_OK, /** - * Input buffer is \c NULL. + * Input buffer is @c NULL. */ CX_PROPERTIES_NULL_INPUT, /** @@ -203,7 +207,8 @@ * @param sink the sink * @param key the key * @param value the value - * @return zero on success, non-zero when sinking the k/v-pair failed + * @retval zero success + * @retval non-zero sinking the k/v-pair failed */ cx_attr_nonnull typedef int(*cx_properties_sink_func)( @@ -241,7 +246,7 @@ * A function that reads data from a source. * * When the source is depleted, implementations SHALL provide an empty - * string in the \p target and return zero. + * string in the @p target and return zero. * A non-zero return value is only permitted in case of an error. * * The meaning of the optional parameters is implementation-dependent. @@ -249,7 +254,8 @@ * @param prop the properties interface that wants to read from the source * @param src the source * @param target a string buffer where the read data shall be stored - * @return zero on success, non-zero when reading data failed + * @retval zero success + * @retval non-zero reading the data failed */ cx_attr_nonnull typedef int(*cx_properties_read_func)( @@ -263,7 +269,8 @@ * * @param prop the properties interface that wants to read from the source * @param src the source - * @return zero when initialization was successful, non-zero otherwise + * @retval zero initialization was successful + * @retval non-zero otherwise */ cx_attr_nonnull typedef int(*cx_properties_read_init_func)( @@ -324,12 +331,13 @@ * @see cxPropertiesInitDefault() */ cx_attr_nonnull +cx_attr_export void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config); /** * Destroys the properties interface. * - * \note Even when you are certain that you did not use the interface in a + * @note Even when you are certain that you did not use the interface in a * way that caused a memory allocation, you should call this function anyway. * Future versions of the library might add features that need additional memory * and you really don't want to search the entire code where you might need @@ -338,6 +346,7 @@ * @param prop the properties interface */ cx_attr_nonnull +cx_attr_export void cxPropertiesDestroy(CxProperties *prop); /** @@ -358,7 +367,7 @@ /** * Initialize a properties parser with the default configuration. * - * @param prop the properties interface + * @param prop (@c CxProperties*) the properties interface * @see cxPropertiesInit() */ #define cxPropertiesInitDefault(prop) \ @@ -381,11 +390,13 @@ * @param prop the properties interface * @param buf a pointer to the data * @param len the length of the data - * @return non-zero when a memory allocation was necessary but failed + * @retval zero success + * @retval non-zero a memory allocation was necessary but failed * @see cxPropertiesFill() */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export int cxPropertiesFilln( CxProperties *prop, const char *buf, @@ -437,7 +448,8 @@ * * @param prop the properties interface * @param str the text to fill in - * @return non-zero when a memory allocation was necessary but failed + * @retval zero success + * @retval non-zero a memory allocation was necessary but failed * @see cxPropertiesFilln() */ #define cxPropertiesFill(prop, str) _Generic((str), \ @@ -490,6 +502,7 @@ * @param capacity the capacity of the stack memory */ cx_attr_nonnull +cx_attr_export void cxPropertiesUseStack( CxProperties *prop, char *buf, @@ -505,23 +518,30 @@ * When an incomplete line is encountered, #CX_PROPERTIES_INCOMPLETE_DATA is * returned, and you can add more data with #cxPropertiesFill(). * - * \remark The incomplete line will be stored in an internal buffer, which is + * @remark The incomplete line will be stored in an internal buffer, which is * allocated on the heap, by default. If you want to avoid allocations, * you can specify sufficient space with cxPropertiesUseStack() after * initialization with cxPropertiesInit(). * - * \attention The returned strings will point into a buffer that might not be + * @attention The returned strings will point into a buffer that might not be * available later. It is strongly recommended to copy the strings for further * use. * * @param prop the properties interface * @param key a pointer to the cxstring that shall contain the property name * @param value a pointer to the cxstring that shall contain the property value - * @return the status code as defined above - * @see cxPropertiesFill() + * @retval CX_PROPERTIES_NO_ERROR (zero) a key/value pair was found + * @retval CX_PROPERTIES_NO_DATA there is no (more) data in the input buffer + * @retval CX_PROPERTIES_INCOMPLETE_DATA the data in the input buffer is incomplete + * (fill more data and try again) + * @retval CX_PROPERTIES_NULL_INPUT the input buffer was never filled + * @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key + * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter + * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export CxPropertiesStatus cxPropertiesNext( CxProperties *prop, cxstring *key, @@ -534,7 +554,7 @@ * The values stored in the map will be pointers to strings allocated * by #cx_strdup_a(). * The default stdlib allocator will be used, unless you specify a custom - * allocator in the optional \c data of the sink. + * allocator in the optional @c data of the sink. * * @param map the map that shall consume the k/v-pairs. * @return the sink @@ -542,6 +562,7 @@ */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export CxPropertiesSink cxPropertiesMapSink(CxMap *map); /** @@ -552,6 +573,7 @@ * @see cxPropertiesLoad() */ cx_attr_nodiscard +cx_attr_export CxPropertiesSource cxPropertiesStringSource(cxstring str); /** @@ -565,6 +587,7 @@ cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1, 2) +cx_attr_export CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len); /** @@ -580,6 +603,7 @@ cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) +cx_attr_export CxPropertiesSource cxPropertiesCstrSource(const char *str); /** @@ -594,6 +618,7 @@ cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1) +cx_attr_export CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size); @@ -605,14 +630,28 @@ * the return value will be #CX_PROPERTIES_NO_ERROR. * When the source was consumed but no k/v-pairs were found, the return value * will be #CX_PROPERTIES_NO_DATA. + * In case the source data ends unexpectedly, the #CX_PROPERTIES_INCOMPLETE_DATA + * is returned. In that case you should call this function again with the same + * sink and either an updated source or the same source if the source is able to + * yield the missing data. + * * The other result codes apply, according to their description. * * @param prop the properties interface * @param sink the sink * @param source the source - * @return the status of the last operation + * @retval CX_PROPERTIES_NO_ERROR (zero) a key/value pair was found + * @retval CX_PROPERTIES_READ_INIT_FAILED initializing the source failed + * @retval CX_PROPERTIES_READ_FAILED reading from the source failed + * @retval CX_PROPERTIES_SINK_FAILED sinking the properties into the sink failed + * @retval CX_PROPERTIES_NO_DATA the source did not provide any key/value pairs + * @retval CX_PROPERTIES_INCOMPLETE_DATA the source did not provide enough data + * @retval CX_PROPERTIES_INVALID_EMPTY_KEY the properties data contains an illegal empty key + * @retval CX_PROPERTIES_INVALID_MISSING_DELIMITER the properties data contains a line without delimiter + * @retval CX_PROPERTIES_BUFFER_ALLOC_FAILED an internal allocation was necessary but failed */ cx_attr_nonnull +cx_attr_export CxPropertiesStatus cxPropertiesLoad( CxProperties *prop, CxPropertiesSink sink,
--- a/ucx/cx/streams.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/streams.h Tue Feb 25 21:12:11 2025 +0100 @@ -27,13 +27,13 @@ */ /** - * \file streams.h + * @file streams.h * - * \brief Utility functions for data streams. + * @brief Utility functions for data streams. * - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_STREAMS_H @@ -52,12 +52,12 @@ * @param dest the destination stream * @param rfnc the read function * @param wfnc the write function - * @param buf a pointer to the copy buffer or \c NULL if a buffer + * @param buf a pointer to the copy buffer or @c NULL if a buffer * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can + * @param bufsize the size of the copy buffer - if @p buf is @c NULL you can * set this to zero to let the implementation decide * @param n the maximum number of bytes that shall be copied. - * If this is larger than \p bufsize, the content is copied over multiple + * If this is larger than @p bufsize, the content is copied over multiple * iterations. * @return the total number of bytes copied */ @@ -65,6 +65,7 @@ cx_attr_access_r(1) cx_attr_access_w(2) cx_attr_access_w(5) +cx_attr_export size_t cx_stream_bncopy( void *src, void *dest, @@ -78,14 +79,14 @@ /** * Reads data from a stream and writes it to another stream. * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function - * @param buf a pointer to the copy buffer or \c NULL if a buffer + * @param src (@c void*) the source stream + * @param dest (@c void*) the destination stream + * @param rfnc (@c cx_read_func) the read function + * @param wfnc (@c cx_write_func) the write function + * @param buf (@c char*) a pointer to the copy buffer or @c NULL if a buffer * shall be implicitly created on the heap - * @param bufsize the size of the copy buffer - if \p buf is \c NULL you can - * set this to zero to let the implementation decide + * @param bufsize (@c size_t) the size of the copy buffer - if @p buf is + * @c NULL you can set this to zero to let the implementation decide * @return total number of bytes copied */ #define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \ @@ -106,6 +107,7 @@ cx_attr_nonnull cx_attr_access_r(1) cx_attr_access_w(2) +cx_attr_export size_t cx_stream_ncopy( void *src, void *dest, @@ -119,10 +121,10 @@ * * The data is temporarily stored in a stack allocated buffer. * - * @param src the source stream - * @param dest the destination stream - * @param rfnc the read function - * @param wfnc the write function + * @param src (@c void*) the source stream + * @param dest (@c void*) the destination stream + * @param rfnc (@c cx_read_func) the read function + * @param wfnc (@c cx_write_func) the write function * @return total number of bytes copied */ #define cx_stream_copy(src, dest, rfnc, wfnc) \
--- a/ucx/cx/string.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/string.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file string.h - * \brief Strings that know their length. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file string.h + * @brief Strings that know their length. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_STRING_H @@ -42,6 +42,7 @@ /** * The maximum length of the "needle" in cx_strstr() that can use SBO. */ +cx_attr_export extern const unsigned cx_strstr_sbo_size; /** @@ -50,8 +51,7 @@ struct cx_mutstr_s { /** * A pointer to the string. - * \note The string is not necessarily \c NULL terminated. - * Always use the length. + * @note The string is not necessarily @c NULL terminated. */ char *ptr; /** The length of the string */ @@ -69,8 +69,7 @@ struct cx_string_s { /** * A pointer to the immutable string. - * \note The string is not necessarily \c NULL terminated. - * Always use the length. + * @note The string is not necessarily @c NULL terminated. */ const char *ptr; /** The length of the string */ @@ -148,7 +147,7 @@ /** * A literal initializer for an UCX string structure. * - * The argument MUST be a string (const char*) \em literal. + * The argument MUST be a string (const char*) @em literal. * * @param literal the string literal */ @@ -160,9 +159,9 @@ /** * Wraps a mutable string that must be zero-terminated. * - * The length is implicitly inferred by using a call to \c strlen(). + * The length is implicitly inferred by using a call to @c strlen(). * - * \note the wrapped string will share the specified pointer to the string. + * @note the wrapped string will share the specified pointer to the string. * If you do want a copy, use cx_strdup() on the return value of this function. * * If you need to wrap a constant string, use cx_str(). @@ -175,19 +174,20 @@ cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) +cx_attr_export cxmutstr cx_mutstr(char *cstring); /** * Wraps a string that does not need to be zero-terminated. * - * The argument may be \c NULL if the length is zero. + * The argument may be @c NULL if the length is zero. * - * \note the wrapped string will share the specified pointer to the string. + * @note the wrapped string will share the specified pointer to the string. * If you do want a copy, use cx_strdup() on the return value of this function. * * If you need to wrap a constant string, use cx_strn(). * - * @param cstring the string to wrap (or \c NULL, only if the length is zero) + * @param cstring the string to wrap (or @c NULL, only if the length is zero) * @param length the length of the string * @return the wrapped string * @@ -195,6 +195,7 @@ */ cx_attr_nodiscard cx_attr_access_rw(1, 2) +cx_attr_export cxmutstr cx_mutstrn( char *cstring, size_t length @@ -203,9 +204,9 @@ /** * Wraps a string that must be zero-terminated. * - * The length is implicitly inferred by using a call to \c strlen(). + * The length is implicitly inferred by using a call to @c strlen(). * - * \note the wrapped string will share the specified pointer to the string. + * @note the wrapped string will share the specified pointer to the string. * If you do want a copy, use cx_strdup() on the return value of this function. * * If you need to wrap a non-constant string, use cx_mutstr(). @@ -218,20 +219,21 @@ cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) +cx_attr_export cxstring cx_str(const char *cstring); /** * Wraps a string that does not need to be zero-terminated. * - * The argument may be \c NULL if the length is zero. + * The argument may be @c NULL if the length is zero. * - * \note the wrapped string will share the specified pointer to the string. + * @note the wrapped string will share the specified pointer to the string. * If you do want a copy, use cx_strdup() on the return value of this function. * * If you need to wrap a non-constant string, use cx_mutstrn(). * - * @param cstring the string to wrap (or \c NULL, only if the length is zero) + * @param cstring the string to wrap (or @c NULL, only if the length is zero) * @param length the length of the string * @return the wrapped string * @@ -239,6 +241,7 @@ */ cx_attr_nodiscard cx_attr_access_r(1, 2) +cx_attr_export cxstring cx_strn( const char *cstring, size_t length @@ -282,12 +285,12 @@ * * Does nothing for already immutable strings. * -* \note This is not seriously a cast. Instead, you get a copy +* @note This is not seriously a cast. Instead, you get a copy * of the struct with the desired pointer type. Both structs still * point to the same location, though! * -* @param str the mutable string to cast -* @return an immutable copy of the string pointer +* @param str (@c cxstring or @c cxmutstr) the string to cast +* @return (@c cxstring) an immutable copy of the string pointer */ #define cx_strcast(str) _Generic((str), \ cxmutstr: cx_strcast_m, \ @@ -296,24 +299,27 @@ #endif /** - * Passes the pointer in this string to \c free(). + * Passes the pointer in this string to @c free(). * - * The pointer in the struct is set to \c NULL and the length is set to zero. + * The pointer in the struct is set to @c NULL and the length is set to zero + * which means that this function protects you against double-free. * - * \note There is no implementation for cxstring, because it is unlikely that + * @note There is no implementation for cxstring, because it is unlikely that * you ever have a <code>const char*</code> you are really supposed to free. * If you encounter such situation, you should double-check your code. * * @param str the string to free */ +cx_attr_export void cx_strfree(cxmutstr *str); /** * Passes the pointer in this string to the allocators free function. * - * The pointer in the struct is set to \c NULL and the length is set to zero. + * The pointer in the struct is set to @c NULL and the length is set to zero + * which means that this function protects you against double-free. * - * \note There is no implementation for cxstring, because it is unlikely that + * @note There is no implementation for cxstring, because it is unlikely that * you ever have a <code>const char*</code> you are really supposed to free. * If you encounter such situation, you should double-check your code. * @@ -321,6 +327,7 @@ * @param str the string to free */ cx_attr_nonnull_arg(1) +cx_attr_export void cx_strfree_a( const CxAllocator *alloc, cxmutstr *str @@ -331,7 +338,7 @@ * * If this sum overflows, errno is set to EOVERFLOW. * - * \attention if the count argument is larger than the number of the + * @attention if the count argument is larger than the number of the * specified strings, the behavior is undefined. * * @param count the total number of specified strings @@ -339,6 +346,7 @@ * @return the accumulated length of all strings */ cx_attr_nodiscard +cx_attr_export size_t cx_strlen( size_t count, ... @@ -348,26 +356,27 @@ * Concatenates strings. * * The resulting string will be allocated by the specified allocator. - * So developers \em must pass the return value to cx_strfree_a() eventually. + * So developers @em must pass the return value to cx_strfree_a() eventually. * - * If \p str already contains a string, the memory will be reallocated and + * If @p str already contains a string, the memory will be reallocated and * the other strings are appended. Otherwise, new memory is allocated. * * If memory allocation fails, the pointer in the returned string will - * be \c NULL. Depending on the allocator, \c errno might be set. + * be @c NULL. Depending on the allocator, @c errno might be set. * - * \note It is guaranteed that there is only one allocation for the + * @note It is guaranteed that there is only one allocation for the * resulting string. * It is also guaranteed that the returned string is zero-terminated. * * @param alloc the allocator to use * @param str the string the other strings shall be concatenated to * @param count the number of the other following strings to concatenate - * @param ... all other strings + * @param ... all other UCX strings * @return the concatenated string */ cx_attr_nodiscard cx_attr_nonnull +cx_attr_export cxmutstr cx_strcat_ma( const CxAllocator *alloc, cxmutstr str, @@ -379,19 +388,19 @@ * Concatenates strings and returns a new string. * * The resulting string will be allocated by the specified allocator. - * So developers \em must pass the return value to cx_strfree_a() eventually. + * So developers @em must pass the return value to cx_strfree_a() eventually. * * If memory allocation fails, the pointer in the returned string will - * be \c NULL. Depending on the allocator, \c errno might be set. + * be @c NULL. Depending on the allocator, @c errno might be set. * - * \note It is guaranteed that there is only one allocation for the + * @note It is guaranteed that there is only one allocation for the * resulting string. * It is also guaranteed that the returned string is zero-terminated. * - * @param alloc the allocator to use - * @param count the number of the other following strings to concatenate - * @param ... all other strings - * @return the concatenated string + * @param alloc (@c CxAllocator*) the allocator to use + * @param count (@c size_t) the number of the other following strings to concatenate + * @param ... all other UCX strings + * @return (@c cxmutstr) the concatenated string */ #define cx_strcat_a(alloc, count, ...) \ cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) @@ -399,19 +408,19 @@ /** * Concatenates strings and returns a new string. * - * The resulting string will be allocated by standard \c malloc(). - * So developers \em must pass the return value to cx_strfree() eventually. + * The resulting string will be allocated by standard @c malloc(). + * So developers @em must pass the return value to cx_strfree() eventually. * * If memory allocation fails, the pointer in the returned string will - * be \c NULL and \c errno might be set. + * be @c NULL and @c errno might be set. * - * \note It is guaranteed that there is only one allocation for the + * @note It is guaranteed that there is only one allocation for the * resulting string. * It is also guaranteed that the returned string is zero-terminated. * - * @param count the number of the other following strings to concatenate - * @param ... all other strings - * @return the concatenated string + * @param count (@c size_t) the number of the other following strings to concatenate + * @param ... all other UCX strings + * @return (@c cxmutstr) the concatenated string */ #define cx_strcat(count, ...) \ cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) @@ -419,23 +428,23 @@ /** * Concatenates strings. * - * The resulting string will be allocated by standard \c malloc(). - * So developers \em must pass the return value to cx_strfree() eventually. + * The resulting string will be allocated by standard @c malloc(). + * So developers @em must pass the return value to cx_strfree() eventually. * - * If \p str already contains a string, the memory will be reallocated and + * If @p str already contains a string, the memory will be reallocated and * the other strings are appended. Otherwise, new memory is allocated. * * If memory allocation fails, the pointer in the returned string will - * be \c NULL and \c errno might be set. + * be @c NULL and @c errno might be set. * - * \note It is guaranteed that there is only one allocation for the + * @note It is guaranteed that there is only one allocation for the * resulting string. * It is also guaranteed that the returned string is zero-terminated. * - * @param str the string the other strings shall be concatenated to - * @param count the number of the other following strings to concatenate - * @param ... all other strings - * @return the concatenated string + * @param str (@c cxmutstr) the string the other strings shall be concatenated to + * @param count (@c size_t) the number of the other following strings to concatenate + * @param ... all other strings + * @return (@c cxmutstr) the concatenated string */ #define cx_strcat_m(str, count, ...) \ cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__) @@ -443,19 +452,20 @@ /** * Returns a substring starting at the specified location. * - * \attention the new string references the same memory area as the - * input string and is usually \em not zero-terminated. + * @attention the new string references the same memory area as the + * input string and is usually @em not zero-terminated. * Use cx_strdup() to get a copy. * * @param string input string * @param start start location of the substring - * @return a substring of \p string starting at \p start + * @return a substring of @p string starting at @p start * * @see cx_strsubsl() * @see cx_strsubs_m() * @see cx_strsubsl_m() */ cx_attr_nodiscard +cx_attr_export cxstring cx_strsubs( cxstring string, size_t start @@ -464,23 +474,24 @@ /** * Returns a substring starting at the specified location. * - * The returned string will be limited to \p length bytes or the number - * of bytes available in \p string, whichever is smaller. + * The returned string will be limited to @p length bytes or the number + * of bytes available in @p string, whichever is smaller. * - * \attention the new string references the same memory area as the - * input string and is usually \em not zero-terminated. + * @attention the new string references the same memory area as the + * input string and is usually @em not zero-terminated. * Use cx_strdup() to get a copy. * * @param string input string * @param start start location of the substring * @param length the maximum length of the returned string - * @return a substring of \p string starting at \p start + * @return a substring of @p string starting at @p start * * @see cx_strsubs() * @see cx_strsubs_m() * @see cx_strsubsl_m() */ cx_attr_nodiscard +cx_attr_export cxstring cx_strsubsl( cxstring string, size_t start, @@ -490,19 +501,20 @@ /** * Returns a substring starting at the specified location. * - * \attention the new string references the same memory area as the - * input string and is usually \em not zero-terminated. + * @attention the new string references the same memory area as the + * input string and is usually @em not zero-terminated. * Use cx_strdup() to get a copy. * * @param string input string * @param start start location of the substring - * @return a substring of \p string starting at \p start + * @return a substring of @p string starting at @p start * * @see cx_strsubsl_m() * @see cx_strsubs() * @see cx_strsubsl() */ cx_attr_nodiscard +cx_attr_export cxmutstr cx_strsubs_m( cxmutstr string, size_t start @@ -511,23 +523,24 @@ /** * Returns a substring starting at the specified location. * - * The returned string will be limited to \p length bytes or the number - * of bytes available in \p string, whichever is smaller. + * The returned string will be limited to @p length bytes or the number + * of bytes available in @p string, whichever is smaller. * - * \attention the new string references the same memory area as the - * input string and is usually \em not zero-terminated. + * @attention the new string references the same memory area as the + * input string and is usually @em not zero-terminated. * Use cx_strdup() to get a copy. * * @param string input string * @param start start location of the substring * @param length the maximum length of the returned string - * @return a substring of \p string starting at \p start + * @return a substring of @p string starting at @p start * * @see cx_strsubs_m() * @see cx_strsubs() * @see cx_strsubsl() */ cx_attr_nodiscard +cx_attr_export cxmutstr cx_strsubsl_m( cxmutstr string, size_t start, @@ -542,11 +555,12 @@ * * @param string the string where to locate the character * @param chr the character to locate - * @return a substring starting at the first location of \p chr + * @return a substring starting at the first location of @p chr * * @see cx_strchr_m() */ cx_attr_nodiscard +cx_attr_export cxstring cx_strchr( cxstring string, int chr @@ -560,11 +574,12 @@ * * @param string the string where to locate the character * @param chr the character to locate - * @return a substring starting at the first location of \p chr + * @return a substring starting at the first location of @p chr * * @see cx_strchr() */ cx_attr_nodiscard +cx_attr_export cxmutstr cx_strchr_m( cxmutstr string, int chr @@ -578,11 +593,12 @@ * * @param string the string where to locate the character * @param chr the character to locate - * @return a substring starting at the last location of \p chr + * @return a substring starting at the last location of @p chr * * @see cx_strrchr_m() */ cx_attr_nodiscard +cx_attr_export cxstring cx_strrchr( cxstring string, int chr @@ -596,11 +612,12 @@ * * @param string the string where to locate the character * @param chr the character to locate - * @return a substring starting at the last location of \p chr + * @return a substring starting at the last location of @p chr * * @see cx_strrchr() */ cx_attr_nodiscard +cx_attr_export cxmutstr cx_strrchr_m( cxmutstr string, int chr @@ -610,19 +627,20 @@ * Returns a substring starting at the location of the first occurrence of the * specified string. * - * If \p haystack does not contain \p needle, an empty string is returned. + * If @p haystack does not contain @p needle, an empty string is returned. * - * If \p needle is an empty string, the complete \p haystack is + * If @p needle is an empty string, the complete @p haystack is * returned. * * @param haystack the string to be scanned * @param needle string containing the sequence of characters to match * @return a substring starting at the first occurrence of - * \p needle, or an empty string, if the sequence is not + * @p needle, or an empty string, if the sequence is not * contained * @see cx_strstr_m() */ cx_attr_nodiscard +cx_attr_export cxstring cx_strstr( cxstring haystack, cxstring needle @@ -632,19 +650,20 @@ * Returns a substring starting at the location of the first occurrence of the * specified string. * - * If \p haystack does not contain \p needle, an empty string is returned. + * If @p haystack does not contain @p needle, an empty string is returned. * - * If \p needle is an empty string, the complete \p haystack is + * If @p needle is an empty string, the complete @p haystack is * returned. * * @param haystack the string to be scanned * @param needle string containing the sequence of characters to match * @return a substring starting at the first occurrence of - * \p needle, or an empty string, if the sequence is not + * @p needle, or an empty string, if the sequence is not * contained * @see cx_strstr() */ cx_attr_nodiscard +cx_attr_export cxmutstr cx_strstr_m( cxmutstr haystack, cxstring needle @@ -653,18 +672,19 @@ /** * Splits a given string using a delimiter string. * - * \note The resulting array contains strings that point to the source - * \p string. Use cx_strdup() to get copies. + * @note The resulting array contains strings that point to the source + * @p string. Use cx_strdup() to get copies. * * @param string the string to split * @param delim the delimiter * @param limit the maximum number of split items - * @param output a pre-allocated array of at least \p limit length + * @param output a preallocated array of at least @p limit length * @return the actual number of split items */ cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3) +cx_attr_export size_t cx_strsplit( cxstring string, cxstring delim, @@ -675,13 +695,13 @@ /** * Splits a given string using a delimiter string. * - * The array pointed to by \p output will be allocated by \p allocator. + * The array pointed to by @p output will be allocated by @p allocator. * - * \note The resulting array contains strings that point to the source - * \p string. Use cx_strdup() to get copies. + * @note The resulting array contains strings that point to the source + * @p string. Use cx_strdup() to get copies. * - * \attention If allocation fails, the \c NULL pointer will be written to - * \p output and the number returned will be zero. + * @attention If allocation fails, the @c NULL pointer will be written to + * @p output and the number returned will be zero. * * @param allocator the allocator to use for allocating the resulting array * @param string the string to split @@ -694,6 +714,7 @@ cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5) +cx_attr_export size_t cx_strsplit_a( const CxAllocator *allocator, cxstring string, @@ -706,18 +727,19 @@ /** * Splits a given string using a delimiter string. * - * \note The resulting array contains strings that point to the source - * \p string. Use cx_strdup() to get copies. + * @note The resulting array contains strings that point to the source + * @p string. Use cx_strdup() to get copies. * * @param string the string to split * @param delim the delimiter * @param limit the maximum number of split items - * @param output a pre-allocated array of at least \p limit length + * @param output a preallocated array of at least @p limit length * @return the actual number of split items */ cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3) +cx_attr_export size_t cx_strsplit_m( cxmutstr string, cxstring delim, @@ -728,13 +750,13 @@ /** * Splits a given string using a delimiter string. * - * The array pointed to by \p output will be allocated by \p allocator. + * The array pointed to by @p output will be allocated by @p allocator. * - * \note The resulting array contains strings that point to the source - * \p string. Use cx_strdup() to get copies. + * @note The resulting array contains strings that point to the source + * @p string. Use cx_strdup() to get copies. * - * \attention If allocation fails, the \c NULL pointer will be written to - * \p output and the number returned will be zero. + * @attention If allocation fails, the @c NULL pointer will be written to + * @p output and the number returned will be zero. * * @param allocator the allocator to use for allocating the resulting array * @param string the string to split @@ -747,6 +769,7 @@ cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5) +cx_attr_export size_t cx_strsplit_ma( const CxAllocator *allocator, cxmutstr string, @@ -760,10 +783,11 @@ * * @param s1 the first string * @param s2 the second string - * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger - * than \p s2, zero if both strings equal + * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger + * than @p s2, zero if both strings equal */ cx_attr_nodiscard +cx_attr_export int cx_strcmp( cxstring s1, cxstring s2 @@ -774,10 +798,11 @@ * * @param s1 the first string * @param s2 the second string - * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger - * than \p s2, zero if both strings equal ignoring case + * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger + * than @p s2, zero if both strings equal ignoring case */ cx_attr_nodiscard +cx_attr_export int cx_strcasecmp( cxstring s1, cxstring s2 @@ -790,11 +815,12 @@ * * @param s1 the first string * @param s2 the second string - * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger - * than \p s2, zero if both strings equal + * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger + * than @p s2, zero if both strings equal */ cx_attr_nodiscard cx_attr_nonnull +cx_attr_export int cx_strcmp_p( const void *s1, const void *s2 @@ -807,11 +833,12 @@ * * @param s1 the first string * @param s2 the second string - * @return negative if \p s1 is smaller than \p s2, positive if \p s1 is larger - * than \p s2, zero if both strings equal ignoring case + * @return negative if @p s1 is smaller than @p s2, positive if @p s1 is larger + * than @p s2, zero if both strings equal ignoring case */ cx_attr_nodiscard cx_attr_nonnull +cx_attr_export int cx_strcasecmp_p( const void *s1, const void *s2 @@ -821,9 +848,9 @@ /** * Creates a duplicate of the specified string. * - * The new string will contain a copy allocated by \p allocator. + * The new string will contain a copy allocated by @p allocator. * - * \note The returned string is guaranteed to be zero-terminated. + * @note The returned string is guaranteed to be zero-terminated. * * @param allocator the allocator to use * @param string the string to duplicate @@ -832,7 +859,8 @@ */ cx_attr_nodiscard cx_attr_nonnull -cxmutstr cx_strdup_a( +cx_attr_export +cxmutstr cx_strdup_a_( const CxAllocator *allocator, cxstring string ); @@ -840,68 +868,58 @@ /** * Creates a duplicate of the specified string. * - * The new string will contain a copy allocated by standard - * \c malloc(). So developers \em must pass the return value to cx_strfree(). + * The new string will contain a copy allocated by @p allocator. + * + * @note The returned string is guaranteed to be zero-terminated. * - * \note The returned string is guaranteed to be zero-terminated. - * + * @param allocator (@c CxAllocator*) the allocator to use * @param string the string to duplicate - * @return a duplicate of the string - * @see cx_strdup_a() + * @return (@c cxmutstr) a duplicate of the string + * @see cx_strdup() + * @see cx_strfree_a() */ -#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string) - - -/** - * Creates a duplicate of the specified string. - * - * The new string will contain a copy allocated by \p allocator. - * - * \note The returned string is guaranteed to be zero-terminated. - * - * @param allocator the allocator to use - * @param string the string to duplicate - * @return a duplicate of the string - * @see cx_strdup_m() - */ -#define cx_strdup_ma(allocator, string) cx_strdup_a(allocator, cx_strcast(string)) +#define cx_strdup_a(allocator, string) \ + cx_strdup_a_((allocator), cx_strcast((string))) /** * Creates a duplicate of the specified string. * * The new string will contain a copy allocated by standard - * \c malloc(). So developers \em must pass the return value to cx_strfree(). + * @c malloc(). So developers @em must pass the return value to cx_strfree(). * - * \note The returned string is guaranteed to be zero-terminated. + * @note The returned string is guaranteed to be zero-terminated. * * @param string the string to duplicate - * @return a duplicate of the string - * @see cx_strdup_ma() + * @return (@c cxmutstr) a duplicate of the string + * @see cx_strdup_a() + * @see cx_strfree() */ -#define cx_strdup_m(string) cx_strdup_a(cxDefaultAllocator, cx_strcast(string)) +#define cx_strdup(string) cx_strdup_a_(cxDefaultAllocator, string) /** * Omits leading and trailing spaces. * - * \note the returned string references the same memory, thus you - * must \em not free the returned memory. + * @note the returned string references the same memory, thus you + * must @em not free the returned memory. * * @param string the string that shall be trimmed * @return the trimmed string */ cx_attr_nodiscard +cx_attr_export cxstring cx_strtrim(cxstring string); /** * Omits leading and trailing spaces. * - * \note the returned string references the same memory, thus you - * must \em not free the returned memory. + * @note the returned string references the same memory, thus you + * must @em not free the returned memory. * * @param string the string that shall be trimmed * @return the trimmed string */ cx_attr_nodiscard +cx_attr_export cxmutstr cx_strtrim_m(cxmutstr string); /** @@ -909,10 +927,11 @@ * * @param string the string to check * @param prefix the prefix the string should have - * @return \c true, if and only if the string has the specified prefix, - * \c false otherwise + * @return @c true, if and only if the string has the specified prefix, + * @c false otherwise */ cx_attr_nodiscard +cx_attr_export bool cx_strprefix( cxstring string, cxstring prefix @@ -923,10 +942,11 @@ * * @param string the string to check * @param suffix the suffix the string should have - * @return \c true, if and only if the string has the specified suffix, - * \c false otherwise + * @return @c true, if and only if the string has the specified suffix, + * @c false otherwise */ cx_attr_nodiscard +cx_attr_export bool cx_strsuffix( cxstring string, cxstring suffix @@ -937,10 +957,11 @@ * * @param string the string to check * @param prefix the prefix the string should have - * @return \c true, if and only if the string has the specified prefix, - * \c false otherwise + * @return @c true, if and only if the string has the specified prefix, + * @c false otherwise */ cx_attr_nodiscard +cx_attr_export bool cx_strcaseprefix( cxstring string, cxstring prefix @@ -951,42 +972,22 @@ * * @param string the string to check * @param suffix the suffix the string should have - * @return \c true, if and only if the string has the specified suffix, - * \c false otherwise + * @return @c true, if and only if the string has the specified suffix, + * @c false otherwise */ cx_attr_nodiscard +cx_attr_export bool cx_strcasesuffix( cxstring string, cxstring suffix ); /** - * Converts the string to lower case. - * - * The change is made in-place. If you want a copy, use cx_strdup(), first. - * - * @param string the string to modify - * @see cx_strdup() - */ -void cx_strlower(cxmutstr string); - -/** - * Converts the string to upper case. + * Replaces a string with another string. * - * The change is made in-place. If you want a copy, use cx_strdup(), first. + * Replaces at most @p replmax occurrences. * - * @param string the string to modify - * @see cx_strdup() - */ -void cx_strupper(cxmutstr string); - -/** - * Replaces a pattern in a string with another string. - * - * The pattern is taken literally and is no regular expression. - * Replaces at most \p replmax occurrences. - * - * The returned string will be allocated by \p allocator and is guaranteed + * The returned string will be allocated by @p allocator and is guaranteed * to be zero-terminated. * * If allocation fails, or the input string is empty, @@ -994,81 +995,76 @@ * * @param allocator the allocator to use * @param str the string where replacements should be applied - * @param pattern the pattern to search for + * @param search the string to search for * @param replacement the replacement string * @param replmax maximum number of replacements * @return the resulting string after applying the replacements */ cx_attr_nodiscard cx_attr_nonnull +cx_attr_export cxmutstr cx_strreplacen_a( const CxAllocator *allocator, cxstring str, - cxstring pattern, + cxstring search, cxstring replacement, size_t replmax ); /** - * Replaces a pattern in a string with another string. + * Replaces a string with another string. * - * The pattern is taken literally and is no regular expression. - * Replaces at most \p replmax occurrences. + * Replaces at most @p replmax occurrences. * - * The returned string will be allocated by \c malloc() and is guaranteed + * The returned string will be allocated by @c malloc() and is guaranteed * to be zero-terminated. * * If allocation fails, or the input string is empty, * the returned string will be empty. * - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @param replmax maximum number of replacements - * @return the resulting string after applying the replacements + * @param str (@c cxstring) the string where replacements should be applied + * @param search (@c cxstring) the string to search for + * @param replacement (@c cxstring) the replacement string + * @param replmax (@c size_t) maximum number of replacements + * @return (@c cxmutstr) the resulting string after applying the replacements */ -#define cx_strreplacen(str, pattern, replacement, replmax) \ -cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, replmax) +#define cx_strreplacen(str, search, replacement, replmax) \ +cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax) /** - * Replaces a pattern in a string with another string. + * Replaces a string with another string. * - * The pattern is taken literally and is no regular expression. - * - * The returned string will be allocated by \p allocator and is guaranteed + * The returned string will be allocated by @p allocator and is guaranteed * to be zero-terminated. * * If allocation fails, or the input string is empty, * the returned string will be empty. * - * @param allocator the allocator to use - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @return the resulting string after applying the replacements + * @param allocator (@c CxAllocator*) the allocator to use + * @param str (@c cxstring) the string where replacements should be applied + * @param search (@c cxstring) the string to search for + * @param replacement (@c cxstring) the replacement string + * @return (@c cxmutstr) the resulting string after applying the replacements */ -#define cx_strreplace_a(allocator, str, pattern, replacement) \ -cx_strreplacen_a(allocator, str, pattern, replacement, SIZE_MAX) +#define cx_strreplace_a(allocator, str, search, replacement) \ +cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX) /** - * Replaces a pattern in a string with another string. + * Replaces a string with another string. * - * The pattern is taken literally and is no regular expression. - * Replaces at most \p replmax occurrences. - * - * The returned string will be allocated by \c malloc() and is guaranteed + * The returned string will be allocated by @c malloc() and is guaranteed * to be zero-terminated. * * If allocation fails, or the input string is empty, * the returned string will be empty. * - * @param str the string where replacements should be applied - * @param pattern the pattern to search for - * @param replacement the replacement string - * @return the resulting string after applying the replacements + * @param str (@c cxstring) the string where replacements should be applied + * @param search (@c cxstring) the string to search for + * @param replacement (@c cxstring) the replacement string + * @return (@c cxmutstr) the resulting string after applying the replacements */ -#define cx_strreplace(str, pattern, replacement) \ -cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, SIZE_MAX) +#define cx_strreplace(str, search, replacement) \ +cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX) /** * Creates a string tokenization context. @@ -1079,26 +1075,23 @@ * @return a new string tokenization context */ cx_attr_nodiscard -CxStrtokCtx cx_strtok( +cx_attr_export +CxStrtokCtx cx_strtok_( cxstring str, cxstring delim, size_t limit ); /** -* Creates a string tokenization context for a mutable string. -* -* @param str the string to tokenize -* @param delim the delimiter (must not be empty) -* @param limit the maximum number of tokens that shall be returned -* @return a new string tokenization context -*/ -cx_attr_nodiscard -CxStrtokCtx cx_strtok_m( - cxmutstr str, - cxstring delim, - size_t limit -); + * Creates a string tokenization context. + * + * @param str the string to tokenize + * @param delim the delimiter string (must not be empty) + * @param limit (@c size_t) the maximum number of tokens that shall be returned + * @return (@c CxStrtokCtx) a new string tokenization context + */ +#define cx_strtok(str, delim, limit) \ + cx_strtok_(cx_strcast((str)), cx_strcast((delim)), (limit)) /** * Returns the next token. @@ -1113,6 +1106,7 @@ cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2) +cx_attr_export bool cx_strtok_next( CxStrtokCtx *ctx, cxstring *token @@ -1122,6 +1116,8 @@ * Returns the next token of a mutable string. * * The token will point to the source string. + * + * @attention * If the context was not initialized over a mutable string, modifying * the data of the returned token is undefined behavior. * @@ -1133,6 +1129,7 @@ cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2) +cx_attr_export bool cx_strtok_next_m( CxStrtokCtx *ctx, cxmutstr *token @@ -1147,6 +1144,7 @@ */ cx_attr_nonnull cx_attr_access_r(2, 3) +cx_attr_export void cx_strtok_delim( CxStrtokCtx *ctx, const cxstring *delim, @@ -1158,90 +1156,276 @@ * ------------------------------------------------------------------------- */ /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtos_lc(cxstring str, short *output, int base, const char *groupsep); -/** - * \copydoc cx_strtouz_lc() - */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoi_lc(cxstring str, int *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtol_lc(cxstring str, long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoll_lc(cxstring str, long long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoi8_lc(cxstring str, int8_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoi16_lc(cxstring str, int16_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoi32_lc(cxstring str, int32_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoi64_lc(cxstring str, int64_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoz_lc(cxstring str, ssize_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtous_lc(cxstring str, unsigned short *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtou_lc(cxstring str, unsigned int *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoul_lc(cxstring str, unsigned long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtoull_lc(cxstring str, unsigned long long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtou8_lc(cxstring str, uint8_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtou16_lc(cxstring str, uint16_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtou32_lc(cxstring str, uint32_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep); + /** - * \copydoc cx_strtouz_lc() + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtou64_lc(cxstring str, uint64_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1254,10 +1438,654 @@ * @param output a pointer to the integer variable where the result shall be stored * @param base 2, 8, 10, or 16 * @param groupsep each character in this string is treated as group separator and ignored during conversion - * @return zero on success, non-zero if conversion was not possible + * @retval zero success + * @retval non-zero conversion was not possible + */ +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep); + +/** + * Converts a string to a single precision floating point number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character. + * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. + * + * @param str the string to convert + * @param output a pointer to the float variable where the result shall be stored + * @param decsep the decimal separator + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep); + +/** + * Converts a string to a double precision floating point number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character. + * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. + * + * @param str the string to convert + * @param output a pointer to the float variable where the result shall be stored + * @param decsep the decimal separator + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +cx_attr_access_w(2) cx_attr_nonnull_arg(2) cx_attr_export +int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep); + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtos_lc(str, output, base, groupsep) cx_strtos_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi_lc(str, output, base, groupsep) cx_strtoi_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtol_lc(str, output, base, groupsep) cx_strtol_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoll_lc(str, output, base, groupsep) cx_strtoll_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi8_lc(str, output, base, groupsep) cx_strtoi8_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi16_lc(str, output, base, groupsep) cx_strtoi16_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi32_lc(str, output, base, groupsep) cx_strtoi32_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi64_lc(str, output, base, groupsep) cx_strtoi64_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtous_lc(str, output, base, groupsep) cx_strtous_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou_lc(str, output, base, groupsep) cx_strtou_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoul_lc(str, output, base, groupsep) cx_strtoul_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoull_lc(str, output, base, groupsep) cx_strtoull_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou8_lc(str, output, base, groupsep) cx_strtou8_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou16_lc(str, output, base, groupsep) cx_strtou16_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou32_lc(str, output, base, groupsep) cx_strtou32_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou64_lc(str, output, base, groupsep) cx_strtou64_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @param groupsep (@c const @c char*) each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtouz_lc(cxstring str, size_t *output, int base, const char *groupsep); +#define cx_strtoz_lc(str, output, base, groupsep) cx_strtoz_lc_(cx_strcast(str), output, base, groupsep) + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtos(str, output, base) cx_strtos_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi(str, output, base) cx_strtoi_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtol(str, output, base) cx_strtol_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoll(str, output, base) cx_strtoll_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi8(str, output, base) cx_strtoi8_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi16(str, output, base) cx_strtoi16_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi32(str, output, base) cx_strtoi32_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoi64(str, output, base) cx_strtoi64_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoz(str, output, base) cx_strtoz_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtous(str, output, base) cx_strtous_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou(str, output, base) cx_strtou_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoul(str, output, base) cx_strtoul_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtoull(str, output, base) cx_strtoull_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou8(str, output, base) cx_strtou8_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou16(str, output, base) cx_strtou16_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou32(str, output, base) cx_strtou32_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. + * It sets errno to ERANGE when the target datatype is too small. + * + * The comma character is treated as group separator and ignored during parsing. + * If you want to choose the set of group separators, use the @c _lc variant of this function (e.g. cx_strtoz_lc()). + * + * @param str the string to convert + * @param output a pointer to the integer variable where the result shall be stored + * @param base 2, 8, 10, or 16 + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtou64(str, output, base) cx_strtou64_lc_(cx_strcast(str), output, base, ",") + +/** + * Converts a string to a single precision floating point number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character. + * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. + * + * @param str the string to convert + * @param output a pointer to the float variable where the result shall be stored + * @param decsep the decimal separator + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtof_lc(str, output, decsep, groupsep) cx_strtof_lc_(cx_strcast(str), output, decsep, groupsep) + +/** + * Converts a string to a double precision floating point number. + * + * The function returns non-zero when conversion is not possible. + * In that case the function sets errno to EINVAL when the reason is an invalid character. + * + * @param str the string to convert + * @param output a pointer to the double variable where the result shall be stored + * @param decsep the decimal separator + * @param groupsep each character in this string is treated as group separator and ignored during conversion + * @retval zero success + * @retval non-zero conversion was not possible + */ +#define cx_strtod_lc(str, output, decsep, groupsep) cx_strtod_lc_(cx_strcast(str), output, decsep, groupsep) /** * Converts a string to a single precision floating point number. @@ -1272,273 +2100,27 @@ * * @param str the string to convert * @param output a pointer to the float variable where the result shall be stored - * @param decsep the decimal separator - * @param groupsep each character in this string is treated as group separator and ignored during conversion - * @return zero on success, non-zero if conversion was not possible + * @retval zero success + * @retval non-zero conversion was not possible */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep); +#define cx_strtof(str, output) cx_strtof_lc_(cx_strcast(str), output, '.', ",") /** * Converts a string to a double precision floating point number. * * The function returns non-zero when conversion is not possible. * In that case the function sets errno to EINVAL when the reason is an invalid character. - * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. * * The decimal separator is assumed to be a dot character. * The comma character is treated as group separator and ignored during parsing. * If you want to choose a different format, use cx_strtof_lc(). * * @param str the string to convert - * @param output a pointer to the float variable where the result shall be stored - * @param decsep the decimal separator - * @param groupsep each character in this string is treated as group separator and ignored during conversion - * @return zero on success, non-zero if conversion was not possible - */ -cx_attr_access_w(2) cx_attr_nonnull_arg(2) -int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep); - -#ifndef CX_STR_IMPLEMENTATION -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtos_lc(str, output, base, groupsep) cx_strtos_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoi_lc(str, output, base, groupsep) cx_strtoi_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtol_lc(str, output, base, groupsep) cx_strtol_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoll_lc(str, output, base, groupsep) cx_strtoll_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoi8_lc(str, output, base, groupsep) cx_strtoi8_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoi16_lc(str, output, base, groupsep) cx_strtoi16_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoi32_lc(str, output, base, groupsep) cx_strtoi32_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoi64_lc(str, output, base, groupsep) cx_strtoi64_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoz_lc(str, output, base, groupsep) cx_strtoz_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtous_lc(str, output, base, groupsep) cx_strtous_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtou_lc(str, output, base, groupsep) cx_strtou_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoul_lc(str, output, base, groupsep) cx_strtoul_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtoull_lc(str, output, base, groupsep) cx_strtoull_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtou8_lc(str, output, base, groupsep) cx_strtou8_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtou16_lc(str, output, base, groupsep) cx_strtou16_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtou32_lc(str, output, base, groupsep) cx_strtou32_lc(cx_strcast(str), output, base, groupsep) -/** - * \copydoc cx_strtouz_lc() - */ -#define cx_strtou64_lc(str, output, base, groupsep) cx_strtou64_lc(cx_strcast(str), output, base, groupsep) -/** - * Converts a string to a number. - * - * The function returns non-zero when conversion is not possible. - * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. - * It sets errno to ERANGE when the target datatype is too small. - * - * @param str the string to convert - * @param output a pointer to the integer variable where the result shall be stored - * @param base 2, 8, 10, or 16 - * @param groupsep each character in this string is treated as group separator and ignored during conversion - * @return zero on success, non-zero if conversion was not possible - */ -#define cx_strtouz_lc(str, output, base, groupsep) cx_strtouz_lc(cx_strcast(str), output, base, groupsep) - -/** - * \copydoc cx_strtouz() - */ -#define cx_strtos(str, output, base) cx_strtos_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtoi(str, output, base) cx_strtoi_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtol(str, output, base) cx_strtol_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtoll(str, output, base) cx_strtoll_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtoi8(str, output, base) cx_strtoi8_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtoi16(str, output, base) cx_strtoi16_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtoi32(str, output, base) cx_strtoi32_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() + * @param output a pointer to the double variable where the result shall be stored + * @retval zero success + * @retval non-zero conversion was not possible */ -#define cx_strtoi64(str, output, base) cx_strtoi64_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtoz(str, output, base) cx_strtoz_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtous(str, output, base) cx_strtous_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtou(str, output, base) cx_strtou_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtoul(str, output, base) cx_strtoul_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtoull(str, output, base) cx_strtoull_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtou8(str, output, base) cx_strtou8_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtou16(str, output, base) cx_strtou16_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtou32(str, output, base) cx_strtou32_lc(str, output, base, ",") -/** - * \copydoc cx_strtouz() - */ -#define cx_strtou64(str, output, base) cx_strtou64_lc(str, output, base, ",") -/** - * Converts a string to a number. - * - * The function returns non-zero when conversion is not possible. - * In that case the function sets errno to EINVAL when the reason is an invalid character or an unsupported base. - * It sets errno to ERANGE when the target datatype is too small. - * - * The comma character is treated as group separator and ignored during parsing. - * If you want to choose the set of group separators, use the \c _lc variant of this function (e.g. cx_strtouz_lc()). - * - * @param str the string to convert - * @param output a pointer to the integer variable where the result shall be stored - * @param base 2, 8, 10, or 16 - * @return zero on success, non-zero if conversion was not possible - */ -#define cx_strtouz(str, output, base) cx_strtouz_lc(str, output, base, ",") - -/** - * Converts a string to a single precision floating point number. - * - * The function returns non-zero when conversion is not possible. - * In that case the function sets errno to EINVAL when the reason is an invalid character. - * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. - * - * The decimal separator is assumed to be a dot character. - * The comma character is treated as group separator and ignored during parsing. - * If you want to choose a different format, use cx_strtof_lc(). - * - * @param str the string to convert - * @param output a pointer to the float variable where the result shall be stored - * @param decsep the decimal separator - * @param groupsep each character in this string is treated as group separator and ignored during conversion - * @return zero on success, non-zero if conversion was not possible - */ -#define cx_strtof_lc(str, output, decsep, groupsep) cx_strtof_lc(cx_strcast(str), output, decsep, groupsep) -/** - * Converts a string to a double precision floating point number. - * - * The function returns non-zero when conversion is not possible. - * In that case the function sets errno to EINVAL when the reason is an invalid character. - * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. - * - * The decimal separator is assumed to be a dot character. - * The comma character is treated as group separator and ignored during parsing. - * If you want to choose a different format, use cx_strtof_lc(). - * - * @param str the string to convert - * @param output a pointer to the float variable where the result shall be stored - * @param decsep the decimal separator - * @param groupsep each character in this string is treated as group separator and ignored during conversion - * @return zero on success, non-zero if conversion was not possible - */ -#define cx_strtod_lc(str, output, decsep, groupsep) cx_strtod_lc(cx_strcast(str), output, decsep, groupsep) - -/** - * Converts a string to a single precision floating point number. - * - * The function returns non-zero when conversion is not possible. - * In that case the function sets errno to EINVAL when the reason is an invalid character. - * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. - * - * The decimal separator is assumed to be a dot character. - * The comma character is treated as group separator and ignored during parsing. - * If you want to choose a different format, use cx_strtof_lc(). - * - * @param str the string to convert - * @param output a pointer to the float variable where the result shall be stored - * @return zero on success, non-zero if conversion was not possible - */ -#define cx_strtof(str, output) cx_strtof_lc(str, output, '.', ",") -/** - * Converts a string to a double precision floating point number. - * - * The function returns non-zero when conversion is not possible. - * In that case the function sets errno to EINVAL when the reason is an invalid character. - * It sets errno to ERANGE when the necessary representation would exceed the limits defined in libc's float.h. - * - * The decimal separator is assumed to be a dot character. - * The comma character is treated as group separator and ignored during parsing. - * If you want to choose a different format, use cx_strtof_lc(). - * - * @param str the string to convert - * @param output a pointer to the float variable where the result shall be stored - * @return zero on success, non-zero if conversion was not possible - */ -#define cx_strtod(str, output) cx_strtod_lc(str, output, '.', ",") - -#endif +#define cx_strtod(str, output) cx_strtod_lc_(cx_strcast(str), output, '.', ",") #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/test.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/test.h Tue Feb 25 21:12:11 2025 +0100 @@ -35,13 +35,13 @@ * * **** IN HEADER FILE: **** * - * <pre> + * <code> * CX_TEST(function_name); * CX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional - * </pre> + * </code> * * **** IN SOURCE FILE: **** - * <pre> + * <code> * CX_TEST_SUBROUTINE(subroutine_name, paramlist) { * // tests with CX_TEST_ASSERT() * } @@ -54,7 +54,7 @@ * } * // cleanup of memory here * } - * </pre> + * </code> * * @attention Do not call own functions within a test, that use * CX_TEST_ASSERT() macros and are not defined by using CX_TEST_SUBROUTINE(). @@ -87,7 +87,6 @@ #define __FUNCTION__ __func__ #endif -// #if !defined(__clang__) && __GNUC__ > 3 #pragma GCC diagnostic ignored "-Wclobbered" #endif @@ -96,7 +95,6 @@ typedef struct CxTestSuite CxTestSuite; /** Pointer to a test function. */ -cx_attr_nonnull typedef void(*CxTest)(CxTestSuite *, void *, cx_write_func); /** Type for the internal list of test cases. */ @@ -175,7 +173,8 @@ * * @param suite the suite, the test function shall be added to * @param test the test function to register - * @return zero on success or non-zero on failure + * @retval zero success + * @retval non-zero failure */ cx_attr_nonnull static inline int cx_test_register(CxTestSuite* suite, CxTest test) { @@ -202,7 +201,7 @@ * Runs a test suite and writes the test log to the specified stream. * @param suite the test suite to run * @param out_target the target buffer or file to write the output to - * @param out_writer the write function writing to \p out_target + * @param out_writer the write function writing to @p out_target */ cx_attr_nonnull static inline void cx_test_run(CxTestSuite *suite, @@ -231,14 +230,14 @@ /** * Runs a test suite and writes the test log to the specified FILE stream. - * @param suite the test suite to run - * @param file the target file to write the output to + * @param suite (@c CxTestSuite*) the test suite to run + * @param file (@c FILE*) the target file to write the output to */ #define cx_test_run_f(suite, file) cx_test_run(suite, (void*)file, (cx_write_func)fwrite) /** * Runs a test suite and writes the test log to stdout. - * @param suite the test suite to run + * @param suite (@c CxTestSuite*) the test suite to run */ #define cx_test_run_stdout(suite) cx_test_run_f(suite, stdout) @@ -253,6 +252,17 @@ /** * Defines the scope of a test. + * + * @code + * CX_TEST(my_test_name) { + * // setup code + * CX_TEST_DO { + * // your tests go here + * } + * // tear down code + * } + * @endcode + * * @attention Any CX_TEST_ASSERT() calls must be performed in scope of * #CX_TEST_DO. */ @@ -270,8 +280,8 @@ * If the assertion is correct, the test carries on. If the assertion is not * correct, the specified message (terminated by a dot and a line break) is * written to the test suites output stream. - * @param condition the condition to check - * @param message the message that shall be printed out on failure + * @param condition (@c bool) the condition to check + * @param message (@c char*) the message that shall be printed out on failure */ #define CX_TEST_ASSERTM(condition,message) if (!(condition)) { \ const char *_assert_msg_ = message; \ @@ -286,7 +296,7 @@ * If the assertion is correct, the test carries on. If the assertion is not * correct, the specified message (terminated by a dot and a line break) is * written to the test suites output stream. - * @param condition the condition to check + * @param condition (@c bool) the condition to check */ #define CX_TEST_ASSERT(condition) CX_TEST_ASSERTM(condition, #condition " failed")
--- a/ucx/cx/tree.h Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/cx/tree.h Tue Feb 25 21:12:11 2025 +0100 @@ -26,11 +26,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ /** - * \file tree.h - * \brief Interface for tree implementations. - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License + * @file tree.h + * @brief Interface for tree implementations. + * @author Mike Becker + * @author Olaf Wintermann + * @copyright 2-Clause BSD License */ #ifndef UCX_TREE_H @@ -138,7 +138,7 @@ */ size_t depth; /** - * The next element in the queue or \c NULL. + * The next element in the queue or @c NULL. */ struct cx_tree_visitor_queue_s *next; }; @@ -233,7 +233,7 @@ * Advises the iterator to skip the subtree below the current node and * also continues the current loop. * - * @param iterator the iterator + * @param iterator (@c CxTreeIterator) the iterator */ #define cxTreeIteratorContinue(iterator) (iterator).skip = true; continue @@ -241,7 +241,7 @@ * Advises the visitor to skip the subtree below the current node and * also continues the current loop. * - * @param visitor the visitor + * @param visitor (@c CxTreeVisitor) the visitor */ #define cxTreeVisitorContinue(visitor) cxTreeIteratorContinue(visitor) @@ -249,7 +249,7 @@ * Links a node to a (new) parent. * * If the node has already a parent, it is unlinked, first. - * If the parent has children already, the node is \em appended to the list + * If the parent has children already, the node is @em appended to the list * of all currently existing children. * * @param parent the parent node @@ -263,6 +263,7 @@ * @see cx_tree_unlink() */ cx_attr_nonnull +cx_attr_export void cx_tree_link( void *parent, void *node, @@ -288,6 +289,7 @@ * @see cx_tree_link() */ cx_attr_nonnull +cx_attr_export void cx_tree_unlink( void *node, ptrdiff_t loc_parent, @@ -305,8 +307,8 @@ /** * Function pointer for a search function. * - * A function of this kind shall check if the specified \p node - * contains the given \p data or if one of the children might contain + * A function of this kind shall check if the specified @p node + * contains the given @p data or if one of the children might contain * the data. * * The function should use the returned integer to indicate how close the @@ -335,8 +337,8 @@ /** * Function pointer for a search function. * - * A function of this kind shall check if the specified \p node - * contains the same \p data as \p new_node or if one of the children might + * A function of this kind shall check if the specified @p node + * contains the same @p data as @p new_node or if one of the children might * contain the data. * * The function should use the returned integer to indicate how close the @@ -354,7 +356,7 @@ * @param node the node that is currently investigated * @param new_node a new node with the information which is searched * - * @return 0 if \p node contains the same data as \p new_node, + * @return 0 if @p node contains the same data as @p new_node, * positive if one of the children might contain the data, * negative if neither the node, nor the children contains the data */ @@ -370,8 +372,8 @@ * * Depending on the tree structure it is not necessarily guaranteed that the * "closest" match is uniquely defined. This function will search for a node - * with the best match according to the \p sfunc (meaning: the return value of - * \p sfunc which is closest to zero). If that is also ambiguous, an arbitrary + * with the best match according to the @p sfunc (meaning: the return value of + * @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary * node matching the criteria is returned. * * @param root the root node @@ -387,6 +389,7 @@ */ cx_attr_nonnull cx_attr_access_w(5) +cx_attr_export int cx_tree_search_data( const void *root, size_t depth, @@ -406,8 +409,8 @@ * * Depending on the tree structure it is not necessarily guaranteed that the * "closest" match is uniquely defined. This function will search for a node - * with the best match according to the \p sfunc (meaning: the return value of - * \p sfunc which is closest to zero). If that is also ambiguous, an arbitrary + * with the best match according to the @p sfunc (meaning: the return value of + * @p sfunc which is closest to zero). If that is also ambiguous, an arbitrary * node matching the criteria is returned. * * @param root the root node @@ -423,6 +426,7 @@ */ cx_attr_nonnull cx_attr_access_w(5) +cx_attr_export int cx_tree_search( const void *root, size_t depth, @@ -454,6 +458,7 @@ * @see cxTreeIteratorDispose() */ cx_attr_nodiscard +cx_attr_export CxTreeIterator cx_tree_iterator( void *root, bool visit_on_exit, @@ -480,6 +485,7 @@ * @see cxTreeVisitorDispose() */ cx_attr_nodiscard +cx_attr_export CxTreeVisitor cx_tree_visitor( void *root, ptrdiff_t loc_children, @@ -491,9 +497,9 @@ * The first argument points to the data the node shall contain and * the second argument may be used for additional data (e.g. an allocator). * Functions of this type shall either return a new pointer to a newly - * created node or \c NULL when allocation fails. + * created node or @c NULL when allocation fails. * - * \note the function may leave the node pointers in the struct uninitialized. + * @note the function may leave the node pointers in the struct uninitialized. * The caller is responsible to set them according to the intended use case. */ cx_attr_nonnull_arg(1) @@ -505,6 +511,7 @@ * This variable is used by #cx_tree_add_array() and #cx_tree_add_iter() to * implement optimized insertion of multiple elements into a tree. */ +cx_attr_export extern unsigned int cx_tree_add_look_around_depth; /** @@ -513,11 +520,11 @@ * Once an element cannot be added to the tree, this function returns, leaving * the iterator in a valid state pointing to the element that could not be * added. - * Also, the pointer of the created node will be stored to \p failed. + * Also, the pointer of the created node will be stored to @p failed. * The integer returned by this function denotes the number of elements obtained - * from the \p iter that have been successfully processed. - * When all elements could be processed, a \c NULL pointer will be written to - * \p failed. + * from the @p iter that have been successfully processed. + * When all elements could be processed, a @c NULL pointer will be written to + * @p failed. * * The advantage of this function compared to multiple invocations of * #cx_tree_add() is that the search for the insert locations is not always @@ -547,6 +554,7 @@ */ cx_attr_nonnull_arg(1, 3, 4, 6, 7) cx_attr_access_w(6) +cx_attr_export size_t cx_tree_add_iter( struct cx_iterator_base_s *iter, size_t num, @@ -566,11 +574,11 @@ * Adds multiple elements efficiently to a tree. * * Once an element cannot be added to the tree, this function returns, storing - * the pointer of the created node to \p failed. + * the pointer of the created node to @p failed. * The integer returned by this function denotes the number of elements from - * the \p src array that have been successfully processed. - * When all elements could be processed, a \c NULL pointer will be written to - * \p failed. + * the @p src array that have been successfully processed. + * When all elements could be processed, a @c NULL pointer will be written to + * @p failed. * * The advantage of this function compared to multiple invocations of * #cx_tree_add() is that the search for the insert locations is not always @@ -583,8 +591,8 @@ * Refer to the documentation of #cx_tree_add() for more details. * * @param src a pointer to the source data array - * @param num the number of elements in the \p src array - * @param elem_size the size of each element in the \p src array + * @param num the number of elements in the @p src array + * @param elem_size the size of each element in the @p src array * @param sfunc a search function * @param cfunc a node creation function * @param cdata optional additional data @@ -601,6 +609,7 @@ */ cx_attr_nonnull_arg(1, 4, 5, 7, 8) cx_attr_access_w(7) +cx_attr_export size_t cx_tree_add_array( const void *src, size_t num, @@ -621,28 +630,28 @@ * Adds data to a tree. * * An adequate location where to add the new tree node is searched with the - * specified \p sfunc. + * specified @p sfunc. * - * When a location is found, the \p cfunc will be invoked with \p cdata. + * When a location is found, the @p cfunc will be invoked with @p cdata. * - * The node returned by \p cfunc will be linked into the tree. - * When \p sfunc returned a positive integer, the new node will be linked as a + * The node returned by @p cfunc will be linked into the tree. + * When @p sfunc returned a positive integer, the new node will be linked as a * child. The other children (now siblings of the new node) are then checked - * with \p sfunc, whether they could be children of the new node and re-linked + * with @p sfunc, whether they could be children of the new node and re-linked * accordingly. * - * When \p sfunc returned zero and the found node has a parent, the new + * When @p sfunc returned zero and the found node has a parent, the new * node will be added as sibling - otherwise, the new node will be added * as a child. * - * When \p sfunc returned a negative value, the new node will not be added to + * When @p sfunc returned a negative value, the new node will not be added to * the tree and this function returns a non-zero value. - * The caller should check if \p cnode contains a node pointer and deal with the + * The caller should check if @p cnode contains a node pointer and deal with the * node that could not be added. * - * This function also returns a non-zero value when \p cfunc tries to allocate - * a new node but fails to do so. In that case, the pointer stored to \p cnode - * will be \c NULL. + * This function also returns a non-zero value when @p cfunc tries to allocate + * a new node but fails to do so. In that case, the pointer stored to @p cnode + * will be @c NULL. * * Multiple elements can be added more efficiently with * #cx_tree_add_array() or #cx_tree_add_iter(). @@ -664,6 +673,7 @@ */ cx_attr_nonnull_arg(1, 2, 3, 5, 6) cx_attr_access_w(5) +cx_attr_export int cx_tree_add( const void *src, cx_tree_search_func sfunc, @@ -727,7 +737,7 @@ /** * A pointer to the root node. * - * Will be \c NULL when \c size is 0. + * Will be @c NULL when @c size is 0. */ void *root; @@ -801,6 +811,10 @@ /** * Macro to roll out the #cx_tree_node_base_s structure with a custom * node type. + * + * Must be used as first member in your custom tree struct. + * + * @param type the data type for the nodes */ #define CX_TREE_NODE_BASE(type) \ type *parent; \ @@ -811,6 +825,11 @@ /** * Macro for specifying the layout of a base node tree. + * + * When your tree uses #CX_TREE_NODE_BASE, you can use this + * macro in all tree functions that expect the layout parameters + * @c loc_parent, @c loc_children, @c loc_last_child, @c loc_prev, + * and @c loc_next. */ #define cx_tree_node_base_layout \ offsetof(struct cx_tree_node_base_s, parent),\ @@ -820,26 +839,15 @@ offsetof(struct cx_tree_node_base_s, next) /** - * Macro for obtaining the node pointer layout for a specific tree. - */ -#define cx_tree_node_layout(tree) \ - (tree)->loc_parent,\ - (tree)->loc_children,\ - (tree)->loc_last_child,\ - (tree)->loc_prev, \ - (tree)->loc_next - -/** * The class definition for arbitrary trees. */ struct cx_tree_class_s { /** * Member function for inserting a single element. * - * Implementations SHALL NOT simply invoke \p insert_many as this comes + * Implementations SHALL NOT simply invoke @p insert_many as this comes * with too much overhead. */ - cx_attr_nonnull int (*insert_element)( struct cx_tree_s *tree, const void *data @@ -851,7 +859,6 @@ * Implementations SHALL avoid to perform a full search in the tree for * every element even though the source data MAY be unsorted. */ - cx_attr_nonnull size_t (*insert_many)( struct cx_tree_s *tree, struct cx_iterator_base_s *iter, @@ -861,7 +868,6 @@ /** * Member function for finding a node. */ - cx_attr_nonnull void *(*find)( struct cx_tree_s *tree, const void *subtree, @@ -886,10 +892,10 @@ * tree contents, but - in contrast to #cxTreeFree() - not the tree * structure, leaving an empty tree behind. * - * \note The destructor function, if any, will \em not be invoked. That means + * @note The destructor function, if any, will @em not be invoked. That means * you will need to free the removed subtree by yourself, eventually. * - * \attention This function will not free the memory of the nodes with the + * @attention This function will not free the memory of the nodes with the * tree's allocator, because that is usually done by the advanced destructor * and would therefore result in a double-free. * @@ -898,6 +904,7 @@ * @see cxTreeFree() */ cx_attr_nonnull +cx_attr_export void cxTreeDestroySubtree(CxTree *tree, void *node); @@ -910,7 +917,7 @@ * This is a convenience macro for invoking #cxTreeDestroySubtree() on the * root node of the tree. * - * \attention Be careful when calling this function when no destructor function + * @attention Be careful when calling this function when no destructor function * is registered that actually frees the memory of nodes. In that case you will * need a reference to the (former) root node of the tree somewhere or * otherwise you will be leaking memory. @@ -928,7 +935,7 @@ * It is guaranteed that for each node the simple destructor is invoked before * the advanced destructor. * - * \attention This function will only invoke the destructor functions + * @attention This function will only invoke the destructor functions * on the nodes. * It will NOT additionally free the nodes with the tree's allocator, because * that would cause a double-free in most scenarios where the advanced @@ -936,25 +943,20 @@ * * @param tree the tree to free */ -static inline void cxTreeFree(CxTree *tree) { - if (tree == NULL) return; - if (tree->root != NULL) { - cxTreeClear(tree); - } - cxFree(tree->allocator, tree); -} +cx_attr_export +void cxTreeFree(CxTree *tree); /** * Creates a new tree structure based on the specified layout. * - * The specified \p allocator will be used for creating the tree struct - * and SHALL be used by \p create_func to allocate memory for the nodes. + * The specified @p allocator will be used for creating the tree struct + * and SHALL be used by @p create_func to allocate memory for the nodes. * - * \note This function will also register an advanced destructor which + * @note This function will also register an advanced destructor which * will free the nodes with the allocator's free() method. * * @param allocator the allocator that shall be used - * (if \c NULL, a default stdlib allocator will be used) + * (if @c NULL, a default stdlib allocator will be used) * @param create_func a function that creates new nodes * @param search_func a function that compares two nodes * @param search_data_func a function that compares a node with data @@ -972,6 +974,7 @@ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1) +cx_attr_export CxTree *cxTreeCreate( const CxAllocator *allocator, cx_tree_node_create_func create_func, @@ -987,18 +990,18 @@ /** * Creates a new tree structure based on a default layout. * - * Nodes created by \p create_func MUST contain #cx_tree_node_base_s as first + * Nodes created by @p create_func MUST contain #cx_tree_node_base_s as first * member (or at least respect the default offsets specified in the tree * struct) and they MUST be allocated with the specified allocator. * - * \note This function will also register an advanced destructor which + * @note This function will also register an advanced destructor which * will free the nodes with the allocator's free() method. * - * @param allocator the allocator that shall be used - * @param create_func a function that creates new nodes - * @param search_func a function that compares two nodes - * @param search_data_func a function that compares a node with data - * @return the new tree + * @param allocator (@c CxAllocator*) the allocator that shall be used + * @param create_func (@c cx_tree_node_create_func) a function that creates new nodes + * @param search_func (@c cx_tree_search_func) a function that compares two nodes + * @param search_data_func (@c cx_tree_search_data_func) a function that compares a node with data + * @return (@c CxTree*) the new tree * @see cxTreeCreate() */ #define cxTreeCreateSimple(\ @@ -1009,15 +1012,15 @@ /** * Creates a new tree structure based on an existing tree. * - * The specified \p allocator will be used for creating the tree struct. + * The specified @p allocator will be used for creating the tree struct. * - * \attention This function will create an incompletely defined tree structure + * @attention This function will create an incompletely defined tree structure * where neither the create function, the search function, nor a destructor * will be set. If you wish to use any of this functionality for the wrapped * tree, you need to specify those functions afterwards. * * @param allocator the allocator that was used for nodes of the wrapped tree - * (if \c NULL, a default stdlib allocator is assumed) + * (if @c NULL, a default stdlib allocator is assumed) * @param root the root node of the tree that shall be wrapped * @param loc_parent offset in the node struct for the parent pointer * @param loc_children offset in the node struct for the children linked list @@ -1032,6 +1035,7 @@ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1) +cx_attr_export CxTree *cxTreeCreateWrapped( const CxAllocator *allocator, void *root, @@ -1045,13 +1049,14 @@ /** * Inserts data into the tree. * - * \remark For this function to work, the tree needs specified search and + * @remark For this function to work, the tree needs specified search and * create functions, which might not be available for wrapped trees * (see #cxTreeCreateWrapped()). * * @param tree the tree * @param data the data to insert - * @return zero on success, non-zero on failure + * @retval zero success + * @retval non-zero failure */ cx_attr_nonnull static inline int cxTreeInsert( @@ -1064,7 +1069,7 @@ /** * Inserts elements provided by an iterator efficiently into the tree. * - * \remark For this function to work, the tree needs specified search and + * @remark For this function to work, the tree needs specified search and * create functions, which might not be available for wrapped trees * (see #cxTreeCreateWrapped()). * @@ -1076,7 +1081,7 @@ cx_attr_nonnull static inline size_t cxTreeInsertIter( CxTree *tree, - struct cx_iterator_base_s *iter, + CxIteratorBase *iter, size_t n ) { return tree->cl->insert_many(tree, iter, n); @@ -1085,7 +1090,7 @@ /** * Inserts an array of data efficiently into the tree. * - * \remark For this function to work, the tree needs specified search and + * @remark For this function to work, the tree needs specified search and * create functions, which might not be available for wrapped trees * (see #cxTreeCreateWrapped()). * @@ -1111,13 +1116,13 @@ /** * Searches the data in the specified tree. * - * \remark For this function to work, the tree needs a specified \c search_data + * @remark For this function to work, the tree needs a specified @c search_data * function, which might not be available wrapped trees * (see #cxTreeCreateWrapped()). * * @param tree the tree * @param data the data to search for - * @return the first matching node, or \c NULL when the data cannot be found + * @return the first matching node, or @c NULL when the data cannot be found */ cx_attr_nonnull cx_attr_nodiscard @@ -1131,13 +1136,13 @@ /** * Searches the data in the specified subtree. * - * When \p max_depth is zero, the depth is not limited. - * The \p subtree_root itself is on depth 1 and its children have depth 2. + * When @p max_depth is zero, the depth is not limited. + * The @p subtree_root itself is on depth 1 and its children have depth 2. * - * \note When \p subtree_root is not part of the \p tree, the behavior is + * @note When @p subtree_root is not part of the @p tree, the behavior is * undefined. * - * \remark For this function to work, the tree needs a specified \c search_data + * @remark For this function to work, the tree needs a specified @c search_data * function, which might not be the case for wrapped trees * (see #cxTreeCreateWrapped()). * @@ -1145,7 +1150,7 @@ * @param data the data to search for * @param subtree_root the node where to start * @param max_depth the maximum search depth - * @return the first matching node, or \c NULL when the data cannot be found + * @return the first matching node, or @c NULL when the data cannot be found */ cx_attr_nonnull cx_attr_nodiscard @@ -1167,6 +1172,7 @@ */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root); /** @@ -1174,10 +1180,11 @@ * * @param tree the tree * @param subtree_root the root node of the subtree - * @return the tree depth including the \p subtree_root + * @return the tree depth including the @p subtree_root */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root); /** @@ -1188,10 +1195,11 @@ */ cx_attr_nonnull cx_attr_nodiscard +cx_attr_export size_t cxTreeDepth(CxTree *tree); /** - * Creates a depth-first iterator for the specified tree starting in \p node. + * Creates a depth-first iterator for the specified tree starting in @p node. * * If the node is not part of the tree, the behavior is undefined. * @@ -1216,7 +1224,7 @@ } /** - * Creates a breadth-first iterator for the specified tree starting in \p node. + * Creates a breadth-first iterator for the specified tree starting in @p node. * * If the node is not part of the tree, the behavior is undefined. * @@ -1267,7 +1275,7 @@ /** * Sets the (new) parent of the specified child. * - * If the \p child is not already member of the tree, this function behaves + * If the @p child is not already member of the tree, this function behaves * as #cxTreeAddChildNode(). * * @param tree the tree @@ -1276,6 +1284,7 @@ * @see cxTreeAddChildNode() */ cx_attr_nonnull +cx_attr_export void cxTreeSetParent( CxTree *tree, void *parent, @@ -1285,10 +1294,10 @@ /** * Adds a new node to the tree. * - * If the \p child is already member of the tree, the behavior is undefined. + * If the @p child is already member of the tree, the behavior is undefined. * Use #cxTreeSetParent() if you want to move a subtree to another location. * - * \attention The node may be externally created, but MUST obey the same rules + * @attention The node may be externally created, but MUST obey the same rules * as if it was created by the tree itself with #cxTreeAddChild() (e.g. use * the same allocator). * @@ -1298,6 +1307,7 @@ * @see cxTreeSetParent() */ cx_attr_nonnull +cx_attr_export void cxTreeAddChildNode( CxTree *tree, void *parent, @@ -1322,6 +1332,7 @@ * @see cxTreeInsert() */ cx_attr_nonnull +cx_attr_export int cxTreeAddChild( CxTree *tree, void *parent, @@ -1352,16 +1363,17 @@ * * If the node is not part of the tree, the behavior is undefined. * - * \note The destructor function, if any, will \em not be invoked. That means + * @note The destructor function, if any, will @em not be invoked. That means * you will need to free the removed node by yourself, eventually. * * @param tree the tree * @param node the node to remove (must not be the root node) * @param relink_func optional callback to update the content of each re-linked * node - * @return zero on success, non-zero if \p node is the root node of the tree + * @return zero on success, non-zero if @p node is the root node of the tree */ cx_attr_nonnull_arg(1, 2) +cx_attr_export int cxTreeRemoveNode( CxTree *tree, void *node, @@ -1373,13 +1385,14 @@ * * If the node is not part of the tree, the behavior is undefined. * - * \note The destructor function, if any, will \em not be invoked. That means + * @note The destructor function, if any, will @em not be invoked. That means * you will need to free the removed subtree by yourself, eventually. * * @param tree the tree * @param node the node to remove */ cx_attr_nonnull +cx_attr_export void cxTreeRemoveSubtree(CxTree *tree, void *node); /** @@ -1390,7 +1403,7 @@ * It is guaranteed that the simple destructor is invoked before * the advanced destructor. * - * \attention This function will not free the memory of the node with the + * @attention This function will not free the memory of the node with the * tree's allocator, because that is usually done by the advanced destructor * and would therefore result in a double-free. * @@ -1398,9 +1411,10 @@ * @param node the node to destroy (must not be the root node) * @param relink_func optional callback to update the content of each re-linked * node - * @return zero on success, non-zero if \p node is the root node of the tree + * @return zero on success, non-zero if @p node is the root node of the tree */ cx_attr_nonnull_arg(1, 2) +cx_attr_export int cxTreeDestroyNode( CxTree *tree, void *node,
--- a/ucx/hash_map.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/hash_map.c Tue Feb 25 21:12:11 2025 +0100 @@ -103,7 +103,8 @@ if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len && memcmp(elm->key.data, key.data, key.len) == 0) { - // overwrite existing element + // overwrite existing element, but call destructors first + cx_invoke_destructor(map, elm->data); if (map->collection.store_pointer) { memcpy(elm->data, &value, sizeof(void *)); } else { @@ -169,13 +170,13 @@ /** * Helper function to avoid code duplication. * - * If \p remove is true, and \p targetbuf is \c NULL, the element + * If @p remove is true, and @p targetbuf is @c NULL, the element * will be destroyed when found. * - * If \p remove is true, and \p targetbuf is set, the element will + * If @p remove is true, and @p targetbuf is set, the element will * be copied to that buffer and no destructor function is called. * - * If \p remove is false, \p targetbuf must not be non-null and + * If @p remove is false, @p targetbuf must not be non-null and * either the pointer, when the map is storing pointers, is copied * to the target buffer, or a pointer to the stored object will * be copied to the target buffer. @@ -252,22 +253,22 @@ } static void *cx_hash_map_iter_current_entry(const void *it) { - const struct cx_iterator_s *iter = it; - // struct has to have a compatible signature - return (struct cx_map_entry_s *) &(iter->kv_data); + const CxMapIterator *iter = it; + // we have to cast away const, because of the signature + return (void*) &iter->entry; } static void *cx_hash_map_iter_current_key(const void *it) { - const struct cx_iterator_s *iter = it; - struct cx_hash_map_element_s *elm = iter->elem_handle; + const CxMapIterator *iter = it; + struct cx_hash_map_element_s *elm = iter->elem; return &elm->key; } static void *cx_hash_map_iter_current_value(const void *it) { - const struct cx_iterator_s *iter = it; - const struct cx_hash_map_s *map = iter->src_handle.c; - struct cx_hash_map_element_s *elm = iter->elem_handle; - if (map->base.collection.store_pointer) { + const CxMapIterator *iter = it; + const CxMap *map = iter->map.c; + struct cx_hash_map_element_s *elm = iter->elem; + if (map->collection.store_pointer) { return *(void **) elm->data; } else { return elm->data; @@ -275,14 +276,15 @@ } static bool cx_hash_map_iter_valid(const void *it) { - const struct cx_iterator_s *iter = it; - return iter->elem_handle != NULL; + const CxMapIterator *iter = it; + return iter->elem != NULL; } static void cx_hash_map_iter_next(void *it) { - struct cx_iterator_s *iter = it; - struct cx_hash_map_element_s *elm = iter->elem_handle; - struct cx_hash_map_s *map = iter->src_handle.m; + CxMapIterator *iter = it; + CxMap *map = iter->map.m; + struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map; + struct cx_hash_map_element_s *elm = iter->elem; // remove current element, if asked if (iter->base.remove) { @@ -295,18 +297,18 @@ // search the previous element struct cx_hash_map_element_s *prev = NULL; - if (map->buckets[iter->slot] != elm) { - prev = map->buckets[iter->slot]; + if (hmap->buckets[iter->slot] != elm) { + prev = hmap->buckets[iter->slot]; while (prev->next != elm) { prev = prev->next; } } // destroy - cx_invoke_destructor((struct cx_map_s *) map, elm->data); + cx_invoke_destructor(map, elm->data); // unlink - cx_hash_map_unlink(map, iter->slot, prev, elm); + cx_hash_map_unlink(hmap, iter->slot, prev, elm); // advance elm = next; @@ -317,32 +319,31 @@ } // search the next bucket, if required - while (elm == NULL && ++iter->slot < map->bucket_count) { - elm = map->buckets[iter->slot]; + while (elm == NULL && ++iter->slot < hmap->bucket_count) { + elm = hmap->buckets[iter->slot]; } + iter->elem = elm; - // fill the struct with the next element - iter->elem_handle = elm; - if (elm == NULL) { - iter->kv_data.key = NULL; - iter->kv_data.value = NULL; - } else { - iter->kv_data.key = &elm->key; - if (map->base.collection.store_pointer) { - iter->kv_data.value = *(void **) elm->data; + // copy data to a location where the iterator can point to + // we need to do it here, because the iterator function call + // must not modify the iterator (the parameter is const) + if (elm != NULL) { + iter->entry.key = &elm->key; + if (iter->map.c->collection.store_pointer) { + iter->entry.value = *(void **) elm->data; } else { - iter->kv_data.value = elm->data; + iter->entry.value = elm->data; } } } -static CxIterator cx_hash_map_iterator( +static CxMapIterator cx_hash_map_iterator( const CxMap *map, enum cx_map_iterator_type type ) { - CxIterator iter; + CxMapIterator iter; - iter.src_handle.c = map; + iter.map.c = map; iter.elem_count = map->collection.size; switch (type) { @@ -376,17 +377,15 @@ while (elm == NULL) { elm = hash_map->buckets[++iter.slot]; } - iter.elem_handle = elm; - iter.kv_data.key = &elm->key; + iter.elem = elm; + iter.entry.key = &elm->key; if (map->collection.store_pointer) { - iter.kv_data.value = *(void **) elm->data; + iter.entry.value = *(void **) elm->data; } else { - iter.kv_data.value = elm->data; + iter.entry.value = elm->data; } } else { - iter.elem_handle = NULL; - iter.kv_data.key = NULL; - iter.kv_data.value = NULL; + iter.elem = NULL; } return iter; @@ -433,11 +432,10 @@ map->base.collection.allocator = allocator; if (itemsize > 0) { - map->base.collection.store_pointer = false; map->base.collection.elem_size = itemsize; } else { + map->base.collection.elem_size = sizeof(void *); map->base.collection.store_pointer = true; - map->base.collection.elem_size = sizeof(void *); } return (CxMap *) map;
--- a/ucx/json.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/json.c Tue Feb 25 21:12:11 2025 +0100 @@ -27,13 +27,10 @@ */ #include "cx/json.h" -#include "cx/compare.h" #include <string.h> -#include <ctype.h> #include <assert.h> #include <stdio.h> -#include <errno.h> #include <inttypes.h> /* @@ -135,6 +132,16 @@ } } +static bool json_isdigit(char c) { + // TODO: remove once UCX has public API for this + return c >= '0' && c <= '9'; +} + +static bool json_isspace(char c) { + // TODO: remove once UCX has public API for this + return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f'; +} + static int num_isexp(const char *content, size_t length, size_t pos) { if (pos >= length) { return 0; @@ -143,7 +150,7 @@ int ok = 0; for (size_t i = pos; i < length; i++) { char c = content[i]; - if (isdigit(c)) { + if (json_isdigit(c)) { ok = 1; } else if (i == pos) { if (!(c == '+' || c == '-')) { @@ -160,7 +167,7 @@ static CxJsonTokenType token_numbertype(const char *content, size_t length) { if (length == 0) return CX_JSON_TOKEN_ERROR; - if (content[0] != '-' && !isdigit(content[0])) { + if (content[0] != '-' && !json_isdigit(content[0])) { return CX_JSON_TOKEN_ERROR; } @@ -173,7 +180,7 @@ type = CX_JSON_TOKEN_NUMBER; } else if (content[i] == 'e' || content[i] == 'E') { return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR; - } else if (!isdigit(content[i])) { + } else if (!json_isdigit(content[i])) { return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep } } @@ -237,7 +244,7 @@ return CX_JSON_TOKEN_STRING; } default: { - if (isspace(c)) { + if (json_isspace(c)) { return CX_JSON_TOKEN_SPACE; } } @@ -254,7 +261,10 @@ // current token type and start index CxJsonTokenType ttype = json->uncompleted.tokentype; - size_t token_start = json->buffer.pos; + size_t token_part_start = json->buffer.pos; + + bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING + && json->uncompleted.content.ptr[json->uncompleted.content.length-1] == '\\'; for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { char c = json->buffer.space[i]; @@ -268,7 +278,7 @@ } else if (ctype == CX_JSON_TOKEN_STRING) { // begin string ttype = CX_JSON_TOKEN_STRING; - token_start = i; + token_part_start = i; } else if (ctype != CX_JSON_NO_TOKEN) { // single-char token json->buffer.pos = i + 1; @@ -276,12 +286,12 @@ return CX_JSON_NO_ERROR; } else { ttype = CX_JSON_TOKEN_LITERAL; // number or literal - token_start = i; + token_part_start = i; } } else { // finish token if (ctype != CX_JSON_NO_TOKEN) { - *result = token_create(json, false, token_start, i); + *result = token_create(json, false, token_part_start, i); if (result->tokentype == CX_JSON_NO_TOKEN) { return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE } @@ -294,18 +304,18 @@ } } else { // currently inside a string - if (json->tokenizer_escape) { - json->tokenizer_escape = false; + if (escape_end_of_string) { + escape_end_of_string = false; } else { if (c == '"') { - *result = token_create(json, true, token_start, i + 1); + *result = token_create(json, true, token_part_start, i + 1); if (result->tokentype == CX_JSON_NO_TOKEN) { return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE } json->buffer.pos = i + 1; return CX_JSON_NO_ERROR; } else if (c == '\\') { - json->tokenizer_escape = true; + escape_end_of_string = true; } } } @@ -313,13 +323,13 @@ if (ttype != CX_JSON_NO_TOKEN) { // uncompleted token - size_t uncompleted_len = json->buffer.size - token_start; + size_t uncompleted_len = json->buffer.size - token_part_start; if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { // current token is uncompleted // save current token content CxJsonToken uncompleted = { ttype, true, - cx_strdup(cx_strn(json->buffer.space + token_start, uncompleted_len)) + cx_strdup(cx_strn(json->buffer.space + token_part_start, uncompleted_len)) }; if (uncompleted.content.ptr == NULL) { return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE @@ -330,7 +340,7 @@ // combine the uncompleted token with the current token assert(json->uncompleted.allocated); cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, - cx_strn(json->buffer.space + token_start, uncompleted_len)); + cx_strn(json->buffer.space + token_part_start, uncompleted_len)); if (str.ptr == NULL) { return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE } @@ -343,9 +353,75 @@ return CX_JSON_INCOMPLETE_DATA; } +// converts a Unicode codepoint to utf8 +static unsigned codepoint_to_utf8(uint32_t codepoint, char *output_buf) { + if (codepoint <= 0x7F) { + *output_buf = (char)codepoint; + return 1; + } else if (codepoint <= 0x7FF) { + output_buf[0] = (char)(0xC0 | ((codepoint >> 6) & 0x1F)); + output_buf[1] = (char)(0x80 | (codepoint & 0x3F)); + return 2; + } else if (codepoint <= 0xFFFF) { + output_buf[0] = (char)(0xE0 | ((codepoint >> 12) & 0x0F)); + output_buf[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + output_buf[2] = (char)(0x80 | (codepoint & 0x3F)); + return 3; + } else if (codepoint <= 0x10FFFF) { + output_buf[0] = (char)(0xF0 | ((codepoint >> 18) & 0x07)); + output_buf[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F)); + output_buf[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); + output_buf[3] = (char)(0x80 | (codepoint & 0x3F)); + return 4; + } + + return 0; // LCOV_EXCL_LINE +} + +// converts a utf16 surrogate pair to utf8 +static inline uint32_t utf16pair_to_codepoint(uint16_t c0, uint16_t c1) { + return ((c0 - 0xD800) << 10) + (c1 - 0xDC00) + 0x10000; +} + +static unsigned unescape_unicode_string(cxstring str, char *utf8buf) { + // str is supposed to start with "\uXXXX" or "\uXXXX\uXXXX" + // remaining bytes in the string are ignored (str may be larger!) + + if (str.length < 6 || str.ptr[0] != '\\' || str.ptr[1] != 'u') { + return 0; + } + + unsigned utf8len = 0; + cxstring ustr1 = { str.ptr + 2, 4}; + uint16_t utf16a, utf16b; + if (!cx_strtou16_lc(ustr1, &utf16a, 16, "")) { + uint32_t codepoint; + if (utf16a < 0xD800 || utf16a > 0xE000) { + // character is in the Basic Multilingual Plane + // and encoded as a single utf16 char + codepoint = utf16a; + utf8len = codepoint_to_utf8(codepoint, utf8buf); + } else if (utf16a >= 0xD800 && utf16a <= 0xDBFF) { + // character is encoded as a surrogate pair + // get next 6 bytes + if (str.length >= 12) { + if (str.ptr[6] == '\\' && str.ptr[7] == 'u') { + cxstring ustr2 = { str.ptr+8, 4 }; + if (!cx_strtou16_lc(ustr2, &utf16b, 16, "") + && utf16b >= 0xDC00 && utf16b <= 0xDFFF) { + codepoint = utf16pair_to_codepoint(utf16a, utf16b); + utf8len = codepoint_to_utf8(codepoint, utf8buf); + } + } + } + } + } + return utf8len; +} + static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) { - // TODO: support more escape sequences - // we know that the unescaped string will be shorter by at least 2 chars + // note: this function expects that str contains the enclosing quotes! + cxmutstr result; result.length = 0; result.ptr = cxMalloc(a, str.length - 1); @@ -358,9 +434,45 @@ u = false; if (c == 'n') { c = '\n'; + } else if (c == '"') { + c = '"'; } else if (c == 't') { c = '\t'; + } else if (c == 'r') { + c = '\r'; + } else if (c == '\\') { + c = '\\'; + } else if (c == '/') { + c = '/'; // always unescape, we don't need settings here + } else if (c == 'f') { + c = '\f'; + } else if (c == 'b') { + c = '\b'; + } else if (c == 'u') { + char utf8buf[4]; + unsigned utf8len = unescape_unicode_string( + cx_strn(str.ptr + i - 1, str.length + 1 - i), + utf8buf + ); + if(utf8len > 0) { + i += utf8len < 4 ? 4 : 10; + // add all bytes from utf8buf except the last char + // to the result (last char will be added below) + utf8len--; + c = utf8buf[utf8len]; + for (unsigned x = 0; x < utf8len; x++) { + result.ptr[result.length++] = utf8buf[x]; + } + } else { + // decoding failed, ignore the entire sequence + result.ptr[result.length++] = '\\'; + } + } else { + // TODO: discuss the behavior for unrecognized escape sequences + // most parsers throw an error here - we just ignore it + result.ptr[result.length++] = '\\'; } + result.ptr[result.length++] = c; } else { if (c == '\\') { @@ -375,7 +487,60 @@ return result; } -static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { +static cxmutstr escape_string(cxmutstr str, bool escape_slash) { + // note: this function produces the string without enclosing quotes + // the reason is that we don't want to allocate memory just for that + CxBuffer buf = {0}; + + bool all_printable = true; + for (size_t i = 0; i < str.length; i++) { + unsigned char c = str.ptr[i]; + bool escape = c < 0x20 || c == '\\' || c == '"' + || (escape_slash && c == '/'); + + if (all_printable && escape) { + size_t capa = str.length + 32; + char *space = malloc(capa); + if (space == NULL) return cx_mutstrn(NULL, 0); + cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); + cxBufferWrite(str.ptr, 1, i, &buf); + all_printable = false; + } + if (escape) { + cxBufferPut(&buf, '\\'); + if (c == '\"') { + cxBufferPut(&buf, '\"'); + } else if (c == '\n') { + cxBufferPut(&buf, 'n'); + } else if (c == '\t') { + cxBufferPut(&buf, 't'); + } else if (c == '\r') { + cxBufferPut(&buf, 'r'); + } else if (c == '\\') { + cxBufferPut(&buf, '\\'); + } else if (c == '/') { + cxBufferPut(&buf, '/'); + } else if (c == '\f') { + cxBufferPut(&buf, 'f'); + } else if (c == '\b') { + cxBufferPut(&buf, 'b'); + } else { + char code[6]; + snprintf(code, sizeof(code), "u%04x", (unsigned int) c); + cxBufferPutString(&buf, code); + } + } else if (!all_printable) { + cxBufferPut(&buf, c); + } + } + if (!all_printable) { + str = cx_mutstrn(buf.space, buf.size); + } + cxBufferDestroy(&buf); + return str; +} + +static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) { CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); if (v == NULL) return NULL; // LCOV_EXCL_LINE @@ -541,21 +706,21 @@ json_add_state(json, 10 + state); switch (token.tokentype) { case CX_JSON_TOKEN_BEGIN_ARRAY: { - if (create_json_value(json, CX_JSON_ARRAY) == NULL) { + if (json_create_value(json, CX_JSON_ARRAY) == NULL) { return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE } json_add_state(json, JP_STATE_VALUE_BEGIN_AR); return_rec(CX_JSON_NO_ERROR); } case CX_JSON_TOKEN_BEGIN_OBJECT: { - if (create_json_value(json, CX_JSON_OBJECT) == NULL) { + if (json_create_value(json, CX_JSON_OBJECT) == NULL) { return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE } json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); return_rec(CX_JSON_NO_ERROR); } case CX_JSON_TOKEN_STRING: { - if ((vbuf = create_json_value(json, CX_JSON_STRING)) == NULL) { + if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE } cxmutstr str = unescape_string(json->allocator, token.content); @@ -568,7 +733,7 @@ case CX_JSON_TOKEN_INTEGER: case CX_JSON_TOKEN_NUMBER: { int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; - if (NULL == (vbuf = create_json_value(json, type))) { + if (NULL == (vbuf = json_create_value(json, type))) { return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE } if (type == CX_JSON_INTEGER) { @@ -583,7 +748,7 @@ return_rec(CX_JSON_NO_ERROR); } case CX_JSON_TOKEN_LITERAL: { - if ((vbuf = create_json_value(json, CX_JSON_LITERAL)) == NULL) { + if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE } if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { @@ -734,6 +899,7 @@ } CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) { + if (allocator == NULL) allocator = cxDefaultAllocator; CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); if (v == NULL) return NULL; v->allocator = allocator; @@ -755,6 +921,7 @@ } CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { + if (allocator == NULL) allocator = cxDefaultAllocator; CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); if (v == NULL) return NULL; v->allocator = allocator; @@ -765,6 +932,7 @@ } CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { + if (allocator == NULL) allocator = cxDefaultAllocator; CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); if (v == NULL) return NULL; v->allocator = allocator; @@ -774,6 +942,7 @@ } CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { + if (allocator == NULL) allocator = cxDefaultAllocator; CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); if (v == NULL) return NULL; v->allocator = allocator; @@ -787,6 +956,7 @@ } CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) { + if (allocator == NULL) allocator = cxDefaultAllocator; CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); if (v == NULL) return NULL; v->allocator = allocator; @@ -798,6 +968,7 @@ } CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { + if (allocator == NULL) allocator = cxDefaultAllocator; CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); if (v == NULL) return NULL; v->allocator = allocator; @@ -808,7 +979,7 @@ // LCOV_EXCL_START // never called as long as malloc() does not return NULL -static void cx_json_arr_free_temp(CxJsonValue** values, size_t count) { +static void json_arr_free_temp(CxJsonValue** values, size_t count) { for (size_t i = 0; i < count; i++) { if (values[i] == NULL) break; cxJsonValueFree(values[i]); @@ -822,7 +993,7 @@ if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateNumber(arr->allocator, num[i]); - if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } + if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); free(values); @@ -834,7 +1005,7 @@ if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateInteger(arr->allocator, num[i]); - if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } + if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); free(values); @@ -846,7 +1017,7 @@ if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateString(arr->allocator, str[i]); - if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } + if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); free(values); @@ -858,7 +1029,7 @@ if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateCxString(arr->allocator, str[i]); - if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } + if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); free(values); @@ -870,7 +1041,7 @@ if (values == NULL) return -1; for (size_t i = 0; i < count; i++) { values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]); - if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } + if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } } int ret = cxJsonArrAddValues(arr, values, count); free(values); @@ -979,25 +1150,25 @@ } } -static const CxJsonWriter cx_json_writer_default = { - false, - true, - 255, - false, - 4 -}; - CxJsonWriter cxJsonWriterCompact(void) { - return cx_json_writer_default; + return (CxJsonWriter) { + false, + true, + 6, + false, + 4, + false + }; } CxJsonWriter cxJsonWriterPretty(bool use_spaces) { return (CxJsonWriter) { true, true, - 255, + 6, use_spaces, - 4 + 4, + false }; } @@ -1044,7 +1215,7 @@ size_t actual = 0, expected = 0; // small buffer for number to string conversions - char numbuf[32]; + char numbuf[40]; // recursively write the values switch (value->type) { @@ -1078,9 +1249,11 @@ // the name actual += wfunc("\"", 1, 1, target); - // TODO: escape the string - actual += wfunc(member->name.ptr, 1, - member->name.length, target); + cxmutstr name = escape_string(member->name, settings->escape_slash); + actual += wfunc(name.ptr, 1, name.length, target); + if (name.ptr != member->name.ptr) { + cx_strfree(&name); + } actual += wfunc("\"", 1, 1, target); const char *obj_name_sep = ": "; if (settings->pretty) { @@ -1146,20 +1319,81 @@ } case CX_JSON_STRING: { actual += wfunc("\"", 1, 1, target); - // TODO: escape the string - actual += wfunc(value->value.string.ptr, 1, - value->value.string.length, target); + cxmutstr str = escape_string(value->value.string, settings->escape_slash); + actual += wfunc(str.ptr, 1, str.length, target); + if (str.ptr != value->value.string.ptr) { + cx_strfree(&str); + } actual += wfunc("\"", 1, 1, target); expected += 2 + value->value.string.length; break; } case CX_JSON_NUMBER: { - // TODO: locale bullshit - // TODO: formatting settings - snprintf(numbuf, 32, "%g", value->value.number); - size_t len = strlen(numbuf); - actual += wfunc(numbuf, 1, len, target); - expected += len; + int precision = settings->frac_max_digits; + // because of the way how %g is defined, we need to + // double the precision and truncate ourselves + precision = 1 + (precision > 15 ? 30 : 2 * precision); + snprintf(numbuf, 40, "%.*g", precision, value->value.number); + char *dot, *exp; + unsigned char max_digits; + // find the decimal separator and hope that it's one of . or , + dot = strchr(numbuf, '.'); + if (dot == NULL) { + dot = strchr(numbuf, ','); + } + if (dot == NULL) { + // no decimal separator found + // output everything until a possible exponent + max_digits = 30; + dot = numbuf; + } else { + // found a decimal separator + // output everything until the separator + // and set max digits to what the settings say + size_t len = dot - numbuf; + actual += wfunc(numbuf, 1, len, target); + expected += len; + max_digits = settings->frac_max_digits; + if (max_digits > 15) { + max_digits = 15; + } + // locale independent separator + if (max_digits > 0) { + actual += wfunc(".", 1, 1, target); + expected++; + } + dot++; + } + // find the exponent + exp = strchr(dot, 'e'); + if (exp == NULL) { + // no exponent - output the rest + if (max_digits > 0) { + size_t len = strlen(dot); + if (len > max_digits) { + len = max_digits; + } + actual += wfunc(dot, 1, len, target); + expected += len; + } + } else { + // exponent found - truncate the frac digits + // and then output the rest + if (max_digits > 0) { + size_t len = exp - dot - 1; + if (len > max_digits) { + len = max_digits; + } + actual += wfunc(dot, 1, len, target); + expected += len; + } + actual += wfunc("e", 1, 1, target); + expected++; + exp++; + size_t len = strlen(exp); + actual += wfunc(exp, 1, len, target); + expected += len; + } break; } case CX_JSON_INTEGER: { @@ -1201,12 +1435,13 @@ cx_write_func wfunc, const CxJsonWriter *settings ) { - if (settings == NULL) { - settings = &cx_json_writer_default; - } assert(target != NULL); assert(value != NULL); assert(wfunc != NULL); + CxJsonWriter writer_default = cxJsonWriterCompact(); + if (settings == NULL) { + settings = &writer_default; + } return cx_json_write_rec(target, value, wfunc, settings, 0); }
--- a/ucx/linked_list.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/linked_list.c Tue Feb 25 21:12:11 2025 +0100 @@ -56,48 +56,33 @@ return (void *) cur; } -ssize_t cx_linked_list_find( +void *cx_linked_list_find( const void *start, ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func, - const void *elem + const void *elem, + size_t *found_index ) { - void *dummy; - return cx_linked_list_find_node( - &dummy, start, - loc_advance, loc_data, - cmp_func, elem - ); -} - -ssize_t cx_linked_list_find_node( - void **result, - const void *start, - ptrdiff_t loc_advance, - ptrdiff_t loc_data, - cx_compare_func cmp_func, - const void *elem -) { - assert(result != NULL); assert(start != NULL); assert(loc_advance >= 0); assert(loc_data >= 0); assert(cmp_func); - const void *node = start; - ssize_t index = 0; + void *node = (void*) start; + size_t index = 0; do { void *current = ll_data(node); if (cmp_func(current, elem) == 0) { - *result = (void *) node; - return index; + if (found_index != NULL) { + *found_index = index; + } + return node; } node = ll_advance(node); index++; } while (node != NULL); - *result = NULL; - return -1; + return NULL; } void *cx_linked_list_first( @@ -811,11 +796,6 @@ list->collection.size = 0; } -#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE -#define CX_LINKED_LIST_SWAP_SBO_SIZE 128 -#endif -const unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE; - static int cx_ll_swap( struct cx_list_s *list, size_t i, @@ -894,41 +874,33 @@ } } - if (list->collection.elem_size > CX_LINKED_LIST_SWAP_SBO_SIZE) { - cx_linked_list_node *prev = nleft->prev; - cx_linked_list_node *next = nright->next; - cx_linked_list_node *midstart = nleft->next; - cx_linked_list_node *midend = nright->prev; + cx_linked_list_node *prev = nleft->prev; + cx_linked_list_node *next = nright->next; + cx_linked_list_node *midstart = nleft->next; + cx_linked_list_node *midend = nright->prev; - if (prev == NULL) { - ll->begin = nright; - } else { - prev->next = nright; - } - nright->prev = prev; - if (midstart == nright) { - // special case: both nodes are adjacent - nright->next = nleft; - nleft->prev = nright; - } else { - // likely case: a chain is between the two nodes - nright->next = midstart; - midstart->prev = nright; - midend->next = nleft; - nleft->prev = midend; - } - nleft->next = next; - if (next == NULL) { - ll->end = nleft; - } else { - next->prev = nleft; - } + if (prev == NULL) { + ll->begin = nright; + } else { + prev->next = nright; + } + nright->prev = prev; + if (midstart == nright) { + // special case: both nodes are adjacent + nright->next = nleft; + nleft->prev = nright; } else { - // swap payloads to avoid relinking - char buf[CX_LINKED_LIST_SWAP_SBO_SIZE]; - memcpy(buf, nleft->payload, list->collection.elem_size); - memcpy(nleft->payload, nright->payload, list->collection.elem_size); - memcpy(nright->payload, buf, list->collection.elem_size); + // likely case: a chain is between the two nodes + nright->next = midstart; + midstart->prev = nright; + midend->next = nleft; + nleft->prev = midend; + } + nleft->next = next; + if (next == NULL) { + ll->end = nleft; + } else { + next->prev = nleft; } return 0; @@ -943,35 +915,30 @@ return node == NULL ? NULL : node->payload; } -static ssize_t cx_ll_find_remove( +static size_t cx_ll_find_remove( struct cx_list_s *list, const void *elem, bool remove ) { + size_t index; + cx_linked_list *ll = ((cx_linked_list *) list); + cx_linked_list_node *node = cx_linked_list_find( + ll->begin, + CX_LL_LOC_NEXT, CX_LL_LOC_DATA, + list->collection.cmpfunc, elem, + &index + ); + if (node == NULL) { + return list->collection.size; + } if (remove) { - cx_linked_list *ll = ((cx_linked_list *) list); - cx_linked_list_node *node; - ssize_t index = cx_linked_list_find_node( - (void **) &node, - ll->begin, - CX_LL_LOC_NEXT, CX_LL_LOC_DATA, - list->collection.cmpfunc, elem - ); - if (node != NULL) { - cx_invoke_destructor(list, node->payload); - cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, - CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); - list->collection.size--; - cxFree(list->collection.allocator, node); - } - return index; - } else { - return cx_linked_list_find( - ((cx_linked_list *) list)->begin, - CX_LL_LOC_NEXT, CX_LL_LOC_DATA, - list->collection.cmpfunc, elem - ); + cx_invoke_destructor(list, node->payload); + cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, + CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); + list->collection.size--; + cxFree(list->collection.allocator, node); } + return index; } static void cx_ll_sort(struct cx_list_s *list) { @@ -1138,17 +1105,8 @@ cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); if (list == NULL) return NULL; - - list->base.cl = &cx_linked_list_class; - list->base.collection.allocator = allocator; - - if (elem_size > 0) { - list->base.collection.elem_size = elem_size; - list->base.collection.cmpfunc = comparator; - } else { - list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; - cxListStorePointers((CxList *) list); - } + cx_list_init((CxList*)list, &cx_linked_list_class, + allocator, comparator, elem_size); return (CxList *) list; }
--- a/ucx/list.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/list.c Tue Feb 25 21:12:11 2025 +0100 @@ -128,13 +128,13 @@ return ptr == NULL ? NULL : *ptr; } -static ssize_t cx_pl_find_remove( +static size_t cx_pl_find_remove( struct cx_list_s *list, const void *elem, bool remove ) { cx_pl_hack_cmpfunc(list); - ssize_t ret = list->climpl->find_remove(list, &elem, remove); + size_t ret = list->climpl->find_remove(list, &elem, remove); cx_pl_unhack_cmpfunc(list); return ret; } @@ -192,22 +192,6 @@ cx_pl_reverse, cx_pl_iterator, }; - -void cxListStoreObjects(CxList *list) { - list->collection.store_pointer = false; - if (list->climpl != NULL) { - list->cl = list->climpl; - list->climpl = NULL; - } -} - -void cxListStorePointers(CxList *list) { - list->collection.elem_size = sizeof(void *); - list->collection.store_pointer = true; - list->climpl = list->cl; - list->cl = &cx_pointer_list_class; -} - // </editor-fold> // <editor-fold desc="empty list implementation"> @@ -223,12 +207,12 @@ return NULL; } -static ssize_t cx_emptyl_find_remove( +static size_t cx_emptyl_find_remove( cx_attr_unused struct cx_list_s *list, cx_attr_unused const void *elem, cx_attr_unused bool remove ) { - return -1; + return 0; } static bool cx_emptyl_iter_valid(cx_attr_unused const void *iter) { @@ -265,18 +249,19 @@ }; CxList cx_empty_list = { - { - NULL, - NULL, - 0, - 0, - NULL, - NULL, - NULL, - false - }, - &cx_empty_list_class, - NULL + { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false, + true, + }, + &cx_empty_list_class, + NULL }; CxList *const cxEmptyList = &cx_empty_list; @@ -417,6 +402,29 @@ return 0; } +void cx_list_init( + struct cx_list_s *list, + struct cx_list_class_s *cl, + const struct cx_allocator_s *allocator, + cx_compare_func comparator, + size_t elem_size +) { + list->cl = cl; + list->collection.allocator = allocator; + list->collection.cmpfunc = comparator; + if (elem_size > 0) { + list->collection.elem_size = elem_size; + } else { + list->collection.elem_size = sizeof(void *); + if (list->collection.cmpfunc == NULL) { + list->collection.cmpfunc = cx_cmp_ptr; + } + list->collection.store_pointer = true; + list->climpl = list->cl; + list->cl = &cx_pointer_list_class; + } +} + int cxListCompare( const CxList *list, const CxList *other @@ -481,3 +489,8 @@ it.base.mutating = true; return it; } + +void cxListFree(CxList *list) { + if (list == NULL) return; + list->cl->deallocate(list); +}
--- a/ucx/map.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/map.c Tue Feb 25 21:12:11 2025 +0100 @@ -46,12 +46,12 @@ return false; } -static CxIterator cx_empty_map_iterator( +static CxMapIterator cx_empty_map_iterator( const struct cx_map_s *map, cx_attr_unused enum cx_map_iterator_type type ) { - CxIterator iter = {0}; - iter.src_handle.c = map; + CxMapIterator iter = {0}; + iter.map.c = map; iter.base.valid = cx_empty_map_iter_valid; return iter; } @@ -66,37 +66,43 @@ }; CxMap cx_empty_map = { - { - NULL, - NULL, - 0, - 0, - NULL, - NULL, - NULL, - false - }, - &cx_empty_map_class + { + NULL, + NULL, + 0, + 0, + NULL, + NULL, + NULL, + false, + true + }, + &cx_empty_map_class }; CxMap *const cxEmptyMap = &cx_empty_map; // </editor-fold> -CxIterator cxMapMutIteratorValues(CxMap *map) { - CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); +CxMapIterator cxMapMutIteratorValues(CxMap *map) { + CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); it.base.mutating = true; return it; } -CxIterator cxMapMutIteratorKeys(CxMap *map) { - CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); +CxMapIterator cxMapMutIteratorKeys(CxMap *map) { + CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); it.base.mutating = true; return it; } -CxIterator cxMapMutIterator(CxMap *map) { - CxIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); +CxMapIterator cxMapMutIterator(CxMap *map) { + CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); it.base.mutating = true; return it; } + +void cxMapFree(CxMap *map) { + if (map == NULL) return; + map->cl->deallocate(map); +}
--- a/ucx/properties.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/properties.c Tue Feb 25 21:12:11 2025 +0100 @@ -32,10 +32,10 @@ const CxPropertiesConfig cx_properties_config_default = { '=', - //'\\', '#', '\0', - '\0' + '\0', + '\\', }; void cxPropertiesInit( @@ -226,8 +226,6 @@ return CX_PROPERTIES_INVALID_EMPTY_KEY; } } - // unreachable - either we returned or skipped a blank line - assert(false); } // when we come to this point, all data must have been read @@ -254,7 +252,7 @@ CxPropertiesSink cxPropertiesMapSink(CxMap *map) { CxPropertiesSink sink; sink.sink = map; - sink.data = cxDefaultAllocator; + sink.data = (void*) cxDefaultAllocator; sink.sink_func = cx_properties_sink_map; return sink; } @@ -282,7 +280,7 @@ ) { target->ptr = src->data_ptr; target->length = fread(src->data_ptr, 1, src->data_size, src->src); - return ferror(src->src); + return ferror((FILE*)src->src); } static int cx_properties_read_init_file( @@ -362,6 +360,7 @@ // transfer the data from the source to the sink CxPropertiesStatus status; + CxPropertiesStatus kv_status = CX_PROPERTIES_NO_DATA; bool found = false; while (true) { // read input @@ -373,14 +372,23 @@ // no more data - break if (input.length == 0) { - status = found ? CX_PROPERTIES_NO_ERROR : CX_PROPERTIES_NO_DATA; + if (found) { + // something was found, check the last kv_status + if (kv_status == CX_PROPERTIES_INCOMPLETE_DATA) { + status = CX_PROPERTIES_INCOMPLETE_DATA; + } else { + status = CX_PROPERTIES_NO_ERROR; + } + } else { + // nothing found + status = CX_PROPERTIES_NO_DATA; + } break; } // set the input buffer and read the k/v-pairs cxPropertiesFill(prop, input); - CxPropertiesStatus kv_status; do { cxstring key, value; kv_status = cxPropertiesNext(prop, &key, &value);
--- a/ucx/string.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/string.c Tue Feb 25 21:12:11 2025 +0100 @@ -25,12 +25,10 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#define CX_STR_IMPLEMENTATION #include "cx/string.h" #include <string.h> #include <stdarg.h> -#include <ctype.h> #include <assert.h> #include <errno.h> #include <limits.h> @@ -222,14 +220,9 @@ cxstring string, int chr ) { - chr = 0xFF & chr; - // TODO: improve by comparing multiple bytes at once - for (size_t i = 0; i < string.length; i++) { - if (string.ptr[i] == chr) { - return cx_strsubs(string, i); - } - } - return (cxstring) {NULL, 0}; + char *ret = memchr(string.ptr, 0xFF & chr, string.length); + if (ret == NULL) return (cxstring) {NULL, 0}; + return (cxstring) {ret, string.length - (ret - string.ptr)}; } cxmutstr cx_strchr_m( @@ -265,7 +258,7 @@ } #ifndef CX_STRSTR_SBO_SIZE -#define CX_STRSTR_SBO_SIZE 512 +#define CX_STRSTR_SBO_SIZE 128 #endif const unsigned cx_strstr_sbo_size = CX_STRSTR_SBO_SIZE; @@ -295,7 +288,7 @@ // check needle length and use appropriate prefix table // if the pattern exceeds static prefix table, allocate on the heap - bool useheap = needle.length >= CX_STRSTR_SBO_SIZE; + const bool useheap = needle.length >= CX_STRSTR_SBO_SIZE; register size_t *ptable = useheap ? calloc(needle.length + 1, sizeof(size_t)) : s_prefix_table; @@ -334,7 +327,7 @@ } // if prefix table was allocated on the heap, free it - if (ptable != s_prefix_table) { + if (useheap) { free(ptable); } @@ -512,7 +505,7 @@ return cx_strcasecmp(*left, *right); } -cxmutstr cx_strdup_a( +cxmutstr cx_strdup_a_( const CxAllocator *allocator, cxstring string ) { @@ -529,14 +522,19 @@ return result; } +static bool str_isspace(char c) { + // TODO: remove once UCX has public API for this + return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f'; +} + cxstring cx_strtrim(cxstring string) { cxstring result = string; // TODO: optimize by comparing multiple bytes at once - while (result.length > 0 && isspace(*result.ptr)) { + while (result.length > 0 && str_isspace(*result.ptr)) { result.ptr++; result.length--; } - while (result.length > 0 && isspace(result.ptr[result.length - 1])) { + while (result.length > 0 && str_isspace(result.ptr[result.length - 1])) { result.length--; } return result; @@ -590,18 +588,6 @@ #endif } -void cx_strlower(cxmutstr string) { - for (size_t i = 0; i < string.length; i++) { - string.ptr[i] = (char) tolower(string.ptr[i]); - } -} - -void cx_strupper(cxmutstr string) { - for (size_t i = 0; i < string.length; i++) { - string.ptr[i] = (char) toupper(string.ptr[i]); - } -} - #ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE #define CX_STRREPLACE_INDEX_BUFFER_SIZE 64 #endif @@ -613,6 +599,8 @@ }; static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) { + // remember, the first data is on the stack! + buf = buf->next; while (buf) { struct cx_strreplace_ibuf *next = buf->next; free(buf->buf); @@ -624,49 +612,46 @@ cxmutstr cx_strreplacen_a( const CxAllocator *allocator, cxstring str, - cxstring pattern, + cxstring search, cxstring replacement, size_t replmax ) { - if (pattern.length == 0 || pattern.length > str.length || replmax == 0) + if (search.length == 0 || search.length > str.length || replmax == 0) return cx_strdup_a(allocator, str); // Compute expected buffer length - size_t ibufmax = str.length / pattern.length; + size_t ibufmax = str.length / search.length; size_t ibuflen = replmax < ibufmax ? replmax : ibufmax; if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) { ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE; } - // Allocate first index buffer - struct cx_strreplace_ibuf *firstbuf, *curbuf; - firstbuf = curbuf = calloc(1, sizeof(struct cx_strreplace_ibuf)); - if (!firstbuf) return cx_mutstrn(NULL, 0); - firstbuf->buf = calloc(ibuflen, sizeof(size_t)); - if (!firstbuf->buf) { - free(firstbuf); - return cx_mutstrn(NULL, 0); - } + // First index buffer can be on the stack + struct cx_strreplace_ibuf ibuf, *curbuf = &ibuf; + size_t ibuf_sbo[CX_STRREPLACE_INDEX_BUFFER_SIZE]; + ibuf.buf = ibuf_sbo; + ibuf.next = NULL; + ibuf.len = 0; // Search occurrences cxstring searchstr = str; size_t found = 0; do { - cxstring match = cx_strstr(searchstr, pattern); + cxstring match = cx_strstr(searchstr, search); if (match.length > 0) { // Allocate next buffer in chain, if required if (curbuf->len == ibuflen) { struct cx_strreplace_ibuf *nextbuf = calloc(1, sizeof(struct cx_strreplace_ibuf)); if (!nextbuf) { - cx_strrepl_free_ibuf(firstbuf); + cx_strrepl_free_ibuf(&ibuf); return cx_mutstrn(NULL, 0); } nextbuf->buf = calloc(ibuflen, sizeof(size_t)); if (!nextbuf->buf) { free(nextbuf); - cx_strrepl_free_ibuf(firstbuf); + cx_strrepl_free_ibuf(&ibuf); return cx_mutstrn(NULL, 0); } curbuf->next = nextbuf; @@ -677,8 +662,8 @@ found++; size_t idx = match.ptr - str.ptr; curbuf->buf[curbuf->len++] = idx; - searchstr.ptr = match.ptr + pattern.length; - searchstr.length = str.length - idx - pattern.length; + searchstr.ptr = match.ptr + search.length; + searchstr.length = str.length - idx - search.length; } else { break; } @@ -687,9 +672,9 @@ // Allocate result string cxmutstr result; { - ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length; + long long adjlen = (long long) replacement.length - (long long) search.length; size_t rcount = 0; - curbuf = firstbuf; + curbuf = &ibuf; do { rcount += curbuf->len; curbuf = curbuf->next; @@ -697,13 +682,13 @@ result.length = str.length + rcount * adjlen; result.ptr = cxMalloc(allocator, result.length + 1); if (!result.ptr) { - cx_strrepl_free_ibuf(firstbuf); + cx_strrepl_free_ibuf(&ibuf); return cx_mutstrn(NULL, 0); } } // Build result string - curbuf = firstbuf; + curbuf = &ibuf; size_t srcidx = 0; char *destptr = result.ptr; do { @@ -718,7 +703,7 @@ } // Copy the replacement and skip the source pattern - srcidx += pattern.length; + srcidx += search.length; memcpy(destptr, replacement.ptr, replacement.length); destptr += replacement.length; } @@ -730,12 +715,12 @@ result.ptr[result.length] = '\0'; // Free index buffer - cx_strrepl_free_ibuf(firstbuf); + cx_strrepl_free_ibuf(&ibuf); return result; } -CxStrtokCtx cx_strtok( +CxStrtokCtx cx_strtok_( cxstring str, cxstring delim, size_t limit @@ -753,14 +738,6 @@ return ctx; } -CxStrtokCtx cx_strtok_m( - cxmutstr str, - cxstring delim, - size_t limit -) { - return cx_strtok(cx_strcast(str), delim, limit); -} - bool cx_strtok_next( CxStrtokCtx *ctx, cxstring *token @@ -832,25 +809,24 @@ *output = (rtype) result; \ return 0 -int cx_strtos_lc(cxstring str, short *output, int base, const char *groupsep) { +int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep) { cx_strtoX_signed_impl(short, SHRT_MIN, SHRT_MAX); } -int cx_strtoi_lc(cxstring str, int *output, int base, const char *groupsep) { +int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep) { cx_strtoX_signed_impl(int, INT_MIN, INT_MAX); } -int cx_strtol_lc(cxstring str, long *output, int base, const char *groupsep) { +int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep) { cx_strtoX_signed_impl(long, LONG_MIN, LONG_MAX); } -int cx_strtoll_lc(cxstring str, long long *output, int base, const char *groupsep) { +int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep) { // strategy: parse as unsigned, check range, negate if required bool neg = false; size_t start_unsigned = 0; - // trim already, to search for a sign character - str = cx_strtrim(str); + // emptiness check if (str.length == 0) { errno = EINVAL; return -1; @@ -890,33 +866,23 @@ } } -int cx_strtoi8_lc(cxstring str, int8_t *output, int base, const char *groupsep) { +int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep) { cx_strtoX_signed_impl(int8_t, INT8_MIN, INT8_MAX); } -int cx_strtoi16_lc(cxstring str, int16_t *output, int base, const char *groupsep) { +int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep) { cx_strtoX_signed_impl(int16_t, INT16_MIN, INT16_MAX); } -int cx_strtoi32_lc(cxstring str, int32_t *output, int base, const char *groupsep) { +int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep) { cx_strtoX_signed_impl(int32_t, INT32_MIN, INT32_MAX); } -int cx_strtoi64_lc(cxstring str, int64_t *output, int base, const char *groupsep) { +int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep) { assert(sizeof(long long) == sizeof(int64_t)); // should be true on all platforms return cx_strtoll_lc(str, (long long*) output, base, groupsep); } -int cx_strtoz_lc(cxstring str, ssize_t *output, int base, const char *groupsep) { -#if SSIZE_MAX == INT32_MAX - return cx_strtoi32_lc(str, (int32_t*) output, base, groupsep); -#elif SSIZE_MAX == INT64_MAX - return cx_strtoll_lc(str, (long long*) output, base, groupsep); -#else -#error "unsupported ssize_t size" -#endif -} - #define cx_strtoX_unsigned_impl(rtype, rmax) \ uint64_t result; \ if (cx_strtou64_lc(str, &result, base, groupsep)) { \ @@ -929,21 +895,20 @@ *output = (rtype) result; \ return 0 -int cx_strtous_lc(cxstring str, unsigned short *output, int base, const char *groupsep) { +int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep) { cx_strtoX_unsigned_impl(unsigned short, USHRT_MAX); } -int cx_strtou_lc(cxstring str, unsigned int *output, int base, const char *groupsep) { +int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep) { cx_strtoX_unsigned_impl(unsigned int, UINT_MAX); } -int cx_strtoul_lc(cxstring str, unsigned long *output, int base, const char *groupsep) { +int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep) { cx_strtoX_unsigned_impl(unsigned long, ULONG_MAX); } -int cx_strtoull_lc(cxstring str, unsigned long long *output, int base, const char *groupsep) { +int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep) { // some sanity checks - str = cx_strtrim(str); if (str.length == 0) { errno = EINVAL; return -1; @@ -1021,37 +986,37 @@ return 0; } -int cx_strtou8_lc(cxstring str, uint8_t *output, int base, const char *groupsep) { +int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep) { cx_strtoX_unsigned_impl(uint8_t, UINT8_MAX); } -int cx_strtou16_lc(cxstring str, uint16_t *output, int base, const char *groupsep) { +int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep) { cx_strtoX_unsigned_impl(uint16_t, UINT16_MAX); } -int cx_strtou32_lc(cxstring str, uint32_t *output, int base, const char *groupsep) { +int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep) { cx_strtoX_unsigned_impl(uint32_t, UINT32_MAX); } -int cx_strtou64_lc(cxstring str, uint64_t *output, int base, const char *groupsep) { +int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep) { assert(sizeof(unsigned long long) == sizeof(uint64_t)); // should be true on all platforms return cx_strtoull_lc(str, (unsigned long long*) output, base, groupsep); } -int cx_strtouz_lc(cxstring str, size_t *output, int base, const char *groupsep) { +int cx_strtoz_lc_(cxstring str, size_t *output, int base, const char *groupsep) { #if SIZE_MAX == UINT32_MAX - return cx_strtou32_lc(str, (uint32_t*) output, base, groupsep); + return cx_strtou32_lc_(str, (uint32_t*) output, base, groupsep); #elif SIZE_MAX == UINT64_MAX - return cx_strtoull_lc(str, (unsigned long long *) output, base, groupsep); + return cx_strtoull_lc_(str, (unsigned long long *) output, base, groupsep); #else #error "unsupported size_t size" #endif } -int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) { +int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep) { // use string to double and add a range check double d; - int ret = cx_strtod_lc(str, &d, decsep, groupsep); + int ret = cx_strtod_lc_(str, &d, decsep, groupsep); if (ret != 0) return ret; // note: FLT_MIN is the smallest POSITIVE number that can be represented double test = d < 0 ? -d : d; @@ -1063,12 +1028,16 @@ return 0; } -int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) { +static bool str_isdigit(char c) { + // TODO: remove once UCX has public API for this + return c >= '0' && c <= '9'; +} + +int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep) { // TODO: overflow check // TODO: increase precision - // trim and check - str = cx_strtrim(str); + // emptiness check if (str.length == 0) { errno = EINVAL; return -1; @@ -1096,7 +1065,7 @@ // parse all digits until we find the decsep size_t pos = 0; do { - if (isdigit(str.ptr[pos])) { + if (str_isdigit(str.ptr[pos])) { result = result * 10 + (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { break; @@ -1125,7 +1094,7 @@ // parse everything until exponent or end double factor = 1.; do { - if (isdigit(str.ptr[pos])) { + if (str_isdigit(str.ptr[pos])) { factor *= 0.1; result = result + factor * (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { @@ -1166,7 +1135,7 @@ // parse the exponent unsigned int exp = 0; do { - if (isdigit(str.ptr[pos])) { + if (str_isdigit(str.ptr[pos])) { exp = 10 * exp + (str.ptr[pos] - '0'); } else if (strchr(groupsep, str.ptr[pos]) == NULL) { errno = EINVAL;
--- a/ucx/tree.c Sun Feb 16 17:38:07 2025 +0100 +++ b/ucx/tree.c Tue Feb 25 21:12:11 2025 +0100 @@ -42,6 +42,13 @@ #define cx_tree_ptr_locations \ loc_parent, loc_children, loc_last_child, loc_prev, loc_next +#define cx_tree_node_layout(tree) \ + (tree)->loc_parent,\ + (tree)->loc_children,\ + (tree)->loc_last_child,\ + (tree)->loc_prev, \ + (tree)->loc_next + static void cx_tree_zero_pointers( void *node, ptrdiff_t loc_parent, @@ -742,7 +749,7 @@ static size_t cx_tree_default_insert_many( CxTree *tree, - struct cx_iterator_base_s *iter, + CxIteratorBase *iter, size_t n ) { size_t ins = 0; @@ -837,6 +844,14 @@ return tree; } +void cxTreeFree(CxTree *tree) { + if (tree == NULL) return; + if (tree->root != NULL) { + cxTreeClear(tree); + } + cxFree(tree->allocator, tree); +} + CxTree *cxTreeCreateWrapped( const CxAllocator *allocator, void *root,