Mon, 10 Nov 2025 21:52:51 +0100
update ucx
--- a/application/config.c Sun Oct 19 21:20:08 2025 +0200 +++ b/application/config.c Mon Nov 10 21:52:51 2025 +0100 @@ -31,7 +31,6 @@ #include <string.h> #include <sys/types.h> #include <cx/hash_map.h> -#include <cx/utils.h> #include <errno.h> #include <libxml/tree.h> @@ -41,6 +40,8 @@ #include <libidav/utils.h> #include <libidav/config.h> +#include <cx/streams.h> + #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b) #define xstrEQ(a,b) !xmlStrcasecmp(BAD_CAST a, BAD_CAST b)
--- a/libidav/crypto.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/crypto.c Mon Nov 10 21:52:51 2025 +0100 @@ -381,12 +381,12 @@ SHA256_Init(ctx); } -void dav_sha256_update(DAV_SHA_CTX *ctx, const void *data, size_t length) { +void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t length) { SHA256_Update(ctx, data, length); } -void dav_sha256_final(char *md, DAV_SHA_CTX *ctx) { - SHA256_Final(md, ctx); +void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf) { + SHA256_Final(buf, ctx); } #else @@ -848,13 +848,16 @@ return ctx; } +void dav_sha256_init(DAV_SHA_CTX *ctx) { + CC_SHA256_Init(ctx); +} + void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t len) { CC_SHA256_Update(ctx, data, len); } void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf) { CC_SHA256_Final(buf, ctx); - free(ctx); } DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) { @@ -1422,7 +1425,7 @@ DAV_SHA_CTX *ctx = dav_sha256_create(); if(ctx) { dav_sha256_update(ctx, data, len); - dav_sha256_final(ctx, hash); + dav_sha256_final_free(ctx, hash); } return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH); } @@ -1448,7 +1451,6 @@ // cleanup cng_cleanup(ctx->hAlg, NULL, ctx->hHash, ctx->pbHashObject); - free(ctx); } DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) { @@ -1514,7 +1516,10 @@ } #endif - +void dav_sha256_final_free(DAV_SHA_CTX *ctx, unsigned char *buf) { + dav_sha256_final(ctx, buf); + free(ctx); +} CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key) { CxBuffer *encbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
--- a/libidav/crypto.h Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/crypto.h Mon Nov 10 21:52:51 2025 +0100 @@ -159,6 +159,7 @@ DAV_SHA_CTX* dav_sha256_create(void); void dav_sha256_update(DAV_SHA_CTX *ctx, const char *data, size_t len); void dav_sha256_final(DAV_SHA_CTX *ctx, unsigned char *buf); +void dav_sha256_final_free(DAV_SHA_CTX *ctx, unsigned char *buf); DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc);
--- a/libidav/davqlexec.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/davqlexec.c Mon Nov 10 21:52:51 2025 +0100 @@ -31,7 +31,6 @@ #include <string.h> #include <inttypes.h> -#include <cx/utils.h> #include <cx/map.h> #include <cx/hash_map.h> #include <cx/printf.h>
--- a/libidav/davqlparser.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/davqlparser.c Mon Nov 10 21:52:51 2025 +0100 @@ -27,7 +27,6 @@ */ #include "davqlparser.h" -#include <cx/utils.h> #include <cx/linked_list.h> #include <cx/printf.h> #include <string.h>
--- a/libidav/methods.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/methods.c Mon Nov 10 21:52:51 2025 +0100 @@ -36,7 +36,6 @@ #include "session.h" #include "xml.h" -#include <cx/utils.h> #include <cx/printf.h> #include <cx/hash_map.h> @@ -481,10 +480,6 @@ DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response) { char *url = NULL; curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url); - if(!root) { - printf("methods.c: TODO: remove\n"); - root = dav_resource_new_href(sn, util_url_path(url)); // TODO: remove - } //printf("%.*s\n\n", response->size, response->space); xmlDoc *doc = xmlReadMemory(response->space, response->size, url, NULL, 0);
--- a/libidav/pwdstore.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/pwdstore.c Mon Nov 10 21:52:51 2025 +0100 @@ -34,7 +34,8 @@ #include <stdlib.h> #include <string.h> -#include <cx/utils.h> +#include <cx/streams.h> + #include <cx/hash_map.h> #ifdef _WIN32 @@ -263,7 +264,7 @@ } static void remove_list_entries(PwdStore *s, const char *id) { - CxIterator i = cxListMutIterator(s->locations); + CxIterator i = cxListIterator(s->locations); cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { cxIteratorFlagRemoval(i); @@ -271,7 +272,7 @@ break; } } - i = cxListMutIterator(s->noloc); + i = cxListIterator(s->noloc); cx_foreach(PwdIndexEntry*, ie, i) { if(!strcmp(ie->id, id)) { cxIteratorFlagRemoval(i);
--- a/libidav/resource.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/resource.c Mon Nov 10 21:52:51 2025 +0100 @@ -37,7 +37,6 @@ #include "methods.h" #include "crypto.h" #include <cx/buffer.h> -#include <cx/utils.h> #include <cx/hash_map.h> #include <cx/printf.h> #include <cx/mempool.h> @@ -835,7 +834,7 @@ HashStream *s = stream; if(offset == 0 && whence == SEEK_SET) { unsigned char buf[DAV_SHA256_DIGEST_LENGTH]; - dav_sha256_final(s->sha, buf); + dav_sha256_final_free(s->sha, buf); s->sha = NULL; } else { s->error = 1; @@ -938,7 +937,7 @@ data->length); if(hstr.sha) { - dav_sha256_final(hstr.sha, (unsigned char*)data->hash); + dav_sha256_final_free(hstr.sha, (unsigned char*)data->hash); char *hash = util_hexstr((unsigned char*)data->hash, 32); dav_set_string_property_ns(res, DAV_NS, "content-hash", hash); free(hash); @@ -1599,7 +1598,7 @@ property->value = n->children ? dav_convert_xml(sn, n->children) : NULL; cxmutstr propkey = dav_property_key(property->ns->name, property->name); - cxMapPut(map, cx_hash_key_cxstr(propkey), property); + cxMapPut(map, propkey, property); cx_strfree(&propkey); } n = n->next;
--- a/libidav/utils.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/utils.c Mon Nov 10 21:52:51 2025 +0100 @@ -34,7 +34,6 @@ #include <ctype.h> #include <cx/string.h> #include <cx/buffer.h> -#include <cx/utils.h> #include <cx/printf.h> #include <libxml/tree.h> #include <curl/curl.h> @@ -348,10 +347,81 @@ return path; } +char* util_url_encode(DavSession *sn, const char *url) { + CURL *handle = sn ? sn->handle : NULL; +#if LIBCURL_VERSION_NUM < 0x075200 + int cleanup_handle = 0; + if(!handle) { + handle = curl_easy_init(); + cleanup_handle = 1; + } +#endif + + char *esc = curl_easy_escape(handle, url, strlen(url)); + char *ret = esc ? strdup(esc) : NULL; + curl_free(esc); + +#if LIBCURL_VERSION_NUM < 0x075200 + if(cleanup_handle) { + curl_easy_cleanup(handle); + } +#endif + + return ret; +} + char* util_url_decode(DavSession *sn, const char *url) { - char *unesc = curl_easy_unescape(sn->handle, url, strlen(url), NULL); - char *ret = strdup(unesc); + CURL *handle = sn ? sn->handle : NULL; +#if LIBCURL_VERSION_NUM < 0x075200 + int cleanup_handle = 0; + if(!handle) { + handle = curl_easy_init(); + cleanup_handle = 1; + } +#endif + + char *unesc = curl_easy_unescape(handle, url, strlen(url), NULL); + char *ret = unesc ? strdup(unesc) : NULL; curl_free(unesc); + +#if LIBCURL_VERSION_NUM < 0x075200 + if(cleanup_handle) { + curl_easy_cleanup(handle); + } +#endif + + return ret; +} + +cxmutstr util_url_encode_s(const CxAllocator *a, cxstring url) { + CURL *handle = NULL; +#if LIBCURL_VERSION_NUM < 0x075200 + handle = curl_easy_init(); +#endif + + char *esc = curl_easy_escape(handle, url.ptr, url.length); + cxmutstr ret = esc ? cx_strdup_a(a, cx_str(esc)) : (cxmutstr){NULL, 0}; + curl_free(esc); + +#if LIBCURL_VERSION_NUM < 0x075200 + curl_easy_cleanup(handle); +#endif + return ret; +} + +cxmutstr util_url_decode_s(const CxAllocator *a, cxstring url) { + CURL *handle = NULL; +#if LIBCURL_VERSION_NUM < 0x075200 + handle = curl_easy_init(); +#endif + + char *unesc = curl_easy_unescape(handle, url.ptr, url.length, NULL); + cxmutstr ret = unesc ? cx_strdup_a(a, cx_str(unesc)) : (cxmutstr){NULL, 0}; + curl_free(unesc); + +#if LIBCURL_VERSION_NUM < 0x075200 + curl_easy_cleanup(handle); +#endif return ret; }
--- a/libidav/utils.h Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/utils.h Mon Nov 10 21:52:51 2025 +0100 @@ -76,7 +76,10 @@ cxstring util_url_base_s(cxstring url); const char* util_url_path(const char *url); cxstring util_url_path_s(cxstring url); +char* util_url_encode(DavSession *sn, const char *url); char* util_url_decode(DavSession *sn, const char *url); +cxmutstr util_url_encode_s(const CxAllocator *a, cxstring url); +cxmutstr util_url_decode_s(const CxAllocator *a, cxstring url); const char* util_resource_name(const char *url); const char* util_resource_name_c(const char *url, char pathseparator); const char* util_path_file_name(const char *url);
--- a/libidav/webdav.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/webdav.c Mon Nov 10 21:52:51 2025 +0100 @@ -36,7 +36,6 @@ #include "session.h" #include "methods.h" #include <cx/buffer.h> -#include <cx/utils.h> #include <cx/linked_list.h> #include <cx/hash_map.h> #include <cx/compare.h> @@ -353,8 +352,8 @@ int ret = 0; dav_context_lock(context); CxList *sessions = context->sessions; - ssize_t i = cxListFind(sessions, sn); - if(i >= 0) { + size_t i = cxListFind(sessions, sn); + if(cxListIndexValid(sessions, i)) { cxListRemove(sessions, i); } else { ret = 1;
--- a/libidav/xml.c Sun Oct 19 21:20:08 2025 +0200 +++ b/libidav/xml.c Mon Nov 10 21:52:51 2025 +0100 @@ -30,7 +30,6 @@ #include <stdlib.h> #include <string.h> -#include <cx/utils.h> #include <cx/printf.h> #include "xml.h"
--- a/ucx/array_list.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/array_list.c Mon Nov 10 21:52:51 2025 +0100 @@ -50,7 +50,7 @@ } CxArrayReallocator cx_array_default_reallocator_impl = { - cx_array_default_realloc, NULL, NULL, 0, 0 + cx_array_default_realloc, NULL, NULL }; CxArrayReallocator *cx_array_default_reallocator = &cx_array_default_reallocator_impl; @@ -72,11 +72,11 @@ } // retrieve the pointer to the actual allocator - const CxAllocator *al = alloc->ptr1; + const CxAllocator *al = alloc->allocator; // check if the array is still located on the stack void *newmem; - if (array == alloc->ptr2) { + if (array == alloc->stack_ptr) { newmem = cxMalloc(al, n); if (newmem != NULL && array != NULL) { memcpy(newmem, array, old_capacity*elem_size); @@ -89,27 +89,45 @@ struct cx_array_reallocator_s cx_array_reallocator( const struct cx_allocator_s *allocator, - const void *stackmem + const void *stack_ptr ) { if (allocator == NULL) { allocator = cxDefaultAllocator; } return (struct cx_array_reallocator_s) { cx_array_advanced_realloc, - (void*) allocator, (void*) stackmem, - 0, 0 + allocator, stack_ptr, }; } // LOW LEVEL ARRAY LIST FUNCTIONS -static size_t cx_array_align_capacity( - size_t cap, - size_t alignment, - size_t max +/** + * Intelligently calculates a new capacity, reserving some more + * elements than required to prevent too many allocations. + * + * @param current_capacity the current capacity of the array + * @param needed_capacity the required capacity of the array + * @param maximum_capacity the maximum capacity (given by the data type) + * @return the new capacity + */ +static size_t cx_array_grow_capacity( + size_t current_capacity, + size_t needed_capacity, + size_t maximum_capacity ) { - if (cap > max - alignment) { - return cap; + if (current_capacity >= needed_capacity) { + return current_capacity; + } + size_t cap = needed_capacity; + size_t alignment; + if (cap < 128) alignment = 16; + else if (cap < 1024) alignment = 64; + else if (cap < 8192) alignment = 512; + else alignment = 1024; + + if (cap - 1 > maximum_capacity - alignment) { + return maximum_capacity; } else { return cap - (cap % alignment) + alignment; } @@ -177,10 +195,6 @@ // reallocate if possible if (newcap > oldcap) { - // calculate new capacity (next number divisible by 16) - newcap = cx_array_align_capacity(newcap, 16, max_size); - - // perform reallocation void *newmem = reallocator->realloc( *array, oldcap, newcap, elem_size, reallocator ); @@ -270,22 +284,18 @@ } // check if resize is required - size_t minsize = index + elem_count; - size_t newsize = oldsize < minsize ? minsize : oldsize; + const size_t minsize = index + elem_count; + const size_t newsize = oldsize < minsize ? minsize : oldsize; - // reallocate if possible - size_t newcap = oldcap; - if (newsize > oldcap) { - // check, if we need to repair the src pointer + // reallocate if necessary + const size_t newcap = cx_array_grow_capacity(oldcap, newsize, max_size); + if (newcap > oldcap) { + // check if we need to repair the src pointer uintptr_t targetaddr = (uintptr_t) *target; uintptr_t srcaddr = (uintptr_t) src; bool repairsrc = targetaddr <= srcaddr && srcaddr < targetaddr + oldcap * elem_size; - // calculate new capacity (next number divisible by 16) - newcap = cx_array_align_capacity(newsize, 16, max_size); - assert(newcap > newsize); - // perform reallocation void *newmem = reallocator->realloc( *target, oldcap, newcap, elem_size, reallocator @@ -368,16 +378,16 @@ } // store some counts - size_t old_size = *size; - size_t old_capacity = *capacity; + const size_t old_size = *size; + const size_t old_capacity = *capacity; // the necessary capacity is the worst case assumption, including duplicates - size_t needed_capacity = old_size + elem_count; + const size_t needed_capacity = cx_array_grow_capacity(old_capacity, + old_size + elem_count, SIZE_MAX); // if we need more than we have, try a reallocation if (needed_capacity > old_capacity) { - size_t new_capacity = cx_array_align_capacity(needed_capacity, 16, SIZE_MAX); void *new_mem = reallocator->realloc( - *target, old_capacity, new_capacity, elem_size, reallocator + *target, old_capacity, needed_capacity, elem_size, reallocator ); if (new_mem == NULL) { // give it up right away, there is no contract @@ -385,7 +395,7 @@ return 1; // LCOV_EXCL_LINE } *target = new_mem; - *capacity = new_capacity; + *capacity = needed_capacity; } // now we have guaranteed that we can insert everything @@ -782,8 +792,8 @@ // guarantee enough capacity if (arl->capacity < list->collection.size + n) { - size_t new_capacity = list->collection.size + n; - new_capacity = new_capacity - (new_capacity % 16) + 16; + const size_t new_capacity = cx_array_grow_capacity(arl->capacity, + list->collection.size + n, SIZE_MAX); if (cxReallocateArray( list->collection.allocator, &arl->data, new_capacity, @@ -881,7 +891,7 @@ const void *elem, int prepend ) { - struct cx_list_s *list = iter->src_handle.m; + struct cx_list_s *list = iter->src_handle; if (iter->index < list->collection.size) { if (cx_arl_insert_element(list, iter->index + 1 - prepend, elem) == NULL) { @@ -1092,7 +1102,7 @@ static bool cx_arl_iter_valid(const void *it) { const struct cx_iterator_s *iter = it; - const struct cx_list_s *list = iter->src_handle.c; + const struct cx_list_s *list = iter->src_handle; return iter->index < list->collection.size; } @@ -1105,31 +1115,39 @@ struct cx_iterator_s *iter = it; if (iter->base.remove) { iter->base.remove = false; - cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL); + cx_arl_remove(iter->src_handle, iter->index, 1, NULL); iter->elem_count--; } else { iter->index++; iter->elem_handle = ((char *) iter->elem_handle) - + ((const struct cx_list_s *) iter->src_handle.c)->collection.elem_size; + + ((const struct cx_list_s *) iter->src_handle)->collection.elem_size; } } static void cx_arl_iter_prev(void *it) { struct cx_iterator_s *iter = it; - const cx_array_list *list = iter->src_handle.c; if (iter->base.remove) { iter->base.remove = false; - cx_arl_remove(iter->src_handle.m, iter->index, 1, NULL); + cx_arl_remove(iter->src_handle, iter->index, 1, NULL); iter->elem_count--; } iter->index--; + cx_array_list *list = iter->src_handle; if (iter->index < list->base.collection.size) { iter->elem_handle = ((char *) list->data) + iter->index * list->base.collection.elem_size; } } +static int cx_arl_change_capacity( + struct cx_list_s *list, + size_t new_capacity +) { + cx_array_list *arl = (cx_array_list *)list; + return cxReallocateArray(list->collection.allocator, + &arl->data, new_capacity, list->collection.elem_size); +} static struct cx_iterator_s cx_arl_iterator( const struct cx_list_s *list, @@ -1139,7 +1157,7 @@ struct cx_iterator_s iter; iter.index = index; - iter.src_handle.c = list; + iter.src_handle = (void*)list; iter.elem_handle = cx_arl_at(list, index); iter.elem_size = list->collection.elem_size; iter.elem_count = list->collection.size; @@ -1147,7 +1165,7 @@ iter.base.current = cx_arl_iter_current; iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next; iter.base.remove = false; - iter.base.mutating = false; + iter.base.allow_remove = true; return iter; } @@ -1167,6 +1185,7 @@ cx_arl_sort, cx_arl_compare, cx_arl_reverse, + cx_arl_change_capacity, cx_arl_iterator, };
--- a/ucx/cx/allocator.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/allocator.h Mon Nov 10 21:52:51 2025 +0100 @@ -46,36 +46,22 @@ /** * The allocator's malloc() implementation. */ - void *(*malloc)( - void *data, - size_t n - ); + void *(*malloc)(void *data, size_t n); /** * The allocator's realloc() implementation. */ - void *(*realloc)( - void *data, - void *mem, - size_t n - ); + void *(*realloc)(void *data, void *mem, size_t n); /** * The allocator's calloc() implementation. */ - void *(*calloc)( - void *data, - size_t nmemb, - size_t size - ); + void *(*calloc)(void *data, size_t nmemb, size_t size); /** * The allocator's free() implementation. */ - void (*free)( - void *data, - void *mem - ); + void (*free)(void *data, void *mem); } cx_allocator_class; /** @@ -100,15 +86,13 @@ /** * A pre-defined allocator using standard library malloc() etc. */ -cx_attr_export -extern const CxAllocator * const cxStdlibAllocator; +CX_EXPORT extern const CxAllocator * const cxStdlibAllocator; /** * The default allocator that is used by UCX. * Initialized with cxStdlibAllocator, but you may change it. */ -cx_attr_export -extern const CxAllocator * cxDefaultAllocator; +CX_EXPORT extern const CxAllocator * cxDefaultAllocator; /** * Function pointer type for destructor functions. @@ -133,10 +117,33 @@ * @param data an optional pointer to custom data * @param memory a pointer to the object to destruct */ -typedef void (*cx_destructor_func2)( - void *data, - void *memory -); +typedef void (*cx_destructor_func2)(void *data, void *memory); + + +/** + * Function pointer type for clone functions. + * + * A clone function is supposed to create a deep copy of the memory pointed to + * by the @p source pointer. + * If the @p target pointer is non-null, the clone function is supposed to store + * the copy into that memory region. + * Otherwise, the clone function shall use the specified @p allocator to create + * a new object. + * + * The return value of a clone function is always a pointer to the target + * memory region, or @c NULL if any allocation failed. + * A clone function SHOULD NOT fail for any other reason than an allocation + * failure. + * + * @param target the target memory or @c NULL, if memory shall be allocated + * @param source the source memory + * @param allocator the allocator that shall be used + * @param data optional additional data + * @return either the specified @p target, a pointer to the allocated memory, + * or @c NULL, if any error occurred + */ +typedef void*(cx_clone_func)(void *target, const void *source, + const CxAllocator *allocator, void *data); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -153,13 +160,8 @@ * @retval non-zero failure * @see cx_reallocatearray() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_reallocate_( - void **mem, - size_t n -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_reallocate_(void **mem, size_t n); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -180,14 +182,8 @@ * @retval non-zero failure * @see cx_reallocate() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -int cx_reallocatearray_( - void **mem, - size_t nmemb, - size_t size -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_reallocatearray_(void **mem, size_t nmemb, size_t size); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -244,11 +240,7 @@ * @param mem a pointer to the block to free */ cx_attr_nonnull_arg(1) -cx_attr_export -void cxFree( - const CxAllocator *allocator, - void *mem -); +CX_EXPORT void cxFree(const CxAllocator *allocator, void *mem); /** * Allocate @p n bytes of memory. @@ -257,16 +249,9 @@ * @param n the number of bytes * @return a pointer to the allocated memory */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_malloc -cx_attr_dealloc_ucx -cx_attr_allocsize(2) -cx_attr_export -void *cxMalloc( - const CxAllocator *allocator, - size_t n -); +cx_attr_nodiscard cx_attr_nonnull +cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2) +CX_EXPORT void *cxMalloc(const CxAllocator *allocator, size_t n); /** * Reallocate the previously allocated block in @p mem, making the new block @@ -281,16 +266,9 @@ * @param n the new size in bytes * @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, - size_t n -); +cx_attr_nodiscard cx_attr_nonnull_arg(1) +cx_attr_dealloc_ucx cx_attr_allocsize(3) +CX_EXPORT void *cxRealloc(const CxAllocator *allocator, void *mem, size_t n); /** * Reallocate the previously allocated block in @p mem, making the new block @@ -310,17 +288,10 @@ * @param size the size of each element * @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, - size_t nmemb, - size_t size -); +cx_attr_nodiscard cx_attr_nonnull_arg(1) +cx_attr_dealloc_ucx cx_attr_allocsize(3, 4) +CX_EXPORT void *cxReallocArray(const CxAllocator *allocator, + void *mem, size_t nmemb, size_t size); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -338,14 +309,8 @@ * @retval zero success * @retval non-zero failure */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -int cxReallocate_( - const CxAllocator *allocator, - void **mem, - size_t n -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cxReallocate_(const CxAllocator *allocator, void **mem, size_t n); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -385,15 +350,9 @@ * @retval zero success * @retval non-zero on failure */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -int cxReallocateArray_( - const CxAllocator *allocator, - void **mem, - size_t nmemb, - size_t size -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cxReallocateArray_(const CxAllocator *allocator, + void **mem, size_t nmemb, size_t size); /** * Reallocate a previously allocated block and changes the pointer in-place, @@ -425,17 +384,9 @@ * @param size the size of each element in bytes * @return a pointer to the allocated memory */ -cx_attr_nonnull_arg(1) -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc_ucx -cx_attr_allocsize(2, 3) -cx_attr_export -void *cxCalloc( - const CxAllocator *allocator, - size_t nmemb, - size_t size -); +cx_attr_nonnull_arg(1) cx_attr_nodiscard +cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2, 3) +CX_EXPORT void *cxCalloc(const CxAllocator *allocator, size_t nmemb, size_t size); /** * Allocate @p n bytes of memory and sets every byte to zero. @@ -444,16 +395,9 @@ * @param n the number of bytes * @return a pointer to the allocated memory */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_malloc -cx_attr_dealloc_ucx -cx_attr_allocsize(2) -cx_attr_export -void *cxZalloc( - const CxAllocator *allocator, - size_t n -); +cx_attr_nodiscard cx_attr_nonnull +cx_attr_malloc cx_attr_dealloc_ucx cx_attr_allocsize(2) +CX_EXPORT void *cxZalloc(const CxAllocator *allocator, size_t n); /** * Convenience macro that invokes cxMalloc() with the cxDefaultAllocator.
--- a/ucx/cx/array_list.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/array_list.h Mon Nov 10 21:52:51 2025 +0100 @@ -47,8 +47,7 @@ * The maximum item size in an array list that fits into * a stack buffer when swapped. */ -cx_attr_export -extern const unsigned cx_array_swap_sbo_size; +CX_EXPORT extern const unsigned cx_array_swap_sbo_size; /** * Declares variables for an array that can be used with the convenience macros. @@ -172,10 +171,9 @@ * Reallocates space for the given array. * * Implementations are not required to free the original array. - * This allows reallocation of static memory by allocating heap memory - * and copying the array contents. The information in the custom fields of - * the referenced allocator can be used to track the state of the memory - * or to transport other additional data. + * This allows reallocation of static or stack memory by allocating heap memory + * and copying the array contents; namely when @c stack_ptr in this struct + * is not @c NULL and @p array equals @c stack_ptr. * * @param array the array to reallocate * @param old_capacity the old number of elements @@ -184,33 +182,18 @@ * @param alloc a reference to this allocator * @return a pointer to the reallocated memory or @c NULL on failure */ - cx_attr_nodiscard - cx_attr_nonnull_arg(5) - cx_attr_allocsize(3, 4) - void *(*realloc)( - void *array, - size_t old_capacity, - size_t new_capacity, - size_t elem_size, - struct cx_array_reallocator_s *alloc - ); + void *(*realloc)( void *array, size_t old_capacity, size_t new_capacity, + size_t elem_size, struct cx_array_reallocator_s *alloc); /** - * Custom data pointer. + * The allocator that shall be used for the reallocations. */ - void *ptr1; - /** - * Custom data pointer. - */ - void *ptr2; + const CxAllocator *allocator; /** - * Custom data integer. + * Optional pointer to stack memory + * if the array is originally located on the stack. */ - size_t int1; - /** - * Custom data integer. - */ - size_t int2; + const void *stack_ptr; }; /** @@ -221,32 +204,28 @@ /** * A default array reallocator that is based on the cxDefaultAllocator. */ -cx_attr_export -extern CxArrayReallocator *cx_array_default_reallocator; +CX_EXPORT extern CxArrayReallocator *cx_array_default_reallocator; /** * Creates a new array reallocator. * * When @p allocator is @c NULL, the cxDefaultAllocator will be used. * - * When @p stackmem is not @c NULL, the reallocator is supposed to be used - * @em only for the specific array initially located at @p stackmem. + * When @p stack_ptr is not @c NULL, the reallocator is supposed to be used + * @em only for the specific array initially located at @p stack_ptr. * When reallocation is needed, the reallocator checks if the array is - * still located at @p stackmem and copies the contents to the heap. + * still located at @p stack_ptr and copies the contents to the heap. * * @note Invoking this function with both arguments being @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 + * @param stack_ptr the address of the array when the array is initially located * on the stack or shall not reallocate in place * @return an array reallocator */ -cx_attr_export -CxArrayReallocator cx_array_reallocator( - const struct cx_allocator_s *allocator, - const void *stackmem -); +CX_EXPORT CxArrayReallocator cx_array_reallocator( + const struct cx_allocator_s *allocator, const void *stack_ptr); /** * Reserves memory for additional elements. @@ -266,6 +245,9 @@ * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit * architecture. If set to zero, the native word width is used. * + * @note This function will reserve the minimum required capacity to hold + * the additional elements and does not perform an overallocation. + * * @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 @@ -279,16 +261,9 @@ * @see cx_array_reallocator() */ cx_attr_nonnull_arg(1, 2, 3) -cx_attr_export -int cx_array_reserve( - void **array, - void *size, - void *capacity, - unsigned width, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -); +CX_EXPORT int cx_array_reserve(void **array, void *size, void *capacity, + unsigned width, size_t elem_size, size_t elem_count, + CxArrayReallocator *reallocator); /** * Copies elements from one array to another. @@ -308,6 +283,9 @@ * Supported are 0, 1, 2, and 4, as well as 8 if running on a 64-bit * architecture. If set to zero, the native word width is used. * + * @note When this function does reallocate the array, it may allocate more + * space than required to avoid further allocations in the near future. + * * @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 @@ -321,20 +299,12 @@ * @retval zero success * @retval non-zero failure * @see cx_array_reallocator() + * @see cx_array_reserve() */ cx_attr_nonnull_arg(1, 2, 3, 6) -cx_attr_export -int cx_array_copy( - void **target, - void *size, - void *capacity, - unsigned width, - size_t index, - const void *src, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -); +CX_EXPORT int cx_array_copy(void **target, void *size, void *capacity, unsigned width, + size_t index, const void *src, size_t elem_size, size_t elem_count, + CxArrayReallocator *reallocator); /** * Convenience macro that uses cx_array_copy() with a default layout and @@ -482,17 +452,9 @@ * @retval non-zero failure */ cx_attr_nonnull_arg(1, 2, 3, 5) -cx_attr_export -int cx_array_insert_sorted( - void **target, - size_t *size, - size_t *capacity, - cx_compare_func cmp_func, - const void *src, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -); +CX_EXPORT int cx_array_insert_sorted(void **target, size_t *size, size_t *capacity, + cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count, + CxArrayReallocator *reallocator); /** * Inserts an element into a sorted array. @@ -500,7 +462,7 @@ * If the target array is not already sorted with respect * to the specified @p cmp_func, the behavior is undefined. * - * If the capacity is insufficient to hold the new data, a reallocation + * If the capacity is not enough to hold the new data, a reallocation * attempt is made. * * The \@ SIZE_TYPE is flexible and can be any unsigned integer type. @@ -611,17 +573,9 @@ * @retval non-zero failure */ cx_attr_nonnull_arg(1, 2, 3, 5) -cx_attr_export -int cx_array_insert_unique( - void **target, - size_t *size, - size_t *capacity, - cx_compare_func cmp_func, - const void *src, - size_t elem_size, - size_t elem_count, - CxArrayReallocator *reallocator -); +CX_EXPORT int cx_array_insert_unique(void **target, size_t *size, size_t *capacity, + cx_compare_func cmp_func, const void *src, size_t elem_size, size_t elem_count, + CxArrayReallocator *reallocator); /** * Inserts an element into a sorted array if it does not exist. @@ -738,14 +692,8 @@ * @see cx_array_binary_search() */ cx_attr_nonnull -cx_attr_export -size_t cx_array_binary_search_inf( - const void *arr, - size_t size, - size_t elem_size, - const void *elem, - cx_compare_func cmp_func -); +CX_EXPORT size_t cx_array_binary_search_inf(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func cmp_func); /** * Searches an item in a sorted array. @@ -764,14 +712,8 @@ * @see cx_array_binary_search_sup() */ cx_attr_nonnull -cx_attr_export -size_t cx_array_binary_search( - const void *arr, - size_t size, - size_t elem_size, - const void *elem, - cx_compare_func cmp_func -); +CX_EXPORT size_t cx_array_binary_search(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func cmp_func); /** * Searches the smallest upper bound in a sorted array. @@ -796,14 +738,8 @@ * @see cx_array_binary_search() */ cx_attr_nonnull -cx_attr_export -size_t cx_array_binary_search_sup( - const void *arr, - size_t size, - size_t elem_size, - const void *elem, - cx_compare_func cmp_func -); +CX_EXPORT size_t cx_array_binary_search_sup(const void *arr, size_t size, + size_t elem_size, const void *elem, cx_compare_func cmp_func); /** * Swaps two array elements. @@ -814,13 +750,7 @@ * @param idx2 index of the second element */ cx_attr_nonnull -cx_attr_export -void cx_array_swap( - void *arr, - size_t elem_size, - size_t idx1, - size_t idx2 -); +CX_EXPORT void cx_array_swap(void *arr, size_t elem_size, size_t idx1, size_t idx2); /** * Allocates an array list for storing elements with @p elem_size bytes each. @@ -841,13 +771,8 @@ cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) -cx_attr_export -CxList *cxArrayListCreate( - const CxAllocator *allocator, - cx_compare_func comparator, - size_t elem_size, - size_t initial_capacity -); +CX_EXPORT CxList *cxArrayListCreate(const CxAllocator *allocator, + cx_compare_func comparator, size_t elem_size, size_t initial_capacity); /** * Allocates an array list for storing elements with @p elem_size bytes each.
--- a/ucx/cx/buffer.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/buffer.h Mon Nov 10 21:52:51 2025 +0100 @@ -227,14 +227,8 @@ * @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, - size_t capacity, - const CxAllocator *allocator, - int flags -); +CX_EXPORT int cxBufferInit(CxBuffer *buffer, void *space, size_t capacity, + const CxAllocator *allocator, int flags); /** * Configures the buffer for flushing. @@ -251,11 +245,7 @@ * @see cxBufferWrite() */ cx_attr_nonnull -cx_attr_export -int cxBufferEnableFlushing( - CxBuffer *buffer, - CxBufferFlushConfig config -); +CX_EXPORT int cxBufferEnableFlushing(CxBuffer *buffer, CxBufferFlushConfig config); /** * Destroys the buffer contents. @@ -267,8 +257,7 @@ * @see cxBufferInit() */ cx_attr_nonnull -cx_attr_export -void cxBufferDestroy(CxBuffer *buffer); +CX_EXPORT void cxBufferDestroy(CxBuffer *buffer); /** * Deallocates the buffer. @@ -279,8 +268,7 @@ * @param buffer the buffer to deallocate * @see cxBufferCreate() */ -cx_attr_export -void cxBufferFree(CxBuffer *buffer); +CX_EXPORT void cxBufferFree(CxBuffer *buffer); /** * Allocates and initializes a fresh buffer. @@ -306,16 +294,9 @@ * @param flags buffer features (see cx_buffer_s.flags) * @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, - const CxAllocator *allocator, - int flags -); +cx_attr_malloc cx_attr_dealloc(cxBufferFree, 1) cx_attr_nodiscard +CX_EXPORT CxBuffer *cxBufferCreate(void *space, size_t capacity, + const CxAllocator *allocator, int flags); /** * Shifts the contents of the buffer by the given offset. @@ -354,11 +335,7 @@ * @see cxBufferShiftRight() */ cx_attr_nonnull -cx_attr_export -int cxBufferShift( - CxBuffer *buffer, - off_t shift -); +CX_EXPORT int cxBufferShift(CxBuffer *buffer, off_t shift); /** * Shifts the buffer to the right. @@ -371,11 +348,7 @@ * @see cxBufferShift() */ cx_attr_nonnull -cx_attr_export -int cxBufferShiftRight( - CxBuffer *buffer, - size_t shift -); +CX_EXPORT int cxBufferShiftRight(CxBuffer *buffer, size_t shift); /** * Shifts the buffer to the left. @@ -388,11 +361,7 @@ * @see cxBufferShift() */ cx_attr_nonnull -cx_attr_export -int cxBufferShiftLeft( - CxBuffer *buffer, - size_t shift -); +CX_EXPORT int cxBufferShiftLeft(CxBuffer *buffer, size_t shift); /** @@ -416,12 +385,7 @@ * */ cx_attr_nonnull -cx_attr_export -int cxBufferSeek( - CxBuffer *buffer, - off_t offset, - int whence -); +CX_EXPORT int cxBufferSeek(CxBuffer *buffer, off_t offset, int whence); /** * Clears the buffer by resetting the position and deleting the data. @@ -436,8 +400,7 @@ * @see cxBufferReset() */ cx_attr_nonnull -cx_attr_export -void cxBufferClear(CxBuffer *buffer); +CX_EXPORT void cxBufferClear(CxBuffer *buffer); /** * Resets the buffer by resetting the position and size to zero. @@ -449,8 +412,7 @@ * @see cxBufferClear() */ cx_attr_nonnull -cx_attr_export -void cxBufferReset(CxBuffer *buffer); +CX_EXPORT void cxBufferReset(CxBuffer *buffer); /** * Tests, if the buffer position has exceeded the buffer size. @@ -460,10 +422,8 @@ * byte of the buffer's contents * @retval false otherwise */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -bool cxBufferEof(const CxBuffer *buffer); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT bool cxBufferEof(const CxBuffer *buffer); /** @@ -481,11 +441,7 @@ * @see cxBufferShrink() */ cx_attr_nonnull -cx_attr_export -int cxBufferMinimumCapacity( - CxBuffer *buffer, - size_t capacity -); +CX_EXPORT int cxBufferMinimumCapacity(CxBuffer *buffer, size_t capacity); /** * Shrinks the capacity of the buffer to fit its current size. @@ -504,11 +460,7 @@ * @see cxBufferMinimumCapacity() */ cx_attr_nonnull -cx_attr_export -void cxBufferShrink( - CxBuffer *buffer, - size_t reserve -); +CX_EXPORT void cxBufferShrink(CxBuffer *buffer, size_t reserve); /** * Writes data to a CxBuffer. @@ -552,13 +504,8 @@ * @see cxBufferRead() */ cx_attr_nonnull -cx_attr_export -size_t cxBufferWrite( - const void *ptr, - size_t size, - size_t nitems, - CxBuffer *buffer -); +CX_EXPORT size_t cxBufferWrite(const void *ptr, size_t size, + size_t nitems, CxBuffer *buffer); /** * Appends data to a CxBuffer. @@ -580,13 +527,8 @@ * @see cxBufferRead() */ cx_attr_nonnull -cx_attr_export -size_t cxBufferAppend( - const void *ptr, - size_t size, - size_t nitems, - CxBuffer *buffer -); +CX_EXPORT size_t cxBufferAppend(const void *ptr, size_t size, + size_t nitems, CxBuffer *buffer); /** * Performs a single flush-run on the specified buffer. @@ -642,8 +584,7 @@ * @see cxBufferEnableFlushing() */ cx_attr_nonnull -cx_attr_export -size_t cxBufferFlush(CxBuffer *buffer); +CX_EXPORT size_t cxBufferFlush(CxBuffer *buffer); /** * Reads data from a CxBuffer. @@ -661,13 +602,8 @@ * @see cxBufferAppend() */ cx_attr_nonnull -cx_attr_export -size_t cxBufferRead( - void *ptr, - size_t size, - size_t nitems, - CxBuffer *buffer -); +CX_EXPORT size_t cxBufferRead(void *ptr, size_t size, + size_t nitems, CxBuffer *buffer); /** * Writes a character to a buffer. @@ -689,11 +625,7 @@ * @see cxBufferTerminate() */ cx_attr_nonnull -cx_attr_export -int cxBufferPut( - CxBuffer *buffer, - int c -); +CX_EXPORT int cxBufferPut(CxBuffer *buffer, int c); /** * Writes a terminating zero to a buffer at the current position. @@ -707,8 +639,7 @@ * @return zero, if the terminator could be written, non-zero otherwise */ cx_attr_nonnull -cx_attr_export -int cxBufferTerminate(CxBuffer *buffer); +CX_EXPORT int cxBufferTerminate(CxBuffer *buffer); /** * Writes a string to a buffer. @@ -719,13 +650,8 @@ * @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 -); +cx_attr_nonnull cx_attr_cstr_arg(2) +CX_EXPORT size_t cxBufferPutString(CxBuffer *buffer, const char *str); /** * Gets a character from a buffer. @@ -736,8 +662,7 @@ * @return the character or @c EOF, if the end of the buffer is reached */ cx_attr_nonnull -cx_attr_export -int cxBufferGet(CxBuffer *buffer); +CX_EXPORT int cxBufferGet(CxBuffer *buffer); #ifdef __cplusplus }
--- a/ucx/cx/collection.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/collection.h Mon Nov 10 21:52:51 2025 +0100 @@ -151,7 +151,15 @@ * @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) +#define cxCollectionSorted(c) ((c)->collection.sorted || (c)->collection.size == 0) + +/** + * Sets the compare function for a collection. + * + * @param c a pointer to a struct that contains #CX_COLLECTION_BASE + * @param func (@c cx_compare_func) the compare function that shall be used by @c c + */ +#define cxCollectionCompareFunc(c, func) (c)->collection.cmpfunc = (func) /** * Sets a simple destructor function for this collection.
--- a/ucx/cx/common.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/common.h Mon Nov 10 21:52:51 2025 +0100 @@ -265,15 +265,32 @@ #define _Thread_local __declspec(thread) #endif // _MSC_VER +// --------------------------------------------------------------------------- +// Exported and inlined functions +// --------------------------------------------------------------------------- + #if defined(CX_WINDLL_EXPORT) -#define cx_attr_export __declspec(dllexport) +#define CX_EXPORT __declspec(dllexport) #elif defined(CX_WINDLL) -#define cx_attr_export __declspec(dllimport) +#define CX_EXPORT __declspec(dllimport) #else /** Only used for building Windows DLLs. */ -#define cx_attr_export +#define CX_EXPORT #endif // CX_WINDLL / CX_WINDLL_EXPORT +#ifdef __GNUC__ +/** + * Declares a function to be inlined. + */ +#define CX_INLINE __attribute__((always_inline)) static inline +#else +#define CX_INLINE static inline +#endif +/** + * Declares a compatibility function for C++ builds. + */ +#define CX_CPPDECL static inline + // --------------------------------------------------------------------------- // Useful function pointers // --------------------------------------------------------------------------- @@ -281,22 +298,12 @@ /** * Function pointer compatible with fwrite-like functions. */ -typedef size_t (*cx_write_func)( - const void *, - size_t, - size_t, - void * -); +typedef size_t (*cx_write_func)(const void*, size_t, size_t, void*); /** * Function pointer compatible with fread-like functions. */ -typedef size_t (*cx_read_func)( - void *, - size_t, - size_t, - void * -); +typedef size_t (*cx_read_func)(void*, size_t, size_t, void*); // --------------------------------------------------------------------------- // Utility macros @@ -348,9 +355,7 @@ #if __cplusplus extern "C" #endif -cx_attr_export int cx_szmul_impl(size_t a, size_t b, size_t *result); +CX_EXPORT int cx_szmul_impl(size_t a, size_t b, size_t *result); #endif // cx_szmul - - #endif // UCX_COMMON_H
--- a/ucx/cx/common.h.orig Sun Oct 19 21:20:08 2025 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file common.h - * - * \brief Common definitions and feature checks. - * - * \author Mike Becker - * \author Olaf Wintermann - * \version 3.0 - * \copyright 2-Clause BSD License - * - * \mainpage UAP Common Extensions - * Library with common and useful functions, macros and data structures. - * <p> - * Latest available source:<br> - * <a href="https://sourceforge.net/projects/ucx/files/">https://sourceforge.net/projects/ucx/files/</a> - * </p> - * - * <p> - * Repositories:<br> - * <a href="https://sourceforge.net/p/ucx/code">https://sourceforge.net/p/ucx/code</a> - * - or - - * <a href="https://develop.uap-core.de/hg/ucx">https://develop.uap-core.de/hg/ucx</a> - * </p> - * - * <h2>LICENCE</h2> - * - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef UCX_COMMON_H -#define UCX_COMMON_H - -/** Major UCX version as integer constant. */ -#define UCX_VERSION_MAJOR 3 - -/** Minor UCX version as integer constant. */ -#define UCX_VERSION_MINOR 0 - -/** Version constant which ensures to increase monotonically. */ -#define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR) - -#define __attribute__(...) - -#include <stdlib.h> -#include <stddef.h> -#include <stdbool.h> -#include <stdint.h> - -/** - * Function pointer compatible with fwrite-like functions. - */ -typedef size_t (*cx_write_func)( - void const *, - size_t, - size_t, - void * -); - -/** - * Function pointer compatible with fread-like functions. - */ -typedef size_t (*cx_read_func)( - void *, - size_t, - size_t, - void * -); - -#ifdef _WIN32 - -#ifdef __MINGW32__ -#include <sys/types.h> -#endif // __MINGW32__ - -#else // !_WIN32 - -#include <sys/types.h> - -#endif // _WIN32 - -#ifndef __GNUC__ -/** - * Removes GNU C attributes where they are not supported. - */ -#define __attribute__(x) -#endif - -#endif // UCX_COMMON_H
--- a/ucx/cx/compare.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/compare.h Mon Nov 10 21:52:51 2025 +0100 @@ -54,13 +54,7 @@ * can be used, but they are NOT compatible with this function * pointer. */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -typedef int (*cx_compare_func)( - const void *left, - const void *right -); +typedef int (*cx_compare_func)(const void *left, const void *right); /** * Compares two integers of type int. @@ -74,10 +68,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_int(const void *i1, const void *i2); /** * Compares two integers of type int. @@ -89,8 +81,7 @@ * @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); +CX_EXPORT int cx_vcmp_int(int i1, int i2); /** * Compares two integers of type long int. @@ -104,10 +95,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_longint(const void *i1, const void *i2); /** * Compares two integers of type long int. @@ -119,8 +108,7 @@ * @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); +CX_EXPORT int cx_vcmp_longint(long int i1, long int i2); /** * Compares two integers of type long long. @@ -134,10 +122,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_longlong(const void *i1, const void *i2); /** * Compares two integers of type long long. @@ -149,8 +135,7 @@ * @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); +CX_EXPORT int cx_vcmp_longlong(long long int i1, long long int i2); /** * Compares two integers of type int16_t. @@ -164,10 +149,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_int16(const void *i1, const void *i2); /** * Compares two integers of type int16_t. @@ -179,8 +162,7 @@ * @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); +CX_EXPORT int cx_vcmp_int16(int16_t i1, int16_t i2); /** * Compares two integers of type int32_t. @@ -194,10 +176,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_int32(const void *i1, const void *i2); /** * Compares two integers of type int32_t. @@ -209,8 +189,7 @@ * @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); +CX_EXPORT int cx_vcmp_int32(int32_t i1, int32_t i2); /** * Compares two integers of type int64_t. @@ -224,10 +203,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_int64(const void *i1, const void *i2); /** * Compares two integers of type int64_t. @@ -239,8 +216,7 @@ * @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); +CX_EXPORT int cx_vcmp_int64(int64_t i1, int64_t i2); /** * Compares two integers of type unsigned int. @@ -254,10 +230,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uint(const void *i1, const void *i2); /** * Compares two integers of type unsigned int. @@ -269,8 +243,7 @@ * @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); +CX_EXPORT int cx_vcmp_uint(unsigned int i1, unsigned int i2); /** * Compares two integers of type unsigned long int. @@ -284,10 +257,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_ulongint(const void *i1, const void *i2); /** * Compares two integers of type unsigned long int. @@ -299,8 +270,7 @@ * @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); +CX_EXPORT int cx_vcmp_ulongint(unsigned long int i1, unsigned long int i2); /** * Compares two integers of type unsigned long long. @@ -314,10 +284,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_ulonglong(const void *i1, const void *i2); /** * Compares two integers of type unsigned long long. @@ -329,8 +297,7 @@ * @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); +CX_EXPORT int cx_vcmp_ulonglong(unsigned long long int i1, unsigned long long int i2); /** * Compares two integers of type uint16_t. @@ -344,10 +311,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uint16(const void *i1, const void *i2); /** * Compares two integers of type uint16_t. @@ -359,8 +324,7 @@ * @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); +CX_EXPORT int cx_vcmp_uint16(uint16_t i1, uint16_t i2); /** * Compares two integers of type uint32_t. @@ -374,10 +338,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uint32(const void *i1, const void *i2); /** * Compares two integers of type uint32_t. @@ -389,8 +351,7 @@ * @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); +CX_EXPORT int cx_vcmp_uint32(uint32_t i1, uint32_t i2); /** * Compares two integers of type uint64_t. @@ -404,10 +365,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uint64(const void *i1, const void *i2); /** * Compares two integers of type uint64_t. @@ -419,8 +378,7 @@ * @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); +CX_EXPORT int cx_vcmp_uint64(uint64_t i1, uint64_t i2); /** * Compares two integers of type size_t. @@ -434,10 +392,8 @@ * @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_size(const void *i1, const void *i2); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_size(const void *i1, const void *i2); /** * Compares two integers of type size_t. @@ -449,8 +405,7 @@ * @retval 1 if the left argument is greater than the right argument */ cx_attr_nodiscard -cx_attr_export -int cx_vcmp_size(size_t i1, size_t i2); +CX_EXPORT int cx_vcmp_size(size_t i1, size_t i2); /** * Compares two real numbers of type float with precision 1e-6f. @@ -464,10 +419,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_float(const void *f1, const void *f2); /** * Compares two real numbers of type float with precision 1e-6f. @@ -479,8 +432,7 @@ * @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); +CX_EXPORT int cx_vcmp_float(float f1, float f2); /** * Compares two real numbers of type double with precision 1e-14. @@ -494,10 +446,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_double(const void *d1, const void *d2); /** * Compares two real numbers of type double with precision 1e-14. @@ -509,8 +459,7 @@ * @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); +CX_EXPORT int cx_vcmp_double(double d1, double d2); /** * Compares the integer representation of two pointers. @@ -524,10 +473,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_intptr(const void *ptr1, const void *ptr2); /** * Compares the integer representation of two pointers. @@ -539,8 +486,7 @@ * @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); +CX_EXPORT int cx_vcmp_intptr(intptr_t ptr1, intptr_t ptr2); /** * Compares the unsigned integer representation of two pointers. @@ -554,10 +500,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_uintptr(const void *ptr1, const void *ptr2); /** * Compares the unsigned integer representation of two pointers. @@ -569,8 +513,7 @@ * @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); +CX_EXPORT int cx_vcmp_uintptr(uintptr_t ptr1, uintptr_t ptr2); /** * Compares the pointers specified in the arguments without dereferencing. @@ -581,10 +524,8 @@ * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cx_cmp_ptr(const void *ptr1, const void *ptr2); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/hash_key.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/hash_key.h Mon Nov 10 21:52:51 2025 +0100 @@ -79,8 +79,7 @@ * @see cx_hash_key() */ cx_attr_nonnull -cx_attr_export -void cx_hash_murmur(CxHashKey *key); +CX_EXPORT void cx_hash_murmur(CxHashKey *key); /** * Mixes up a 32-bit integer to be used as a hash. @@ -90,8 +89,7 @@ * @param x the integer * @return the hash */ -cx_attr_export -uint32_t cx_hash_u32(uint32_t x); +CX_EXPORT uint32_t cx_hash_u32(uint32_t x); /** * Mixes up a 64-bit integer to be used as a hash. @@ -101,8 +99,7 @@ * @param x the integer * @return the hash */ -cx_attr_export -uint64_t cx_hash_u64(uint64_t x); +CX_EXPORT uint64_t cx_hash_u64(uint64_t x); /** * Computes a hash key from a 32-bit integer. @@ -111,8 +108,7 @@ * @return the hash key */ cx_attr_nodiscard -cx_attr_export -CxHashKey cx_hash_key_u32(uint32_t x); +CX_EXPORT CxHashKey cx_hash_key_u32(uint32_t x); /** * Computes a hash key from a 64-bit integer. @@ -121,8 +117,7 @@ * @return the hash key */ cx_attr_nodiscard -cx_attr_export -CxHashKey cx_hash_key_u64(uint64_t x); +CX_EXPORT CxHashKey cx_hash_key_u64(uint64_t x); /** * Computes a hash key from a string. @@ -132,10 +127,8 @@ * @param str the string * @return the hash key */ -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -CxHashKey cx_hash_key_str(const char *str); +cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT CxHashKey cx_hash_key_str(const char *str); /** * Computes a hash key from a string. @@ -148,12 +141,8 @@ * @param str the string * @return the hash key */ -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -static inline CxHashKey cx_hash_key_ustr(const unsigned char *str) { - return cx_hash_key_str((const char*)str); -} +cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT CxHashKey cx_hash_key_ustr(const unsigned char *str); /** * Computes a hash key from a byte array. @@ -162,13 +151,8 @@ * @param len the length * @return the hash key */ -cx_attr_nodiscard -cx_attr_access_r(1, 2) -cx_attr_export -CxHashKey cx_hash_key_bytes( - const unsigned char *bytes, - size_t len -); +cx_attr_nodiscard cx_attr_access_r(1, 2) +CX_EXPORT CxHashKey cx_hash_key_bytes(const unsigned char *bytes, size_t len); /** * Computes a hash key for an arbitrary object. @@ -183,11 +167,7 @@ */ cx_attr_nodiscard cx_attr_access_r(1, 2) -cx_attr_export -CxHashKey cx_hash_key( - const void *obj, - size_t len -); +CX_EXPORT CxHashKey cx_hash_key(const void *obj, size_t len); /** * Computes a hash key from a UCX string. @@ -196,9 +176,7 @@ * @return the hash key */ cx_attr_nodiscard -static inline CxHashKey cx_hash_key_cxstr(cxstring str) { - return cx_hash_key(str.ptr, str.length); -} +CX_EXPORT CxHashKey cx_hash_key_cxstr(cxstring str); /** * Computes a hash key from a UCX string. @@ -207,9 +185,7 @@ * @return the hash key */ cx_attr_nodiscard -static inline CxHashKey cx_hash_key_mutstr(cxmutstr str) { - return cx_hash_key(str.ptr, str.length); -} +CX_EXPORT CxHashKey cx_hash_key_mutstr(cxmutstr str); /** * The identity function for the CX_HASH_KEY() macro. @@ -219,7 +195,7 @@ * @return a copy of the key */ cx_attr_nodiscard -static inline CxHashKey cx_hash_key_identity(CxHashKey key) { +CX_INLINE CxHashKey cx_hash_key_identity(CxHashKey key) { return key; } @@ -249,25 +225,16 @@ #endif // __cplusplus /** - * Computes a hash key from a UCX string. - * Convenience macro that accepts both cxstring and cxmutstr. - * @deprecated use the CX_HASH_KEY() macro instead - * @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)) - -/** * Compare function for hash keys. * - * @param left the first key - * @param right the second key + * The pointers are untyped to be compatible with the cx_compare_func signature. + * + * @param left (@c CxHashKey*) the first key + * @param right (@c CxHashKey*) the second key * @return zero when the keys equal, non-zero when they differ */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cx_hash_key_cmp(const void *left, const void *right); #ifdef __cplusplus } // extern "C" @@ -276,31 +243,31 @@ // Overloads of CX_HASH_KEY (the C++ version of a _Generic) // ---------------------------------------------------------- -static inline CxHashKey CX_HASH_KEY(CxHashKey key) { +CX_CPPDECL CxHashKey CX_HASH_KEY(CxHashKey key) { return key; } -static inline CxHashKey CX_HASH_KEY(cxstring str) { +CX_CPPDECL CxHashKey CX_HASH_KEY(cxstring str) { return cx_hash_key_cxstr(str); } -static inline CxHashKey CX_HASH_KEY(cxmutstr str) { +CX_CPPDECL CxHashKey CX_HASH_KEY(cxmutstr str) { return cx_hash_key_mutstr(str); } -static inline CxHashKey CX_HASH_KEY(const char *str) { +CX_CPPDECL CxHashKey CX_HASH_KEY(const char *str) { return cx_hash_key_str(str); } -static inline CxHashKey CX_HASH_KEY(const unsigned char *str) { +CX_CPPDECL CxHashKey CX_HASH_KEY(const unsigned char *str) { return cx_hash_key_ustr(str); } -static inline CxHashKey CX_HASH_KEY(uint32_t key) { +CX_CPPDECL CxHashKey CX_HASH_KEY(uint32_t key) { return cx_hash_key_u32(key); } -static inline CxHashKey CX_HASH_KEY(uint64_t key) { +CX_CPPDECL CxHashKey CX_HASH_KEY(uint64_t key) { return cx_hash_key_u64(key); } #endif
--- a/ucx/cx/hash_map.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/hash_map.h Mon Nov 10 21:52:51 2025 +0100 @@ -83,15 +83,9 @@ * @param buckets the initial number of buckets in this hash map * @return a pointer to the new hash map */ -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxMapFree, 1) -cx_attr_export -CxMap *cxHashMapCreate( - const CxAllocator *allocator, - size_t itemsize, - size_t buckets -); +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1) +CX_EXPORT CxMap *cxHashMapCreate(const CxAllocator *allocator, + size_t itemsize, size_t buckets); /** * Creates a new hash map with a default number of buckets. @@ -129,8 +123,7 @@ * @retval non-zero if a memory allocation error occurred */ cx_attr_nonnull -cx_attr_export -int cxMapRehash(CxMap *map); +CX_EXPORT int cxMapRehash(CxMap *map); #ifdef __cplusplus
--- a/ucx/cx/iterator.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/iterator.h Mon Nov 10 21:52:51 2025 +0100 @@ -50,7 +50,10 @@ * True if the iterator points to valid data. */ bool (*valid)(const void *); - + /** + * Original implementation in case the function needs to be wrapped. + */ + bool (*valid_impl)(const void *); /** * Returns a pointer to the current element. * @@ -62,7 +65,6 @@ * Original implementation in case the function needs to be wrapped. */ void *(*current_impl)(const void *); - /** * Advances the iterator. * @@ -76,7 +78,7 @@ /** * Indicates whether this iterator may remove elements. */ - bool mutating; + bool allow_remove; /** * Internal flag for removing the current element when advancing. */ @@ -112,16 +114,7 @@ /** * Handle for the source collection, if any. */ - union { - /** - * Access for mutating iterators. - */ - void *m; - /** - * Access for normal iterators. - */ - const void *c; - } src_handle; + void *src_handle; /** * If the iterator is position-aware, contains the index of the element in the underlying collection. @@ -149,7 +142,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 obsolete, * and it must not be used anymore. */ typedef struct cx_iterator_s CxIterator; @@ -182,13 +175,12 @@ #define cxIteratorNext(iter) (iter).base.next(&iter) /** - * Flags the current element for removal if this iterator is mutating. - * - * Does nothing for non-mutating iterators. + * Flags the current element for removal if the iterator allows it. * * @param iter the iterator + * @return @c true if removal is allowed, @c false otherwise */ -#define cxIteratorFlagRemoval(iter) (iter).base.remove |= (iter).base.mutating +#define cxIteratorFlagRemoval(iter) ((iter).base.remove = (iter).base.allow_remove) /** * Obtains a reference to an arbitrary iterator. @@ -222,22 +214,34 @@ * use cxIteratorPtr() to create an iterator which directly * yields the stored pointers. * + * While the iterator is in use, the array may only be altered by removing + * 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 + * 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. + * * @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 + * when removing an element * @return an iterator for the specified array * @see cxIteratorPtr() */ cx_attr_nodiscard -cx_attr_export -CxIterator cxIterator( - const void *array, - size_t elem_size, - size_t elem_count -); +CX_EXPORT CxIterator cxIterator(const void *array, + size_t elem_size, size_t elem_count, bool remove_keeps_order); /** - * Creates a mutating iterator for the specified plain array. + * Creates an iterator for the specified plain pointer array. + * + * This iterator assumes that every element in the array is a pointer + * and yields exactly those pointers during iteration (on the other + * hand, an iterator created with cxIterator() would return the + * addresses of those pointers within the array). * * While the iterator is in use, the array may only be altered by removing * elements through #cxIteratorFlagRemoval(). Every other change to the array @@ -248,67 +252,16 @@ * moving all subsequent elements by one. Usually, when the order of elements is * 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. - * - * - * @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 - * 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, - size_t elem_count, - bool remove_keeps_order -); - -/** - * Creates an iterator for the specified plain pointer array. - * - * This iterator assumes that every element in the array is a pointer - * and yields exactly those pointers during iteration (on the other - * hand, 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 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 -); - -/** - * Creates a mutating iterator for the specified plain pointer array. - * - * This is the mutating variant of cxIteratorPtr(). See also - * cxMutIterator(). - * * @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 * when removing an element * @return an iterator for the specified array - * @see cxMutIterator() - * @see cxIteratorPtr() + * @see cxIterator() */ cx_attr_nodiscard -cx_attr_export -CxIterator cxMutIteratorPtr( - void *array, - size_t elem_count, - bool remove_keeps_order -); +CX_EXPORT CxIterator cxIteratorPtr(const void *array, size_t elem_count, + bool remove_keeps_order); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/json.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/json.h Mon Nov 10 21:52:51 2025 +0100 @@ -475,8 +475,7 @@ * @return new JSON writer settings */ cx_attr_nodiscard -cx_attr_export -CxJsonWriter cxJsonWriterCompact(void); +CX_EXPORT CxJsonWriter cxJsonWriterCompact(void); /** * Creates a default writer configuration for pretty output. @@ -485,8 +484,7 @@ * @return new JSON writer settings */ cx_attr_nodiscard -cx_attr_export -CxJsonWriter cxJsonWriterPretty(bool use_spaces); +CX_EXPORT CxJsonWriter cxJsonWriterPretty(bool use_spaces); /** * Writes a JSON value to a buffer or stream. @@ -507,13 +505,8 @@ * @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, - cx_write_func wfunc, - const CxJsonWriter* settings -); +CX_EXPORT int cxJsonWrite(void* target, const CxJsonValue* value, + cx_write_func wfunc, const CxJsonWriter* settings); /** * Initializes the JSON interface. @@ -523,8 +516,7 @@ * @see cxJsonDestroy() */ cx_attr_nonnull_arg(1) -cx_attr_export -void cxJsonInit(CxJson *json, const CxAllocator *allocator); +CX_EXPORT void cxJsonInit(CxJson *json, const CxAllocator *allocator); /** * Destroys the JSON interface. @@ -533,8 +525,7 @@ * @see cxJsonInit() */ cx_attr_nonnull -cx_attr_export -void cxJsonDestroy(CxJson *json); +CX_EXPORT void cxJsonDestroy(CxJson *json); /** * Destroys and re-initializes the JSON interface. @@ -545,11 +536,7 @@ * @param json the JSON interface */ cx_attr_nonnull -static inline void cxJsonReset(CxJson *json) { - const CxAllocator *allocator = json->allocator; - cxJsonDestroy(json); - cxJsonInit(json, allocator); -} +CX_EXPORT void cxJsonReset(CxJson *json); /** * Fills the input buffer. @@ -569,10 +556,8 @@ * @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); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonFilln(CxJson *json, const char *buf, size_t len); /** @@ -584,7 +569,7 @@ * @retval non-zero internal allocation error */ cx_attr_nonnull -static inline int cx_json_fill(CxJson *json, cxstring str) { +CX_INLINE int cx_json_fill(CxJson *json, cxstring str) { return cxJsonFilln(json, str.ptr, str.length); } @@ -616,8 +601,7 @@ * @see cxJsonArrAddValues() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); +CX_EXPORT CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator); /** * Creates a new (empty) JSON array. @@ -628,8 +612,7 @@ * @see cxJsonArrAddValues() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); +CX_EXPORT CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator); /** * Creates a new JSON number value. @@ -641,8 +624,7 @@ * @see cxJsonArrAddNumbers() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num); +CX_EXPORT CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num); /** * Creates a new JSON number value based on an integer. @@ -654,8 +636,7 @@ * @see cxJsonArrAddIntegers() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num); +CX_EXPORT CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num); /** * Creates a new JSON string. @@ -667,11 +648,8 @@ * @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); +cx_attr_nodiscard cx_attr_nonnull_arg(2) cx_attr_cstr_arg(2) +CX_EXPORT CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char *str); /** * Creates a new JSON string. @@ -684,8 +662,7 @@ * @see cxJsonArrAddCxStrings() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); +CX_EXPORT CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str); /** * Creates a new JSON literal. @@ -697,8 +674,7 @@ * @see cxJsonArrAddLiterals() */ cx_attr_nodiscard -cx_attr_export -CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit); +CX_EXPORT CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit); /** * Adds number values to a JSON array. @@ -709,10 +685,8 @@ * @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); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count); /** * Adds number values, of which all are integers, to a JSON array. @@ -723,10 +697,8 @@ * @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); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count); /** * Adds strings to a JSON array. @@ -740,10 +712,8 @@ * @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); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count); /** * Adds strings to a JSON array. @@ -757,10 +727,8 @@ * @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); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count); /** * Adds literals to a JSON array. @@ -771,10 +739,8 @@ * @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); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count); /** * Add arbitrary values to a JSON array. @@ -788,10 +754,8 @@ * @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); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count); /** * Adds or replaces a value within a JSON object. @@ -808,8 +772,7 @@ * @retval non-zero allocation failure */ cx_attr_nonnull -cx_attr_export -int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); +CX_EXPORT int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child); /** * Creates a new JSON object and adds it to an existing object. @@ -821,8 +784,7 @@ * @see cxJsonCreateObj() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); +CX_EXPORT CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name); /** * Creates a new JSON array and adds it to an object. @@ -834,8 +796,7 @@ * @see cxJsonCreateArr() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); +CX_EXPORT CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name); /** * Creates a new JSON number and adds it to an object. @@ -848,8 +809,7 @@ * @see cxJsonCreateNumber() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); +CX_EXPORT CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num); /** * Creates a new JSON number, based on an integer, and adds it to an object. @@ -862,8 +822,7 @@ * @see cxJsonCreateInteger() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); +CX_EXPORT CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num); /** * Creates a new JSON string and adds it to an object. @@ -877,10 +836,8 @@ * @see cxJsonObjPut() * @see cxJsonCreateString() */ -cx_attr_nonnull -cx_attr_cstr_arg(3) -cx_attr_export -CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); +cx_attr_nonnull cx_attr_cstr_arg(3) +CX_EXPORT CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str); /** * Creates a new JSON string and adds it to an object. @@ -895,8 +852,7 @@ * @see cxJsonCreateCxString() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); +CX_EXPORT CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str); /** * Creates a new JSON literal and adds it to an object. @@ -909,8 +865,7 @@ * @see cxJsonCreateLiteral() */ cx_attr_nonnull -cx_attr_export -CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); +CX_EXPORT CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit); /** * Recursively deallocates the memory of a JSON value. @@ -923,8 +878,7 @@ * * @param value the value */ -cx_attr_export -void cxJsonValueFree(CxJsonValue *value); +CX_EXPORT void cxJsonValueFree(CxJsonValue *value); /** * Tries to obtain the next JSON value. @@ -948,10 +902,8 @@ * @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); +cx_attr_nonnull cx_attr_access_w(2) +CX_EXPORT CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); /** * Checks if the specified value is a JSON object. @@ -961,7 +913,7 @@ * @retval false otherwise */ cx_attr_nonnull -static inline bool cxJsonIsObject(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsObject(const CxJsonValue *value) { return value->type == CX_JSON_OBJECT; } @@ -973,7 +925,7 @@ * @retval false otherwise */ cx_attr_nonnull -static inline bool cxJsonIsArray(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsArray(const CxJsonValue *value) { return value->type == CX_JSON_ARRAY; } @@ -985,7 +937,7 @@ * @retval false otherwise */ cx_attr_nonnull -static inline bool cxJsonIsString(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsString(const CxJsonValue *value) { return value->type == CX_JSON_STRING; } @@ -1001,7 +953,7 @@ * @see cxJsonIsInteger() */ cx_attr_nonnull -static inline bool cxJsonIsNumber(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsNumber(const CxJsonValue *value) { return value->type == CX_JSON_NUMBER || value->type == CX_JSON_INTEGER; } @@ -1014,7 +966,7 @@ * @see cxJsonIsNumber() */ cx_attr_nonnull -static inline bool cxJsonIsInteger(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsInteger(const CxJsonValue *value) { return value->type == CX_JSON_INTEGER; } @@ -1031,7 +983,7 @@ * @see cxJsonIsNull() */ cx_attr_nonnull -static inline bool cxJsonIsLiteral(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsLiteral(const CxJsonValue *value) { return value->type == CX_JSON_LITERAL; } @@ -1045,7 +997,7 @@ * @see cxJsonIsFalse() */ cx_attr_nonnull -static inline bool cxJsonIsBool(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsBool(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal != CX_JSON_NULL; } @@ -1062,7 +1014,7 @@ * @see cxJsonIsFalse() */ cx_attr_nonnull -static inline bool cxJsonIsTrue(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsTrue(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_TRUE; } @@ -1079,7 +1031,7 @@ * @see cxJsonIsTrue() */ cx_attr_nonnull -static inline bool cxJsonIsFalse(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsFalse(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_FALSE; } @@ -1092,7 +1044,7 @@ * @see cxJsonIsLiteral() */ cx_attr_nonnull -static inline bool cxJsonIsNull(const CxJsonValue *value) { +CX_INLINE bool cxJsonIsNull(const CxJsonValue *value) { return cxJsonIsLiteral(value) && value->value.literal == CX_JSON_NULL; } @@ -1105,11 +1057,8 @@ * @return the value represented as C string * @see cxJsonIsString() */ -cx_attr_nonnull -cx_attr_returns_nonnull -static inline char *cxJsonAsString(const CxJsonValue *value) { - return value->value.string.ptr; -} +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT char *cxJsonAsString(const CxJsonValue *value); /** * Obtains a UCX string from the given JSON value. @@ -1121,9 +1070,7 @@ * @see cxJsonIsString() */ cx_attr_nonnull -static inline cxstring cxJsonAsCxString(const CxJsonValue *value) { - return cx_strcast(value->value.string); -} +CX_EXPORT cxstring cxJsonAsCxString(const CxJsonValue *value); /** * Obtains a mutable UCX string from the given JSON value. @@ -1135,9 +1082,7 @@ * @see cxJsonIsString() */ cx_attr_nonnull -static inline cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { - return value->value.string; -} +CX_EXPORT cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value); /** * Obtains a double-precision floating-point value from the given JSON value. @@ -1149,13 +1094,7 @@ * @see cxJsonIsNumber() */ cx_attr_nonnull -static inline double cxJsonAsDouble(const CxJsonValue *value) { - if (value->type == CX_JSON_INTEGER) { - return (double) value->value.integer; - } else { - return value->value.number; - } -} +CX_EXPORT double cxJsonAsDouble(const CxJsonValue *value); /** * Obtains a 64-bit signed integer from the given JSON value. @@ -1170,13 +1109,7 @@ * @see cxJsonIsInteger() */ cx_attr_nonnull -static inline int64_t cxJsonAsInteger(const CxJsonValue *value) { - if (value->type == CX_JSON_INTEGER) { - return value->value.integer; - } else { - return (int64_t) value->value.number; - } -} +CX_EXPORT int64_t cxJsonAsInteger(const CxJsonValue *value); /** * Obtains a Boolean value from the given JSON value. @@ -1189,7 +1122,7 @@ * @see cxJsonIsLiteral() */ cx_attr_nonnull -static inline bool cxJsonAsBool(const CxJsonValue *value) { +CX_INLINE bool cxJsonAsBool(const CxJsonValue *value) { return value->value.literal == CX_JSON_TRUE; } @@ -1203,7 +1136,7 @@ * @see cxJsonIsArray() */ cx_attr_nonnull -static inline size_t cxJsonArrSize(const CxJsonValue *value) { +CX_INLINE size_t cxJsonArrSize(const CxJsonValue *value) { return value->value.array.array_size; } @@ -1221,10 +1154,8 @@ * @return the value at the specified index * @see cxJsonIsArray() */ -cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_export -CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); /** * Removes an element from a JSON array. @@ -1240,8 +1171,7 @@ * @see cxJsonIsArray() */ cx_attr_nonnull -cx_attr_export -CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index); +CX_EXPORT CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index); /** * Returns an iterator over the JSON array elements. @@ -1254,10 +1184,8 @@ * @return an iterator over the array elements * @see cxJsonIsArray() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxIterator cxJsonArrIter(const CxJsonValue *value); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxIterator cxJsonArrIter(const CxJsonValue *value); /** * Returns an iterator over the JSON object members. @@ -1271,10 +1199,8 @@ * @return an iterator over the object members * @see cxJsonIsObject() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxIterator cxJsonObjIter(const CxJsonValue *value); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxIterator cxJsonObjIter(const CxJsonValue *value); /** * Internal function, do not use. @@ -1282,10 +1208,8 @@ * @param name the key to look up * @return the value corresponding to the key */ -cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_export -CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name); +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name); /** * Returns a value corresponding to a key in a JSON object. @@ -1310,8 +1234,7 @@ * @return the value corresponding to the key or @c NULL when the key is not part of the object */ cx_attr_nonnull -cx_attr_export -CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name); +CX_EXPORT CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name); /** * Removes and returns a value corresponding to a key in a JSON object.
--- a/ucx/cx/kv_list.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/kv_list.h Mon Nov 10 21:52:51 2025 +0100 @@ -66,15 +66,9 @@ * @see cxKvListAsMap() * @see cxKvListAsList() */ -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxListFree, 1) -cx_attr_export -CxList *cxKvListCreate( - const CxAllocator *allocator, - cx_compare_func comparator, - size_t elem_size -); +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) +CX_EXPORT CxList *cxKvListCreate(const CxAllocator *allocator, + cx_compare_func comparator, size_t elem_size); /** * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. @@ -97,15 +91,9 @@ * @see cxKvListAsMap() * @see cxKvListAsList() */ -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxMapFree, 1) -cx_attr_export -CxMap *cxKvListCreateAsMap( - const CxAllocator *allocator, - cx_compare_func comparator, - size_t elem_size -); +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMapFree, 1) +CX_EXPORT CxMap *cxKvListCreateAsMap(const CxAllocator *allocator, + cx_compare_func comparator, size_t elem_size); /** * Allocates a linked list with a lookup-map for storing elements with @p elem_size bytes each. @@ -158,11 +146,8 @@ * @param map a map pointer that was returned by a call to cxKvListAsMap() * @return the original list pointer */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_export -CxList *cxKvListAsList(CxMap *map); +cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT CxList *cxKvListAsList(CxMap *map); /** * Converts a map pointer belonging to a key-value-List back to the original list pointer. @@ -170,11 +155,8 @@ * @param list a list created by cxKvListCreate() or cxKvListCreateSimple() * @return a map pointer that lets you use the list as if it was a map */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_returns_nonnull -cx_attr_export -CxMap *cxKvListAsMap(CxList *list); +cx_attr_nodiscard cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT CxMap *cxKvListAsMap(CxList *list); /** * Sets or updates the key of a list item. @@ -190,8 +172,7 @@ * @see cxKvListSetKey() */ cx_attr_nonnull -cx_attr_export -int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key); +CX_EXPORT int cx_kv_list_set_key(CxList *list, size_t index, CxHashKey key); /** * Inserts an item into the list at the specified index and associates it with the specified key. @@ -205,8 +186,7 @@ * @see cxKvListInsert() */ cx_attr_nonnull -cx_attr_export -int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value); +CX_EXPORT int cx_kv_list_insert(CxList *list, size_t index, CxHashKey key, void *value); /** * Sets or updates the key of a list item. @@ -250,8 +230,7 @@ * @retval non-zero the index is out of bounds */ cx_attr_nonnull -cx_attr_export -int cxKvListRemoveKey(CxList *list, size_t index); +CX_EXPORT int cxKvListRemoveKey(CxList *list, size_t index); /** * Returns the key of a list item. @@ -261,8 +240,7 @@ * @return a pointer to the key or @c NULL when the index is out of bounds or the item does not have a key */ cx_attr_nonnull -cx_attr_export -const CxHashKey *cxKvListGetKey(CxList *list, size_t index); +CX_EXPORT const CxHashKey *cxKvListGetKey(CxList *list, size_t index); /** * Adds an item into the list and associates it with the specified key.
--- a/ucx/cx/linked_list.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/linked_list.h Mon Nov 10 21:52:51 2025 +0100 @@ -90,15 +90,9 @@ * @param elem_size the size of each element in bytes * @return the created list */ -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxListFree, 1) -cx_attr_export -CxList *cxLinkedListCreate( - const CxAllocator *allocator, - cx_compare_func comparator, - size_t elem_size -); +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxListFree, 1) +CX_EXPORT CxList *cxLinkedListCreate(const CxAllocator *allocator, + cx_compare_func comparator, size_t elem_size); /** * Allocates a linked list for storing elements with @p elem_size bytes each. @@ -115,7 +109,7 @@ * @return (@c CxList*) the created list */ #define cxLinkedListCreateSimple(elem_size) \ - cxLinkedListCreate(NULL, NULL, elem_size) + cxLinkedListCreate(NULL, NULL, elem_size) /** * Finds the node at a certain index. @@ -134,15 +128,9 @@ * @param index the search index * @return the node found at the specified index */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -void *cx_linked_list_at( - const void *start, - size_t start_index, - ptrdiff_t loc_advance, - size_t index -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT void *cx_linked_list_at(const void *start,size_t start_index, + ptrdiff_t loc_advance, size_t index); /** * Finds the node containing an element within a linked list. @@ -157,15 +145,9 @@ * @return a pointer to the found node or @c NULL if no matching node was found */ 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, - size_t *found_index -); +CX_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, + size_t *found_index); /** * Finds the first node in a linked list. @@ -178,13 +160,8 @@ * @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 -); +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT void *cx_linked_list_first(const void *node, ptrdiff_t loc_prev); /** * Finds the last node in a linked list. @@ -197,13 +174,8 @@ * @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 -); +cx_attr_nonnull cx_attr_returns_nonnull +CX_EXPORT void *cx_linked_list_last(const void *node, ptrdiff_t loc_next); /** * Finds the predecessor of a node in case it is not linked. @@ -216,12 +188,7 @@ * @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, - const void *node -); +CX_EXPORT void *cx_linked_list_prev(const void *begin, ptrdiff_t loc_next, const void *node); /** * Adds a new node to a linked list. @@ -236,14 +203,7 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *new_node -); +CX_EXPORT void cx_linked_list_add(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node); /** * Prepends a new node to a linked list. @@ -258,14 +218,7 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *new_node -); +CX_EXPORT void cx_linked_list_prepend(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node); /** * Links two nodes. @@ -276,13 +229,7 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_linked_list_link(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Unlinks two nodes. @@ -295,13 +242,7 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_linked_list_unlink(void *left, void *right, ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Inserts a new node after a given node of a linked list. @@ -318,15 +259,8 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *node, - void *new_node -); +CX_EXPORT void cx_linked_list_insert(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *new_node); /** * Inserts a chain of nodes after a given node of a linked list. @@ -349,16 +283,8 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *node, - void *insert_begin, - void *insert_end -); +CX_EXPORT void cx_linked_list_insert_chain(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, void *insert_begin, void *insert_end); /** * Inserts a node into a sorted linked list. @@ -375,15 +301,8 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *new_node, - cx_compare_func cmp_func -); +CX_EXPORT void cx_linked_list_insert_sorted(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func); /** * Inserts a chain of nodes into a sorted linked list. @@ -405,15 +324,8 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *insert_begin, - cx_compare_func cmp_func -); +CX_EXPORT void cx_linked_list_insert_sorted_chain(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func); /** * Inserts a node into a sorted linked list if no other node with the same value already exists. @@ -432,15 +344,8 @@ * @retval non-zero when a node with the same value already exists */ cx_attr_nonnull_arg(1, 5, 6) -cx_attr_export -int cx_linked_list_insert_unique( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *new_node, - cx_compare_func cmp_func -); +CX_EXPORT int cx_linked_list_insert_unique(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node, cx_compare_func cmp_func); /** * Inserts a chain of nodes into a sorted linked list, avoiding duplicates. @@ -460,17 +365,9 @@ * @param cmp_func a compare function that will receive the node pointers * @return a pointer to a new chain with all duplicates that were not inserted (or @c NULL when there were no duplicates) */ -cx_attr_nonnull_arg(1, 5, 6) -cx_attr_nodiscard -cx_attr_export -void *cx_linked_list_insert_unique_chain( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *insert_begin, - cx_compare_func cmp_func -); +cx_attr_nonnull_arg(1, 5, 6) cx_attr_nodiscard +CX_EXPORT void *cx_linked_list_insert_unique_chain(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *insert_begin, cx_compare_func cmp_func); /** * Removes a chain of nodes from the linked list. @@ -494,15 +391,8 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *node, - size_t num -); +CX_EXPORT size_t cx_linked_list_remove_chain(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node, size_t num); /** * Removes a node from the linked list. @@ -524,15 +414,8 @@ * @param node the node to remove */ cx_attr_nonnull_arg(5) -static inline void cx_linked_list_remove( - void **begin, - void **end, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - void *node -) { - cx_linked_list_remove_chain(begin, end, loc_prev, loc_next, node, 1); -} +CX_EXPORT void cx_linked_list_remove(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, void *node); /** * Determines the size of a linked list starting with @p node. @@ -542,11 +425,7 @@ * @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 -); +CX_EXPORT size_t cx_linked_list_size(const void *node, ptrdiff_t loc_next); /** * Sorts a linked list based on a comparison function. @@ -571,15 +450,8 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next, - ptrdiff_t loc_data, - cx_compare_func cmp_func -); +CX_EXPORT void cx_linked_list_sort(void **begin, void **end, + ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_data, cx_compare_func cmp_func); /** @@ -596,14 +468,8 @@ * 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, - ptrdiff_t loc_advance, - ptrdiff_t loc_data, - cx_compare_func cmp_func -); +CX_EXPORT int cx_linked_list_compare(const void *begin_left, const void *begin_right, + ptrdiff_t loc_advance, ptrdiff_t loc_data, cx_compare_func cmp_func); /** * Reverses the order of the nodes in a linked list. @@ -614,13 +480,7 @@ * @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, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_linked_list_reverse(void **begin, void **end, ptrdiff_t loc_prev, ptrdiff_t loc_next); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/list.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/list.h Mon Nov 10 21:52:51 2025 +0100 @@ -39,8 +39,6 @@ #include "common.h" #include "collection.h" -#include <assert.h> - #ifdef __cplusplus extern "C" { #endif @@ -85,54 +83,38 @@ * The data pointer may be @c NULL, in which case the function shall only allocate memory. * Returns a pointer to the allocated memory or @c NULL if allocation fails. */ - void *(*insert_element)( - struct cx_list_s *list, - size_t index, - const void *data - ); + void *(*insert_element)(struct cx_list_s *list, size_t index, const void *data); /** * Member function for inserting multiple elements. * + * The data pointer may be @c NULL, in which case the function shall only allocate memory. + * Returns the number of successfully inserted or allocated elements. + * * @see cx_list_default_insert_array() */ - size_t (*insert_array)( - struct cx_list_s *list, - size_t index, - const void *data, - size_t n - ); + size_t (*insert_array)(struct cx_list_s *list, size_t index, const void *data, size_t n); /** * Member function for inserting sorted elements into a sorted list. + * Returns the number of successfully inserted elements. * * @see cx_list_default_insert_sorted() */ - size_t (*insert_sorted)( - struct cx_list_s *list, - const void *sorted_data, - size_t n - ); + size_t (*insert_sorted)(struct cx_list_s *list, const void *sorted_data, size_t n); /** * Member function for inserting multiple elements if they do not exist. - * + * Implementations shall return the number of successfully processed elements + * (including those which were not added because they are already contained). * @see cx_list_default_insert_unique() */ - size_t (*insert_unique)( - struct cx_list_s *list, - const void *sorted_data, - size_t n - ); + size_t (*insert_unique)(struct cx_list_s *list, const void *sorted_data, size_t n); /** * Member function for inserting an element relative to an iterator position. */ - int (*insert_iter)( - struct cx_iterator_s *iter, - const void *elem, - int prepend - ); + int (*insert_iter)(struct cx_iterator_s *iter, const void *elem, int prepend); /** * Member function for removing elements. @@ -144,12 +126,7 @@ * The function SHALL return the actual number of elements removed, which * might be lower than @p num when going out of bounds. */ - size_t (*remove)( - struct cx_list_s *list, - size_t index, - size_t num, - void *targetbuf - ); + size_t (*remove)(struct cx_list_s *list, size_t index, size_t num, void *targetbuf); /** * Member function for removing all elements. @@ -161,28 +138,17 @@ * * @see cx_list_default_swap() */ - int (*swap)( - struct cx_list_s *list, - size_t i, - size_t j - ); + int (*swap)(struct cx_list_s *list, size_t i, size_t j); /** * Member function for element lookup. */ - void *(*at)( - const struct cx_list_s *list, - size_t index - ); + void *(*at)(const struct cx_list_s *list, size_t index); /** * Member function for finding and optionally removing an element. */ - size_t (*find_remove)( - struct cx_list_s *list, - const void *elem, - bool remove - ); + size_t (*find_remove)(struct cx_list_s *list, const void *elem, bool remove); /** * Member function for sorting the list. @@ -196,10 +162,7 @@ * to another list of the same type. * If set to @c NULL, the comparison won't be optimized. */ - int (*compare)( - const struct cx_list_s *list, - const struct cx_list_s *other - ); + int (*compare)(const struct cx_list_s *list, const struct cx_list_s *other); /** * Member function for reversing the order of the items. @@ -207,13 +170,15 @@ void (*reverse)(struct cx_list_s *list); /** + * Optional member function for changing the capacity. + * If the list does not support overallocation, this can be set to @c NULL. + */ + int (*change_capacity)(struct cx_list_s *list, size_t new_capacity); + + /** * Member function for returning an iterator pointing to the specified index. */ - struct cx_iterator_s (*iterator)( - const struct cx_list_s *list, - size_t index, - bool backward - ); + struct cx_iterator_s (*iterator)(const struct cx_list_s *list, size_t index, bool backward); }; /** @@ -229,8 +194,7 @@ * You can use this as 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; +CX_EXPORT extern CxList *const cxEmptyList; /** * Default implementation of an array insert. @@ -247,13 +211,8 @@ * @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, - const void *data, - size_t n -); +CX_EXPORT size_t cx_list_default_insert_array(struct cx_list_s *list, + size_t index, const void *data, size_t n); /** * Default implementation of a sorted insert. @@ -272,12 +231,8 @@ * @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, - size_t n -); +CX_EXPORT size_t cx_list_default_insert_sorted(struct cx_list_s *list, + const void *sorted_data, size_t n); /** * Default implementation of an array insert where only elements are inserted when they don't exist in the list. @@ -296,12 +251,8 @@ * @return the number of elements from the @p sorted_data that are definitely present in the list after this call */ cx_attr_nonnull -cx_attr_export -size_t cx_list_default_insert_unique( - struct cx_list_s *list, - const void *sorted_data, - size_t n -); +CX_EXPORT size_t cx_list_default_insert_unique(struct cx_list_s *list, + const void *sorted_data, size_t n); /** * Default unoptimized sort implementation. @@ -315,8 +266,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); +CX_EXPORT void cx_list_default_sort(struct cx_list_s *list); /** * Default unoptimized swap implementation. @@ -332,8 +282,7 @@ * 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); +CX_EXPORT int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j); /** * Initializes a list struct. @@ -380,14 +329,9 @@ * @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 -); +CX_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); /** * Returns the number of elements currently stored in the list. @@ -396,9 +340,7 @@ * @return the number of currently stored elements */ cx_attr_nonnull -static inline size_t cxListSize(const CxList *list) { - return list->collection.size; -} +CX_EXPORT size_t cxListSize(const CxList *list); /** * Adds an item to the end of the list. @@ -411,13 +353,7 @@ * @see cxListEmplace() */ cx_attr_nonnull -static inline int cxListAdd( - CxList *list, - const void *elem -) { - list->collection.sorted = false; - return list->cl->insert_element(list, list->collection.size, elem) == NULL; -} +CX_EXPORT int cxListAdd(CxList *list, const void *elem); /** * Adds multiple items to the end of the list. @@ -434,16 +370,10 @@ * @param array a pointer to the elements to add * @param n the number of elements to add * @return the number of added elements + * @see cxListEmplaceArray() */ cx_attr_nonnull -static inline size_t cxListAddArray( - CxList *list, - const void *array, - size_t n -) { - list->collection.sorted = false; - return list->cl->insert_array(list, list->collection.size, array, n); -} +CX_EXPORT size_t cxListAddArray(CxList *list, const void *array, size_t n); /** * Inserts an item at the specified index. @@ -460,14 +390,7 @@ * @see cxListEmplaceAt() */ cx_attr_nonnull -static inline int cxListInsert( - CxList *list, - size_t index, - const void *elem -) { - list->collection.sorted = false; - return list->cl->insert_element(list, index, elem) == NULL; -} +CX_EXPORT int cxListInsert(CxList *list, size_t index, const void *elem); /** * Allocates memory for an element at the specified index and returns a pointer to that memory. @@ -478,14 +401,11 @@ * @param index the index where to emplace the element * @return a pointer to the allocated memory; @c NULL when the operation fails, or the index is out-of-bounds * @see cxListEmplace() + * @see cxListEmplaceArrayAt() * @see cxListInsert() */ cx_attr_nonnull -static inline void *cxListEmplaceAt(CxList *list, size_t index) { - list->collection.sorted = false; - return list->cl->insert_element(list, index, NULL); -} - +CX_EXPORT void *cxListEmplaceAt(CxList *list, size_t index); /** * Allocates memory for an element at the end of the list and returns a pointer to that memory. @@ -498,10 +418,46 @@ * @see cxListAdd() */ cx_attr_nonnull -static inline void *cxListEmplace(CxList *list) { - list->collection.sorted = false; - return list->cl->insert_element(list, list->collection.size, NULL); -} +CX_EXPORT void *cxListEmplace(CxList *list); + +/** + * Allocates memory for multiple elements and returns an iterator. + * + * The iterator will only iterate over the successfully allocated elements. + * The @c elem_count attribute is set to that number, and the @c index attribute + * will range from zero to @c elem_count minus one. + * + * @remark When the list is storing pointers, the iterator will iterate over + * the @c void** elements. + * + * @param list the list + * @param index the index where to insert the new data + * @param n the number of elements for which to allocate the memory + * @return an iterator, iterating over the new memory + * @see cxListEmplaceAt() + * @see cxListInsertArray() + */ +cx_attr_nonnull +CX_EXPORT CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n); + +/** + * Allocates memory for multiple elements and returns an iterator. + * + * The iterator will only iterate over the successfully allocated elements. + * The @c elem_count attribute is set to that number, and the @c index attribute + * will range from zero to @c elem_count minus one. + * + * @remark When the list is storing pointers, the iterator will iterate over + * the @c void** elements. + * + * @param list the list + * @param n the number of elements for which to allocate the memory + * @return an iterator, iterating over the new memory + * @see cxListEmplace() + * @see cxListAddArray() + */ +cx_attr_nonnull +CX_EXPORT CxIterator cxListEmplaceArray(CxList *list, size_t n); /** * Inserts an item into a sorted list. @@ -514,20 +470,15 @@ * @retval non-zero memory allocation failure */ cx_attr_nonnull -static inline int cxListInsertSorted( - CxList *list, - const void *elem -) { - assert(list->collection.sorted || list->collection.size == 0); - list->collection.sorted = true; - const void *data = list->collection.store_pointer ? &elem : elem; - return list->cl->insert_sorted(list, data, 1) == 0; -} +CX_EXPORT int cxListInsertSorted(CxList *list, const void *elem); /** - * Inserts an item into a sorted list if it does not exist. + * Inserts an item into a list if it does not exist. * - * If the list is not sorted already, the behavior is undefined. + * If the list is not sorted already, this function will check all elements + * and append the new element when it was not found. + * It is strongly recommended to use this function only on sorted lists, where + * the element, if it is not contained, is inserted at the correct position. * * @param list the list * @param elem a pointer to the element to add @@ -535,15 +486,7 @@ * @retval non-zero memory allocation failure */ cx_attr_nonnull -static inline int cxListInsertUnique( - CxList *list, - const void *elem -) { - assert(list->collection.sorted || list->collection.size == 0); - list->collection.sorted = true; - const void *data = list->collection.store_pointer ? &elem : elem; - return list->cl->insert_unique(list, data, 1) == 0; -} +CX_EXPORT int cxListInsertUnique(CxList *list, const void *elem); /** * Inserts multiple items to the list at the specified index. @@ -563,17 +506,10 @@ * @param array a pointer to the elements to add * @param n the number of elements to add * @return the number of added elements + * @see cxListEmplaceArrayAt() */ cx_attr_nonnull -static inline size_t cxListInsertArray( - CxList *list, - size_t index, - const void *array, - size_t n -) { - list->collection.sorted = false; - return list->cl->insert_array(list, index, array, n); -} +CX_EXPORT size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n); /** * Inserts a sorted array into a sorted list. @@ -595,18 +531,17 @@ * @return the number of added elements */ cx_attr_nonnull -static inline size_t cxListInsertSortedArray( - CxList *list, - const void *array, - size_t n -) { - assert(list->collection.sorted || list->collection.size == 0); - list->collection.sorted = true; - return list->cl->insert_sorted(list, array, n); -} +CX_EXPORT size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n); /** - * Inserts a sorted array into a sorted list, skipping duplicates. + * Inserts an array into a list, skipping duplicates. + * + * The @p list does not need to be sorted (in contrast to cxListInsertSortedArray()). + * But it is strongly recommended to use this function only on sorted lists, + * because otherwise it will fall back to an inefficient algorithm which inserts + * all elements one by one. + * If the @p list is not sorted, the @p array also does not need to be sorted. + * But when the @p list is sorted, the @p array must also be sorted. * * This method is usually more efficient than inserting each element separately * because consecutive chunks of sorted data are inserted in one pass. @@ -623,9 +558,6 @@ * 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. - * This is also the case when the @p array is not sorted. - * * @param list the list * @param array a pointer to the elements to add * @param n the number of elements to add @@ -634,15 +566,7 @@ * @return the number of elements from the @p sorted_data that are definitely present in the list after this call */ cx_attr_nonnull -static inline size_t cxListInsertUniqueArray( - CxList *list, - const void *array, - size_t n -) { - assert(list->collection.sorted || list->collection.size == 0); - list->collection.sorted = true; - return list->cl->insert_unique(list, array, n); -} +CX_EXPORT size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n); /** * Inserts an element after the current location of the specified iterator. @@ -661,14 +585,7 @@ * @see cxListInsertBefore() */ cx_attr_nonnull -static inline int cxListInsertAfter( - CxIterator *iter, - const void *elem -) { - CxList* list = (CxList*)iter->src_handle.m; - list->collection.sorted = false; - return list->cl->insert_iter(iter, elem, 0); -} +CX_EXPORT int cxListInsertAfter(CxIterator *iter, const void *elem); /** * Inserts an element before the current location of the specified iterator. @@ -687,14 +604,7 @@ * @see cxListInsertAfter() */ cx_attr_nonnull -static inline int cxListInsertBefore( - CxIterator *iter, - const void *elem -) { - CxList* list = (CxList*)iter->src_handle.m; - list->collection.sorted = false; - return list->cl->insert_iter(iter, elem, 1); -} +CX_EXPORT int cxListInsertBefore(CxIterator *iter, const void *elem); /** * Removes the element at the specified index. @@ -708,12 +618,7 @@ * @retval non-zero index out of bounds */ cx_attr_nonnull -static inline int cxListRemove( - CxList *list, - size_t index -) { - return list->cl->remove(list, index, 1, NULL) == 0; -} +CX_EXPORT int cxListRemove(CxList *list, size_t index); /** * Removes and returns the element at the specified index. @@ -728,15 +633,8 @@ * @retval zero success * @retval non-zero index out of bounds */ -cx_attr_nonnull -cx_attr_access_w(3) -static inline int cxListRemoveAndGet( - CxList *list, - size_t index, - void *targetbuf -) { - return list->cl->remove(list, index, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(3) +CX_EXPORT int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf); /** * Removes and returns the first element of the list. @@ -752,14 +650,8 @@ * @see cxListPopFront() * @see cxListRemoveAndGetLast() */ -cx_attr_nonnull -cx_attr_access_w(2) -static inline int cxListRemoveAndGetFirst( - CxList *list, - void *targetbuf -) { - return list->cl->remove(list, 0, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(2) +CX_EXPORT int cxListRemoveAndGetFirst(CxList *list, void *targetbuf); /** * Removes and returns the first element of the list. @@ -792,15 +684,8 @@ * @retval zero success * @retval non-zero the list is empty */ -cx_attr_nonnull -cx_attr_access_w(2) -static inline int cxListRemoveAndGetLast( - CxList *list, - void *targetbuf -) { - // note: index may wrap - member function will catch that - return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0; -} +cx_attr_nonnull cx_attr_access_w(2) +CX_EXPORT int cxListRemoveAndGetLast(CxList *list, void *targetbuf); /** * Removes and returns the last element of the list. @@ -836,13 +721,7 @@ * @return the actual number of removed elements */ cx_attr_nonnull -static inline size_t cxListRemoveArray( - CxList *list, - size_t index, - size_t num -) { - return list->cl->remove(list, index, num, NULL); -} +CX_EXPORT size_t cxListRemoveArray(CxList *list, size_t index, size_t num); /** * Removes and returns multiple elements starting at the specified index. @@ -857,16 +736,8 @@ * @param targetbuf a buffer where to copy the elements * @return the actual number of removed elements */ -cx_attr_nonnull -cx_attr_access_w(4) -static inline size_t cxListRemoveArrayAndGet( - CxList *list, - size_t index, - size_t num, - void *targetbuf -) { - return list->cl->remove(list, index, num, targetbuf); -} +cx_attr_nonnull cx_attr_access_w(4) +CX_EXPORT size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf); /** * Removes all elements from this list. @@ -877,10 +748,7 @@ * @param list the list */ cx_attr_nonnull -static inline void cxListClear(CxList *list) { - list->cl->clear(list); - list->collection.sorted = true; // empty lists are always sorted -} +CX_EXPORT void cxListClear(CxList *list); /** * Swaps two items in the list. @@ -896,14 +764,7 @@ * or the swap needed extra memory, but allocation failed */ cx_attr_nonnull -static inline int cxListSwap( - CxList *list, - size_t i, - size_t j -) { - list->collection.sorted = false; - return list->cl->swap(list, i, j); -} +CX_EXPORT int cxListSwap(CxList *list, size_t i, size_t j); /** * Returns a pointer to the element at the specified index. @@ -915,12 +776,7 @@ * @return a pointer to the element or @c NULL if the index is out of bounds */ cx_attr_nonnull -static inline void *cxListAt( - const CxList *list, - size_t index -) { - return list->cl->at(list, index); -} +CX_EXPORT void *cxListAt(const CxList *list, size_t index); /** * Returns a pointer to the first element. @@ -931,9 +787,7 @@ * @return a pointer to the first element or @c NULL if the list is empty */ cx_attr_nonnull -static inline void *cxListFirst(const CxList *list) { - return list->cl->at(list, 0); -} +CX_EXPORT void *cxListFirst(const CxList *list); /** * Returns a pointer to the last element. @@ -944,12 +798,13 @@ * @return a pointer to the last element or @c NULL if the list is empty */ cx_attr_nonnull -static inline void *cxListLast(const CxList *list) { - return list->cl->at(list, list->collection.size - 1); -} +CX_EXPORT void *cxListLast(const CxList *list); /** - * Sets the element at the specified index in the list + * Sets the element at the specified index in the list. + * + * This overwrites the element in-place without calling any destructor + * on the overwritten element. * * @param list the list to set the element in * @param index the index to set the element at @@ -958,12 +813,7 @@ * @retval non-zero when index is out of bounds */ cx_attr_nonnull -cx_attr_export -int cxListSet( - CxList *list, - size_t index, - const void *elem -); +CX_EXPORT int cxListSet(CxList *list, size_t index, const void *elem); /** * Returns an iterator pointing to the item at the specified index. @@ -977,13 +827,7 @@ * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListIteratorAt( - const CxList *list, - size_t index -) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, index, false); -} +CX_EXPORT CxIterator cxListIteratorAt(const CxList *list, size_t index); /** * Returns a backwards iterator pointing to the item at the specified index. @@ -997,50 +841,7 @@ * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListBackwardsIteratorAt( - const CxList *list, - size_t index -) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, index, true); -} - -/** - * Returns a mutating iterator pointing to the item at the specified index. - * - * The returned iterator is position-aware. - * - * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @param index the index where the iterator shall point at - * @return a new iterator - */ -cx_attr_nodiscard -cx_attr_export -CxIterator cxListMutIteratorAt( - CxList *list, - size_t index -); - -/** - * Returns a mutating backwards iterator pointing to the item at the - * specified index. - * - * The returned iterator is position-aware. - * - * If the index is out of range or @p list is @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @param index the index where the iterator shall point at - * @return a new iterator - */ -cx_attr_nodiscard -cx_attr_export -CxIterator cxListMutBackwardsIteratorAt( - CxList *list, - size_t index -); +CX_EXPORT CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index); /** * Returns an iterator pointing to the first item of the list. @@ -1053,27 +854,7 @@ * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListIterator(const CxList *list) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, 0, false); -} - -/** - * Returns a mutating iterator pointing to the first item of the list. - * - * The returned iterator is position-aware. - * - * If the list is empty or @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @return a new iterator - */ -cx_attr_nodiscard -static inline CxIterator cxListMutIterator(CxList *list) { - if (list == NULL) list = cxEmptyList; - return cxListMutIteratorAt(list, 0); -} - +CX_EXPORT CxIterator cxListIterator(const CxList *list); /** * Returns a backwards iterator pointing to the last item of the list. @@ -1086,26 +867,7 @@ * @return a new iterator */ cx_attr_nodiscard -static inline CxIterator cxListBackwardsIterator(const CxList *list) { - if (list == NULL) list = cxEmptyList; - return list->cl->iterator(list, list->collection.size - 1, true); -} - -/** - * Returns a mutating backwards iterator pointing to the last item of the list. - * - * The returned iterator is position-aware. - * - * If the list is empty or @c NULL, a past-the-end iterator will be returned. - * - * @param list the list - * @return a new iterator - */ -cx_attr_nodiscard -static inline CxIterator cxListMutBackwardsIterator(CxList *list) { - if (list == NULL) list = cxEmptyList; - return cxListMutBackwardsIteratorAt(list, list->collection.size - 1); -} +CX_EXPORT CxIterator cxListBackwardsIterator(const CxList *list); /** * Returns the index of the first element that equals @p elem. @@ -1118,14 +880,8 @@ * @see cxListIndexValid() * @see cxListContains() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline size_t cxListFind( - const CxList *list, - const void *elem -) { - return list->cl->find_remove((CxList*)list, elem, false); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxListFind(const CxList *list, const void *elem); /** * Checks if the list contains the specified element. @@ -1138,14 +894,8 @@ * @retval false if the element is not contained * @see cxListFind() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline bool cxListContains( - const CxList* list, - const void* elem -) { - return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size; -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT bool cxListContains(const CxList* list, const void* elem); /** * Checks if the specified index is within bounds. @@ -1155,11 +905,8 @@ * @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; -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT bool cxListIndexValid(const CxList *list, size_t index); /** * Removes and returns the index of the first element that equals @p elem. @@ -1173,12 +920,7 @@ * @see cxListIndexValid() */ cx_attr_nonnull -static inline size_t cxListFindRemove( - CxList *list, - const void *elem -) { - return list->cl->find_remove(list, elem, true); -} +CX_EXPORT size_t cxListFindRemove(CxList *list, const void *elem); /** * Sorts the list. @@ -1188,11 +930,7 @@ * @param list the list */ cx_attr_nonnull -static inline void cxListSort(CxList *list) { - if (list->collection.sorted) return; - list->cl->sort(list); - list->collection.sorted = true; -} +CX_EXPORT void cxListSort(CxList *list); /** * Reverses the order of the items. @@ -1200,11 +938,7 @@ * @param list the list */ 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); -} +CX_EXPORT void cxListReverse(CxList *list); /** * Compares a list to another list of the same type. @@ -1215,18 +949,13 @@ * @param list the list * @param other the list to compare to * @retval zero both lists are equal element wise - * @retval negative the first list is smaller + * @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 -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT int cxListCompare(const CxList *list, const CxList *other); /** * Deallocates the memory of the specified list structure. @@ -1235,9 +964,228 @@ * * @param list the list that shall be freed */ -cx_attr_export -void cxListFree(CxList *list); +CX_EXPORT void cxListFree(CxList *list); + + +/** + * Performs a deep clone of one list into another. + * + * If the destination list already contains elements, the cloned elements + * are appended to that list. + * + * @attention If the cloned elements need to be destroyed by a destructor + * function, you must make sure that the destination list also uses this + * destructor function. + * + * @param dst the destination list + * @param src the source list + * @param clone_func the clone function for the elements + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when all elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListCloneSimple() + */ +cx_attr_nonnull_arg(1, 2, 3) +CX_EXPORT int cxListClone(CxList *dst, const CxList *src, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Clones elements from a list only if they are not present in another list. + * + * If the @p minuend does not contain duplicates, this is equivalent to adding + * the set difference to the destination list. + * + * This function is optimized for the case when both the @p minuend and the + * @p subtrahend are sorted. + * + * @param dst the destination list + * @param minuend the list to subtract elements from + * @param subtrahend the elements that shall be subtracted + * @param clone_func the clone function for the elements + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListDifferenceSimple() + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxListDifference(CxList *dst, + const CxList *minuend, const CxList *subtrahend, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Clones elements from a list only if they are also present in another list. + * + * This function is optimized for the case when both the @p src and the + * @p other list are sorted. + * + * If the destination list already contains elements, the intersection is appended + * to that list. + * + * @param dst the destination list + * @param src the list to clone the elements from + * @param other the list to check the elements for existence + * @param clone_func the clone function for the elements + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListIntersectionSimple() + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxListIntersection(CxList *dst, const CxList *src, const CxList *other, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Performs a deep clone of one list into another, skipping duplicates. + * + * This function is optimized for the case when both the @p src and the + * @p other list are sorted. + * In that case, the union will also be sorted. + * + * If the destination list already contains elements, the union is appended + * to that list. In that case the destination is not necessarily sorted. + * + * @param dst the destination list + * @param src the primary source list + * @param other the other list, where elements are only cloned from + * when they are not in @p src + * @param clone_func the clone function for the elements + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListUnionSimple() + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxListUnion(CxList *dst, const CxList *src, const CxList *other, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); +/** + * Performs a shallow clone of one list into another. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * If the destination list already contains elements, the cloned elements + * are appended to that list. + * + * @attention If the cloned elements need to be destroyed by a destructor + * function, you must make sure that the destination list also uses this + * destructor function. + * + * @param dst the destination list + * @param src the source list + * @retval zero when all elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListClone() + */ +cx_attr_nonnull +CX_EXPORT int cxListCloneSimple(CxList *dst, const CxList *src); + +/** + * Clones elements from a list only if they are not present in another list. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * If the @p minuend does not contain duplicates, this is equivalent to adding + * the set difference to the destination list. + * + * This function is optimized for the case when both the @p minuend and the + * @p subtrahend are sorted. + * + * @param dst the destination list + * @param minuend the list to subtract elements from + * @param subtrahend the elements that shall be subtracted + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListDifference() + */ +cx_attr_nonnull +CX_EXPORT int cxListDifferenceSimple(CxList *dst, + const CxList *minuend, const CxList *subtrahend); + +/** + * Clones elements from a list only if they are also present in another list. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * This function is optimized for the case when both the @p src and the + * @p other list are sorted. + * + * If the destination list already contains elements, the intersection is appended + * to that list. + * + * @param dst the destination list + * @param src the list to clone the elements from + * @param other the list to check the elements for existence + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListIntersection() + */ +cx_attr_nonnull +CX_EXPORT int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other); + +/** + * Performs a deep clone of one list into another, skipping duplicates. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * This function is optimized for the case when both the @p src and the + * @p other list are sorted. + * In that case, the union will also be sorted. + * + * If the destination list already contains elements, the union is appended + * to that list. In that case the destination is not necessarily sorted. + * + * @param dst the destination list + * @param src the primary source list + * @param other the other list, where elements are only cloned from + * when they are not in @p src + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxListUnion() + */ +cx_attr_nonnull +CX_EXPORT int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other); + +/** + * Asks the list to reserve enough memory for a given total number of elements. + * + * List implementations are free to choose if reserving memory upfront makes + * sense. + * For example, array-based implementations usually do support reserving memory + * for additional elements while linked lists usually don't. + * + * @note When the requested capacity is smaller than the current size, + * this function returns zero without performing any action. + * + * @param list the list + * @param capacity the expected total number of elements + * @retval zero on success or when overallocation is not supported + * @retval non-zero when an allocation error occurred + * @see cxListShrink() + */ +cx_attr_nonnull +CX_EXPORT int cxListReserve(CxList *list, size_t capacity); + +/** + * Advises the list to free any overallocated memory. + * + * Lists that do not support overallocation simply return zero. + * + * This function usually returns zero, except for very special and custom + * list and/or allocator implementations where freeing memory can fail. + * + * @param list the list + * @return usually zero + */ +cx_attr_nonnull +CX_EXPORT int cxListShrink(CxList *list); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/map.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/map.h Mon Nov 10 21:52:51 2025 +0100 @@ -41,6 +41,11 @@ #include "string.h" #include "hash_key.h" +#ifndef UCX_LIST_H +// forward-declare CxList +typedef struct cx_list_s CxList; +#endif + #ifdef __cplusplus extern "C" { #endif @@ -111,16 +116,7 @@ /** * Handle for the source map. */ - union { - /** - * Access for mutating iterators. - */ - CxMap *m; - /** - * Access for normal iterators. - */ - const CxMap *c; - } map; + CxMap *map; /** * Handle for the current element. @@ -189,19 +185,12 @@ * shall only allocate memory instead of adding an existing value to the map. * Returns a pointer to the allocated memory or @c NULL if allocation fails. */ - void *(*put)( - CxMap *map, - CxHashKey key, - void *value - ); + void *(*put)(CxMap *map, CxHashKey key, void *value); /** * Returns an element. */ - void *(*get)( - const CxMap *map, - CxHashKey key - ); + void *(*get)(const CxMap *map, CxHashKey key); /** * Removes an element. @@ -213,11 +202,7 @@ * The function SHALL return zero when the @p key was found and * non-zero, otherwise. */ - int (*remove)( - CxMap *map, - CxHashKey key, - void *targetbuf - ); + int (*remove)(CxMap *map, CxHashKey key, void *targetbuf); /** * Creates an iterator for this map. @@ -233,8 +218,7 @@ * You can use this as a placeholder for initializing CxMap pointers * for which you do not want to reserve memory right from the beginning. */ -cx_attr_export -extern CxMap *const cxEmptyMap; +CX_EXPORT extern CxMap *const cxEmptyMap; /** * Deallocates the memory of the specified map. @@ -243,8 +227,7 @@ * * @param map the map to be freed */ -cx_attr_export -void cxMapFree(CxMap *map); +CX_EXPORT void cxMapFree(CxMap *map); /** @@ -255,9 +238,7 @@ * @param map the map to be cleared */ cx_attr_nonnull -static inline void cxMapClear(CxMap *map) { - map->cl->clear(map); -} +CX_EXPORT void cxMapClear(CxMap *map); /** * Returns the number of elements in this map. @@ -266,9 +247,7 @@ * @return the number of stored elements */ cx_attr_nonnull -static inline size_t cxMapSize(const CxMap *map) { - return map->collection.size; -} +CX_EXPORT size_t cxMapSize(const CxMap *map); /** * Creates a value iterator for a map. @@ -284,10 +263,7 @@ * @return an iterator for the currently stored values */ cx_attr_nodiscard -static inline CxMapIterator cxMapIteratorValues(const CxMap *map) { - if (map == NULL) map = cxEmptyMap; - return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); -} +CX_EXPORT CxMapIterator cxMapIteratorValues(const CxMap *map); /** * Creates a key iterator for a map. @@ -302,10 +278,7 @@ * @return an iterator for the currently stored keys */ cx_attr_nodiscard -static inline CxMapIterator cxMapIteratorKeys(const CxMap *map) { - if (map == NULL) map = cxEmptyMap; - return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); -} +CX_EXPORT CxMapIterator cxMapIteratorKeys(const CxMap *map); /** * Creates an iterator for a map. @@ -322,62 +295,7 @@ * @see cxMapIteratorValues() */ cx_attr_nodiscard -static inline CxMapIterator cxMapIterator(const CxMap *map) { - if (map == NULL) map = cxEmptyMap; - return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); -} - - -/** - * Creates a mutating iterator over the values of a map. - * - * 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 (can be @c NULL) - * @return an iterator for the currently stored values - */ -cx_attr_nodiscard -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, 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 - * highly depends on the map implementation and may change arbitrarily when the contents change. - * - * @param map the map to create the iterator for (can be @c NULL) - * @return an iterator for the currently stored keys - */ -cx_attr_nodiscard -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, 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 - * highly depends on the map implementation and may change arbitrarily when the contents change. - * - * @param map the map to create the iterator for (can be @c NULL) - * @return an iterator for the currently stored entries - * @see cxMapMutIteratorKeys() - * @see cxMapMutIteratorValues() - */ -cx_attr_nodiscard -cx_attr_export -CxMapIterator cxMapMutIterator(CxMap *map); +CX_EXPORT CxMapIterator cxMapIterator(const CxMap *map); /** * Puts a key/value-pair into the map. @@ -400,13 +318,7 @@ * @see cxMapPut() */ cx_attr_nonnull -static inline int cx_map_put( - CxMap *map, - CxHashKey key, - void *value -) { - return map->cl->put(map, key, value) == NULL; -} +CX_EXPORT int cx_map_put(CxMap *map, CxHashKey key, void *value); /** * Puts a key/value-pair into the map. @@ -450,12 +362,7 @@ * @see cxMapEmplace() */ cx_attr_nonnull -static inline void *cx_map_emplace( - CxMap *map, - CxHashKey key -) { - return map->cl->put(map, key, NULL); -} +CX_EXPORT void *cx_map_emplace(CxMap *map, CxHashKey key); /** * Allocates memory for a value in the map associated with the specified key. @@ -490,14 +397,8 @@ * @return the value * @see cxMapGet() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cx_map_get( - const CxMap *map, - CxHashKey key -) { - return map->cl->get(map, key); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT void *cx_map_get(const CxMap *map, CxHashKey key); /** * Retrieves a value by using a key. @@ -508,12 +409,23 @@ * * @param map (@c CxMap*) the map * @param key (any supported key type) the key - * @return (@c void*) the value + * @return (@c void*) the value or @c NULL when no value with that @p key exists * @see CX_HASH_KEY() */ #define cxMapGet(map, key) cx_map_get(map, CX_HASH_KEY(key)) /** + * Checks if a map contains a specific key. + * + * @param map (@c CxMap*) the map + * @param key (any supported key type) the key + * @retval true if the key exists in the map + * @retval false if the key does not exist in the map + * @see CX_HASH_KEY() + */ +#define cxMapContains(map, key) (cxMapGet(map, key) != NULL) + +/** * Removes a key/value-pair from the map by using the key. * * Invokes the destructor functions, if any, on the removed element if and only if the @@ -529,13 +441,7 @@ * @see cxMapRemoveAndGet() */ cx_attr_nonnull_arg(1) -static inline int cx_map_remove( - CxMap *map, - CxHashKey key, - void *targetbuf -) { - return map->cl->remove(map, key, targetbuf); -} +CX_EXPORT int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf); /** * Removes a key/value-pair from the map by using the key. @@ -574,6 +480,246 @@ */ #define cxMapRemoveAndGet(map, key, targetbuf) cx_map_remove(map, CX_HASH_KEY(key), targetbuf) + +/** + * Performs a deep clone of one map into another. + * + * If the destination map already contains entries, the cloned entries + * are added to that map, possibly overwriting existing elements when + * the keys already exist. + * + * When elements in the destination map need to be replaced, any destructor + * function is called on the replaced elements before replacing them. + * + * @attention If the cloned elements need to be destroyed by a destructor + * function, you must make sure that the destination map also uses this + * destructor function. + * + * @param dst the destination map + * @param src the source map + * @param clone_func the clone function for the values + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when all elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull_arg(1, 2, 3) +CX_EXPORT int cxMapClone(CxMap *dst, const CxMap *src, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + + +/** + * Clones entries of a map if their key is not present in another map. + * + * @param dst the destination map + * @param minuend the map to subtract the entries from + * @param subtrahend the map containing the elements to be subtracted + * @param clone_func the clone function for the values + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Clones entries of a map if their key is not present in a list. + * + * Note that the list must contain keys of type @c CxKey + * (or pointers to such keys) and must use @c cx_hash_key_cmp + * as the compare function. + * Generic key types cannot be processed in this case. + * + * @param dst the destination map + * @param src the source map + * @param keys the list of @c CxKey items + * @param clone_func the clone function for the values + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + + +/** + * Clones entries of a map only if their key is present in another map. + * + * @param dst the destination map + * @param src the map to clone the entries from + * @param other the map to check for existence of the keys + * @param clone_func the clone function for the values + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Clones entries of a map only if their key is present in a list. + * + * Note that the list must contain keys of type @c CxKey + * (or pointers to such keys) and must use @c cx_hash_key_cmp + * as the compare function. + * Generic key types cannot be processed in this case. + * + * @param dst the destination map + * @param src the source map + * @param keys the list of @c CxKey items + * @param clone_func the clone function for the values + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull_arg(1, 2, 3, 4) +CX_EXPORT int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Clones entries into a map if their key does not exist yet. + * + * If you want to calculate the union of two maps into a fresh new map, + * you can proceed as follows: + * 1. Clone the first map into a fresh, empty map. + * 2. Use this function to clone the second map into the result from step 1. + * + * @param dst the destination map + * @param src the map to clone the entries from + * @param clone_func the clone function for the values + * @param clone_allocator the allocator that is passed to the clone function + * @param data optional additional data that is passed to the clone function + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull_arg(1, 2, 3) +CX_EXPORT int cxMapUnion(CxMap *dst, const CxMap *src, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data); + +/** + * Performs a shallow clone of one map into another. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * If the destination map already contains entries, the cloned entries + * are added to that map, possibly overwriting existing elements when + * the keys already exist. + * + * When elements in the destination map need to be replaced, any destructor + * function is called on the replaced elements before replacing them. + * + * @attention If the cloned elements need to be destroyed by a destructor + * function, you must make sure that the destination map also uses this + * destructor function. + * + * @param dst the destination map + * @param src the source map + * @retval zero when all elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxMapClone() + */ +cx_attr_nonnull +CX_EXPORT int cxMapCloneSimple(CxMap *dst, const CxMap *src); + +/** + * Clones entries of a map if their key is not present in another map. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * @param dst the destination map + * @param minuend the map to subtract the entries from + * @param subtrahend the map containing the elements to be subtracted + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull +CX_EXPORT int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend); + +/** + * Clones entries of a map if their key is not present in a list. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * Note that the list must contain keys of type @c CxKey + * (or pointers to such keys) and must use @c cx_hash_key_cmp + * as the compare function. + * Generic key types cannot be processed in this case. + * + * @param dst the destination map + * @param src the source map + * @param keys the list of @c CxKey items + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + * @see cxMapListDifference() + */ +cx_attr_nonnull +CX_EXPORT int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys); + + +/** + * Clones entries of a map only if their key is present in another map. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * @param dst the destination map + * @param src the map to clone the entries from + * @param other the map to check for existence of the keys + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull +CX_EXPORT int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other); + +/** + * Clones entries of a map only if their key is present in a list. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * Note that the list must contain keys of type @c CxKey + * (or pointers to such keys) and must use @c cx_hash_key_cmp + * as the compare function. + * Generic key types cannot be processed in this case. + * + * @param dst the destination map + * @param src the source map + * @param keys the list of @c CxKey items + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull +CX_EXPORT int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys); + +/** + * Clones entries into a map if their key does not exist yet. + * + * This function uses the default allocator, if needed, and performs + * shallow clones with @c memcpy(). + * + * If you want to calculate the union of two maps into a fresh new map, + * you can proceed as follows: + * 1. Clone the first map into a fresh, empty map. + * 2. Use this function to clone the second map into the result from step 1. + * + * @param dst the destination map + * @param src the map to clone the entries from + * @retval zero when the elements were successfully cloned + * @retval non-zero when an allocation error occurred + */ +cx_attr_nonnull +CX_EXPORT int cxMapUnionSimple(CxMap *dst, const CxMap *src); + #ifdef __cplusplus } // extern "C" #endif
--- a/ucx/cx/mempool.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/mempool.h Mon Nov 10 21:52:51 2025 +0100 @@ -156,8 +156,7 @@ * * @param pool the memory pool to free */ -cx_attr_export -void cxMempoolFree(CxMempool *pool); +CX_EXPORT void cxMempoolFree(CxMempool *pool); /** * Creates an array-based memory pool. @@ -169,11 +168,8 @@ * @param type the type of memory pool * @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, enum cx_mempool_type type); +cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxMempoolFree, 1) +CX_EXPORT CxMempool *cxMempoolCreate(size_t capacity, enum cx_mempool_type type); /** * Creates a basic array-based memory pool. @@ -212,8 +208,7 @@ * @param fnc the destructor that shall be applied to all memory blocks */ cx_attr_nonnull_arg(1) -cx_attr_export -void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc); +CX_EXPORT void cxMempoolGlobalDestructor(CxMempool *pool, cx_destructor_func fnc); /** * Sets the global destructor for all memory blocks within the specified pool. @@ -223,8 +218,7 @@ * @param data additional data for the destructor function */ cx_attr_nonnull_arg(1) -cx_attr_export -void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data); +CX_EXPORT void cxMempoolGlobalDestructor2(CxMempool *pool, cx_destructor_func2 fnc, void *data); /** * Sets the destructor function for a specific allocated memory object. @@ -237,11 +231,7 @@ * @param fnc the destructor function */ cx_attr_nonnull -cx_attr_export -void cxMempoolSetDestructor( - void *memory, - cx_destructor_func fnc -); +CX_EXPORT void cxMempoolSetDestructor(void *memory, cx_destructor_func fnc); /** * Sets the destructor function for a specific allocated memory object. @@ -255,12 +245,7 @@ * @param data additional data for the destructor function */ cx_attr_nonnull -cx_attr_export -void cxMempoolSetDestructor2( - void *memory, - cx_destructor_func2 fnc, - void *data -); +CX_EXPORT void cxMempoolSetDestructor2(void *memory, cx_destructor_func2 fnc, void *data); /** * Removes the destructor function for a specific allocated memory object. @@ -271,8 +256,7 @@ * @param memory the object allocated in the pool */ cx_attr_nonnull -cx_attr_export -void cxMempoolRemoveDestructor(void *memory); +CX_EXPORT void cxMempoolRemoveDestructor(void *memory); /** * Removes the destructor function for a specific allocated memory object. @@ -283,8 +267,7 @@ * @param memory the object allocated in the pool */ cx_attr_nonnull -cx_attr_export -void cxMempoolRemoveDestructor2(void *memory); +CX_EXPORT void cxMempoolRemoveDestructor2(void *memory); /** * Registers foreign memory with this pool. @@ -302,12 +285,7 @@ * @retval non-zero failure */ cx_attr_nonnull -cx_attr_export -int cxMempoolRegister( - CxMempool *pool, - void *memory, - cx_destructor_func destr -); +CX_EXPORT int cxMempoolRegister(CxMempool *pool, void *memory, cx_destructor_func destr); /** @@ -330,13 +308,7 @@ * @retval non-zero failure */ cx_attr_nonnull -cx_attr_export -int cxMempoolRegister2( - CxMempool *pool, - void *memory, - cx_destructor_func2 destr, - void *data -); +CX_EXPORT int cxMempoolRegister2(CxMempool *pool, void *memory, cx_destructor_func2 destr, void *data); /** * Transfers all the memory managed by one pool to another. @@ -354,11 +326,7 @@ * @retval non-zero allocation failure or incompatible pools */ cx_attr_nonnull -cx_attr_export -int cxMempoolTransfer( - CxMempool *source, - CxMempool *dest -); +CX_EXPORT int cxMempoolTransfer(CxMempool *source, CxMempool *dest); /** * Transfers an object from one pool to another. @@ -375,12 +343,7 @@ * @retval non-zero failure, or the object was not found in the source pool, or the pools are incompatible */ cx_attr_nonnull -cx_attr_export -int cxMempoolTransferObject( - CxMempool *source, - CxMempool *dest, - const void *obj -); +CX_EXPORT int cxMempoolTransferObject(CxMempool *source, CxMempool *dest, const void *obj); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/printf.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/printf.h Mon Nov 10 21:52:51 2025 +0100 @@ -56,8 +56,7 @@ /** * The maximum string length that fits into stack memory. */ -cx_attr_export -extern const unsigned cx_printf_sbo_size; +CX_EXPORT extern const unsigned cx_printf_sbo_size; /** * A @c fprintf like function which writes the output to a stream by @@ -69,16 +68,8 @@ * @param ... additional arguments * @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, - const char *fmt, - ... -); +cx_attr_nonnull_arg(1, 2, 3) cx_attr_printf(3, 4) cx_attr_cstr_arg(3) +CX_EXPORT int cx_fprintf(void *stream, cx_write_func wfc, const char *fmt, ...); /** * A @c vfprintf like function which writes the output to a stream by @@ -91,15 +82,8 @@ * @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, - const char *fmt, - va_list ap -); +cx_attr_nonnull cx_attr_cstr_arg(3) +CX_EXPORT int cx_vfprintf(void *stream, cx_write_func wfc, const char *fmt, va_list ap); /** * An @c asprintf like function which allocates space for a string @@ -115,15 +99,8 @@ * @return the formatted string * @see cx_strfree_a() */ -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, - ... -); +cx_attr_nonnull_arg(1, 2) cx_attr_printf(2, 3) cx_attr_cstr_arg(2) +CX_EXPORT cxmutstr cx_asprintf_a(const CxAllocator *allocator, const char *fmt, ...); /** * An @c asprintf like function which allocates space for a string @@ -138,8 +115,7 @@ * @return (@c cxmutstr) the formatted string * @see cx_strfree() */ -#define cx_asprintf(fmt, ...) \ - cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__) +#define cx_asprintf(fmt, ...) cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__) /** * A @c vasprintf like function which allocates space for a string @@ -155,14 +131,8 @@ * @return the formatted string * @see cx_asprintf_a() */ -cx_attr_nonnull -cx_attr_cstr_arg(2) -cx_attr_export -cxmutstr cx_vasprintf_a( - const CxAllocator *allocator, - const char *fmt, - va_list ap -); +cx_attr_nonnull cx_attr_cstr_arg(2) +CX_EXPORT cxmutstr cx_vasprintf_a(const CxAllocator *allocator, const char *fmt, va_list ap); /** * A @c vasprintf like function which allocates space for a string @@ -189,8 +159,7 @@ * @see cx_fprintf() * @see cxBufferWrite() */ -#define cx_bprintf(buffer, fmt, ...) cx_fprintf((void*)buffer, \ - cxBufferWriteFunc, fmt, __VA_ARGS__) +#define cx_bprintf(buffer, fmt, ...) cx_fprintf((void*)buffer, cxBufferWriteFunc, fmt, __VA_ARGS__) /** @@ -224,17 +193,8 @@ * @param ... additional arguments * @return the length of the 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( - const CxAllocator *alloc, - char **str, - size_t *len, - const char *fmt, - ... -); +cx_attr_nonnull_arg(1, 2, 3, 4) cx_attr_printf(4, 5) cx_attr_cstr_arg(4) +CX_EXPORT int cx_sprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, ...); /** @@ -268,18 +228,8 @@ * @param ap argument list * @return the length of the 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( - const CxAllocator *alloc, - char **str, - size_t *len, - const char *fmt, - va_list ap -); +cx_attr_nonnull cx_attr_cstr_arg(4) cx_attr_access_rw(2) cx_attr_access_rw(3) +CX_EXPORT int cx_vsprintf_a(const CxAllocator *alloc, char **str, size_t *len, const char *fmt, va_list ap); /** @@ -325,21 +275,9 @@ * @param ... additional arguments * @return the length of the produced string or an error code from stdlib printf implementation */ -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( - const CxAllocator *alloc, - char *buf, - size_t *len, - char **str, - const char *fmt, - ... -); +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_EXPORT int cx_sprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, ...); /** * An @c sprintf like function which allocates a new string when the buffer is not large enough. @@ -384,17 +322,8 @@ * @param ap argument list * @return the length of the 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( - const CxAllocator *alloc, - char *buf, - size_t *len, - char **str, - const char *fmt, - va_list ap -); +cx_attr_nonnull cx_attr_cstr_arg(5) +CX_EXPORT int cx_vsprintf_sa(const CxAllocator *alloc, char *buf, size_t *len, char **str, const char *fmt, va_list ap); #ifdef __cplusplus
--- a/ucx/cx/properties.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/properties.h Mon Nov 10 21:52:51 2025 +0100 @@ -94,8 +94,7 @@ /** * Default properties configuration. */ -cx_attr_export -extern const CxPropertiesConfig cx_properties_config_default; +CX_EXPORT extern const CxPropertiesConfig cx_properties_config_default; /** * Status codes for the properties interface. @@ -210,7 +209,6 @@ * @retval zero success * @retval non-zero sinking the k/v-pair failed */ -cx_attr_nonnull typedef int(*cx_properties_sink_func)( CxProperties *prop, CxPropertiesSink *sink, @@ -257,7 +255,6 @@ * @retval zero success * @retval non-zero reading the data failed */ -cx_attr_nonnull typedef int(*cx_properties_read_func)( CxProperties *prop, CxPropertiesSource *src, @@ -272,7 +269,6 @@ * @retval zero initialization was successful * @retval non-zero otherwise */ -cx_attr_nonnull typedef int(*cx_properties_read_init_func)( CxProperties *prop, CxPropertiesSource *src @@ -284,7 +280,6 @@ * @param prop the properties interface that wants to read from the source * @param src the source */ -cx_attr_nonnull typedef void(*cx_properties_read_clean_func)( CxProperties *prop, CxPropertiesSource *src @@ -331,8 +326,7 @@ * @see cxPropertiesInitDefault() */ cx_attr_nonnull -cx_attr_export -void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config); +CX_EXPORT void cxPropertiesInit(CxProperties *prop, CxPropertiesConfig config); /** * Destroys the properties interface. @@ -346,8 +340,7 @@ * @param prop the properties interface */ cx_attr_nonnull -cx_attr_export -void cxPropertiesDestroy(CxProperties *prop); +CX_EXPORT void cxPropertiesDestroy(CxProperties *prop); /** * Destroys and re-initializes the properties interface. @@ -358,11 +351,7 @@ * @param prop the properties interface */ cx_attr_nonnull -static inline void cxPropertiesReset(CxProperties *prop) { - CxPropertiesConfig config = prop->config; - cxPropertiesDestroy(prop); - cxPropertiesInit(prop, config); -} +CX_EXPORT void cxPropertiesReset(CxProperties *prop); /** * Initialize a properties parser with the default configuration. @@ -371,7 +360,7 @@ * @see cxPropertiesInit() */ #define cxPropertiesInitDefault(prop) \ - cxPropertiesInit(prop, cx_properties_config_default) + cxPropertiesInit(prop, cx_properties_config_default) /** * Fills the input buffer with data. @@ -394,14 +383,8 @@ * @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, - size_t len -); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT int cxPropertiesFilln(CxProperties *prop, const char *buf, size_t len); /** * Internal function, do not use. @@ -412,10 +395,7 @@ * @retval non-zero a memory allocation was necessary but failed */ cx_attr_nonnull -static inline int cx_properties_fill( - CxProperties *prop, - cxstring str -) { +CX_INLINE int cx_properties_fill(CxProperties *prop, cxstring str) { return cxPropertiesFilln(prop, str.ptr, str.length); } @@ -449,12 +429,7 @@ * @param capacity the capacity of the stack memory */ cx_attr_nonnull -cx_attr_export -void cxPropertiesUseStack( - CxProperties *prop, - char *buf, - size_t capacity -); +CX_EXPORT void cxPropertiesUseStack(CxProperties *prop, char *buf, size_t capacity); /** * Retrieves the next key/value-pair. @@ -486,14 +461,8 @@ * @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, - cxstring *value -); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxPropertiesStatus cxPropertiesNext(CxProperties *prop, cxstring *key, cxstring *value); /** * Creates a properties sink for an UCX map. @@ -509,10 +478,8 @@ * @return the sink * @see cxPropertiesLoad() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -CxPropertiesSink cxPropertiesMapSink(CxMap *map); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxPropertiesSink cxPropertiesMapSink(CxMap *map); /** * Creates a properties source based on an UCX string. @@ -522,8 +489,7 @@ * @see cxPropertiesLoad() */ cx_attr_nodiscard -cx_attr_export -CxPropertiesSource cxPropertiesStringSource(cxstring str); +CX_EXPORT CxPropertiesSource cxPropertiesStringSource(cxstring str); /** * Creates a properties source based on C string with the specified length. @@ -533,11 +499,8 @@ * @return the properties source * @see cxPropertiesLoad() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_access_r(1, 2) -cx_attr_export -CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len); +cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1, 2) +CX_EXPORT CxPropertiesSource cxPropertiesCstrnSource(const char *str, size_t len); /** * Creates a properties source based on a C string. @@ -549,11 +512,8 @@ * @return the properties source * @see cxPropertiesLoad() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -CxPropertiesSource cxPropertiesCstrSource(const char *str); +cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT CxPropertiesSource cxPropertiesCstrSource(const char *str); /** * Creates a properties source based on an FILE. @@ -564,11 +524,8 @@ * @return the properties source * @see cxPropertiesLoad() */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_access_r(1) -cx_attr_export -CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size); +cx_attr_nonnull cx_attr_nodiscard cx_attr_access_r(1) +CX_EXPORT CxPropertiesSource cxPropertiesFileSource(FILE *file, size_t chunk_size); /** @@ -600,12 +557,8 @@ * @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, - CxPropertiesSource source -); +CX_EXPORT CxPropertiesStatus cxPropertiesLoad(CxProperties *prop, + CxPropertiesSink sink, CxPropertiesSource source); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/streams.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/streams.h Mon Nov 10 21:52:51 2025 +0100 @@ -62,19 +62,10 @@ * @return the total number of bytes copied */ cx_attr_nonnull_arg(1, 2, 3, 4) -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, - cx_read_func rfnc, - cx_write_func wfnc, - char *buf, - size_t bufsize, - size_t n -); +cx_attr_access_r(1) cx_attr_access_w(2) cx_attr_access_w(5) +CX_EXPORT size_t cx_stream_bncopy(void *src, void *dest, + cx_read_func rfnc, cx_write_func wfnc, + char *buf, size_t bufsize, size_t n); /** * Reads data from a stream and writes it to another stream. @@ -104,17 +95,9 @@ * @param n the maximum number of bytes that shall be copied. * @return total number of bytes copied */ -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, - cx_read_func rfnc, - cx_write_func wfnc, - size_t n -); +cx_attr_nonnull cx_attr_access_r(1) cx_attr_access_w(2) +CX_EXPORT size_t cx_stream_ncopy(void *src, void *dest, + cx_read_func rfnc, cx_write_func wfnc, size_t n); /** * Reads data from a stream and writes it to another stream.
--- a/ucx/cx/string.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/string.h Mon Nov 10 21:52:51 2025 +0100 @@ -48,8 +48,7 @@ /** * The maximum length of the "needle" in cx_strstr() that can use SBO. */ -cx_attr_export -extern const unsigned cx_strstr_sbo_size; +CX_EXPORT extern const unsigned cx_strstr_sbo_size; /** * The UCX string structure. @@ -179,10 +178,8 @@ * * @see cx_mutstrn() */ -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -cxmutstr cx_mutstr(char *cstring); +cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT cxmutstr cx_mutstr(char *cstring); /** * Wraps a string that does not need to be zero-terminated. @@ -200,13 +197,8 @@ * * @see cx_mutstr() */ -cx_attr_nodiscard -cx_attr_access_rw(1, 2) -cx_attr_export -cxmutstr cx_mutstrn( - char *cstring, - size_t length -); +cx_attr_nodiscard cx_attr_access_rw(1, 2) +CX_EXPORT cxmutstr cx_mutstrn(char *cstring, size_t length); /** * Wraps a string that must be zero-terminated. @@ -225,10 +217,8 @@ * * @see cx_strn() */ -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_export -cxstring cx_str(const char *cstring); +cx_attr_nodiscard cx_attr_cstr_arg(1) +CX_EXPORT cxstring cx_str(const char *cstring); /** @@ -247,28 +237,27 @@ * * @see cx_str() */ -cx_attr_nodiscard -cx_attr_access_r(1, 2) -cx_attr_export -cxstring cx_strn( - const char *cstring, - size_t length -); +cx_attr_nodiscard cx_attr_access_r(1, 2) +CX_EXPORT cxstring cx_strn(const char *cstring, size_t length); #ifdef __cplusplus } // extern "C" cx_attr_nodiscard -static inline cxstring cx_strcast(cxmutstr str) { +CX_CPPDECL cxstring cx_strcast(cxmutstr str) { return cx_strn(str.ptr, str.length); } cx_attr_nodiscard -static inline cxstring cx_strcast(cxstring str) { +CX_CPPDECL cxstring cx_strcast(cxstring str) { return str; } cx_attr_nodiscard -static inline cxstring cx_strcast(const char *str) { +CX_CPPDECL cxstring cx_strcast(const char *str) { return cx_str(str); } +cx_attr_nodiscard +CX_CPPDECL cxstring cx_strcast(const unsigned char *str) { + return cx_str(static_cast<const char*>(str)); +} extern "C" { #else /** @@ -278,7 +267,7 @@ * @see cx_strcast() */ cx_attr_nodiscard -static inline cxstring cx_strcast_m(cxmutstr str) { +CX_INLINE cxstring cx_strcast_m(cxmutstr str) { return (cxstring) {str.ptr, str.length}; } /** @@ -288,7 +277,7 @@ * @see cx_strcast() */ cx_attr_nodiscard -static inline cxstring cx_strcast_c(cxstring str) { +CX_INLINE cxstring cx_strcast_c(cxstring str) { return str; } @@ -299,7 +288,18 @@ * @see cx_strcast() */ cx_attr_nodiscard -static inline cxstring cx_strcast_z(const char *str) { +CX_INLINE cxstring cx_strcast_u(const unsigned char *str) { + return cx_str((const char*)str); +} + +/** + * Internal function, do not use. + * @param str + * @return + * @see cx_strcast() + */ +cx_attr_nodiscard +CX_INLINE cxstring cx_strcast_z(const char *str) { return cx_str(str); } @@ -312,8 +312,8 @@ #define cx_strcast(str) _Generic((str), \ cxmutstr: cx_strcast_m, \ cxstring: cx_strcast_c, \ - const unsigned char*: cx_strcast_z, \ - unsigned char *: cx_strcast_z, \ + const unsigned char*: cx_strcast_u, \ + unsigned char *: cx_strcast_u, \ const char*: cx_strcast_z, \ char *: cx_strcast_z) (str) #endif @@ -330,8 +330,7 @@ * * @param str the string to free */ -cx_attr_export -void cx_strfree(cxmutstr *str); +CX_EXPORT void cx_strfree(cxmutstr *str); /** * Passes the pointer in this string to the allocator's free function. @@ -347,11 +346,7 @@ * @param str the string to free */ cx_attr_nonnull_arg(1) -cx_attr_export -void cx_strfree_a( - const CxAllocator *alloc, - cxmutstr *str -); +CX_EXPORT void cx_strfree_a(const CxAllocator *alloc, cxmutstr *str); /** * Copies a string. @@ -369,12 +364,7 @@ * @retval non-zero if re-allocation failed */ cx_attr_nonnull_arg(1) -cx_attr_export -int cx_strcpy_a( - const CxAllocator *alloc, - cxmutstr *dest, - cxstring src -); +CX_EXPORT int cx_strcpy_a(const CxAllocator *alloc, cxmutstr *dest, cxstring src); /** @@ -406,11 +396,7 @@ * @return the accumulated length of all strings */ cx_attr_nodiscard -cx_attr_export -size_t cx_strlen( - size_t count, - ... -); +CX_EXPORT size_t cx_strlen(size_t count, ...); /** * Concatenates strings. @@ -434,15 +420,9 @@ * @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, - size_t count, - ... -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT cxmutstr cx_strcat_ma(const CxAllocator *alloc, + cxmutstr str, size_t count, ...); /** * Concatenates strings and returns a new string. @@ -463,7 +443,7 @@ * @return (@c cxmutstr) the concatenated string */ #define cx_strcat_a(alloc, count, ...) \ -cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + cx_strcat_ma(alloc, cx_mutstrn(NULL, 0), count, __VA_ARGS__) /** * Concatenates strings and returns a new string. @@ -483,7 +463,7 @@ * @return (@c cxmutstr) the concatenated string */ #define cx_strcat(count, ...) \ -cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) + cx_strcat_ma(cxDefaultAllocator, cx_mutstrn(NULL, 0), count, __VA_ARGS__) /** * Concatenates strings. @@ -507,7 +487,7 @@ * @return (@c cxmutstr) the concatenated string */ #define cx_strcat_m(str, count, ...) \ -cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__) + cx_strcat_ma(cxDefaultAllocator, str, count, __VA_ARGS__) /** * Returns a substring starting at the specified location. @@ -525,11 +505,7 @@ * @see cx_strsubsl_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strsubs( - cxstring string, - size_t start -); +CX_EXPORT cxstring cx_strsubs(cxstring string, size_t start); /** * Returns a substring starting at the specified location. @@ -551,12 +527,7 @@ * @see cx_strsubsl_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strsubsl( - cxstring string, - size_t start, - size_t length -); +CX_EXPORT cxstring cx_strsubsl(cxstring string, size_t start, size_t length); /** * Returns a substring starting at the specified location. @@ -574,11 +545,7 @@ * @see cx_strsubsl() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strsubs_m( - cxmutstr string, - size_t start -); +CX_EXPORT cxmutstr cx_strsubs_m(cxmutstr string, size_t start); /** * Returns a substring starting at the specified location. @@ -600,12 +567,7 @@ * @see cx_strsubsl() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strsubsl_m( - cxmutstr string, - size_t start, - size_t length -); +CX_EXPORT cxmutstr cx_strsubsl_m(cxmutstr string, size_t start, size_t length); /** * Returns a substring starting at the location of the first occurrence of the @@ -620,11 +582,7 @@ * @see cx_strchr_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strchr( - cxstring string, - int chr -); +CX_EXPORT cxstring cx_strchr(cxstring string, int chr); /** * Returns a substring starting at the location of the first occurrence of the @@ -639,11 +597,7 @@ * @see cx_strchr() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strchr_m( - cxmutstr string, - int chr -); +CX_EXPORT cxmutstr cx_strchr_m(cxmutstr string, int chr); /** * Returns a substring starting at the location of the last occurrence of the @@ -658,11 +612,7 @@ * @see cx_strrchr_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strrchr( - cxstring string, - int chr -); +CX_EXPORT cxstring cx_strrchr(cxstring string, int chr); /** * Returns a substring starting at the location of the last occurrence of the @@ -677,11 +627,7 @@ * @see cx_strrchr() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strrchr_m( - cxmutstr string, - int chr -); +CX_EXPORT cxmutstr cx_strrchr_m(cxmutstr string, int chr); /** * Returns a substring starting at the location of the first occurrence of the @@ -700,11 +646,7 @@ * @see cx_strstr_m() */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strstr( - cxstring haystack, - cxstring needle -); +CX_EXPORT cxstring cx_strstr(cxstring haystack, cxstring needle); /** * Returns a substring starting at the location of the first occurrence of the @@ -723,11 +665,7 @@ * @see cx_strstr() */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strstr_m( - cxmutstr haystack, - cxstring needle -); +CX_EXPORT cxmutstr cx_strstr_m(cxmutstr haystack, cxstring needle); /** * Splits a given string using a delimiter string. @@ -741,16 +679,9 @@ * @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, - size_t limit, - cxstring *output -); +cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3) +CX_EXPORT size_t cx_strsplit(cxstring string, cxstring delim, + size_t limit, cxstring *output); /** * Splits a given string using a delimiter string. @@ -771,17 +702,10 @@ * written to * @return the actual number of split items */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_access_w(5) -cx_attr_export -size_t cx_strsplit_a( - const CxAllocator *allocator, - cxstring string, - cxstring delim, - size_t limit, - cxstring **output -); +cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5) +CX_EXPORT size_t cx_strsplit_a(const CxAllocator *allocator, + cxstring string, cxstring delim, + size_t limit, cxstring **output); /** @@ -796,16 +720,9 @@ * @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, - size_t limit, - cxmutstr *output -); +cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(4, 3) +CX_EXPORT size_t cx_strsplit_m(cxmutstr string, cxstring delim, + size_t limit, cxmutstr *output); /** * Splits a given string using a delimiter string. @@ -826,17 +743,10 @@ * written to * @return the actual number of split items */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_access_w(5) -cx_attr_export -size_t cx_strsplit_ma( - const CxAllocator *allocator, - cxmutstr string, - cxstring delim, - size_t limit, - cxmutstr **output -); +cx_attr_nodiscard cx_attr_nonnull cx_attr_access_w(5) +CX_EXPORT size_t cx_strsplit_ma(const CxAllocator *allocator, + cxmutstr string, cxstring delim, size_t limit, + cxmutstr **output); /** * Compares two strings. @@ -847,11 +757,17 @@ * than @p s2, zero if both strings equal */ cx_attr_nodiscard -cx_attr_export -int cx_strcmp( - cxstring s1, - cxstring s2 -); +CX_EXPORT int cx_strcmp_(cxstring s1, cxstring s2); + +/** + * Compares two strings. + * + * @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 + */ +#define cx_strcmp(s1, s2) cx_strcmp_(cx_strcast(s1), cx_strcast(s2)) /** * Compares two strings ignoring case. @@ -862,29 +778,33 @@ * than @p s2, zero if both strings equal ignoring case */ cx_attr_nodiscard -cx_attr_export -int cx_strcasecmp( - cxstring s1, - cxstring s2 -); +CX_EXPORT int cx_strcasecmp_(cxstring s1, cxstring s2); + +/** + * Compares two strings ignoring case. + * + * @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 + */ +#define cx_strcasecmp(s1, s2) cx_strcasecmp_(cx_strcast(s1), cx_strcast(s2)) /** * Compares two strings. * * This function has a compatible signature for the use as a cx_compare_func. * + * @attention This function can @em only compare UCX strings. It is unsafe to + * pass normal C-strings to this function. + * * @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 */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -int cx_strcmp_p( - const void *s1, - const void *s2 -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cx_strcmp_p(const void *s1, const void *s2); /** * Compares two strings ignoring case. @@ -896,13 +816,8 @@ * @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 -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT int cx_strcasecmp_p(const void *s1, const void *s2); /** @@ -917,13 +832,8 @@ * @return a duplicate of the string * @see cx_strdup() */ -cx_attr_nodiscard -cx_attr_nonnull -cx_attr_export -cxmutstr cx_strdup_a_( - const CxAllocator *allocator, - cxstring string -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT cxmutstr cx_strdup_a_(const CxAllocator *allocator, cxstring string); /** * Creates a duplicate of the specified string. @@ -938,8 +848,7 @@ * @see cx_strdup() * @see cx_strfree_a() */ -#define cx_strdup_a(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. @@ -966,8 +875,7 @@ * @return the trimmed string */ cx_attr_nodiscard -cx_attr_export -cxstring cx_strtrim(cxstring string); +CX_EXPORT cxstring cx_strtrim(cxstring string); /** * Omits leading and trailing spaces. @@ -979,8 +887,7 @@ * @return the trimmed string */ cx_attr_nodiscard -cx_attr_export -cxmutstr cx_strtrim_m(cxmutstr string); +CX_EXPORT cxmutstr cx_strtrim_m(cxmutstr string); /** * Checks if a string has a specific prefix. @@ -991,11 +898,17 @@ * @c false otherwise */ cx_attr_nodiscard -cx_attr_export -bool cx_strprefix( - cxstring string, - cxstring prefix -); +CX_EXPORT bool cx_strprefix_(cxstring string, cxstring prefix); + +/** + * Checks if a string has a specific prefix. + * + * @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 + */ +#define cx_strprefix(string, prefix) cx_strprefix_(cx_strcast(string), cx_strcast(prefix)) /** * Checks if a string has a specific suffix. @@ -1006,11 +919,17 @@ * @c false otherwise */ cx_attr_nodiscard -cx_attr_export -bool cx_strsuffix( - cxstring string, - cxstring suffix -); +CX_EXPORT bool cx_strsuffix_(cxstring string, cxstring suffix); + +/** + * Checks if a string has a specific suffix. + * + * @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 + */ +#define cx_strsuffix(string, suffix) cx_strsuffix_(cx_strcast(string), cx_strcast(suffix)) /** * Checks if a string has a specific prefix, ignoring the case. @@ -1021,11 +940,17 @@ * @c false otherwise */ cx_attr_nodiscard -cx_attr_export -bool cx_strcaseprefix( - cxstring string, - cxstring prefix -); +CX_EXPORT bool cx_strcaseprefix_(cxstring string, cxstring prefix); + +/** + * Checks if a string has a specific prefix, ignoring the case. + * + * @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 + */ +#define cx_strcaseprefix(string, prefix) cx_strcaseprefix_(cx_strcast(string), cx_strcast(prefix)) /** * Checks, if a string has a specific suffix, ignoring the case. @@ -1036,11 +961,17 @@ * @c false otherwise */ cx_attr_nodiscard -cx_attr_export -bool cx_strcasesuffix( - cxstring string, - cxstring suffix -); +CX_EXPORT bool cx_strcasesuffix_(cxstring string, cxstring suffix); + +/** + * Checks, if a string has a specific suffix, ignoring the case. + * + * @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 + */ +#define cx_strcasesuffix(string, suffix) cx_strcasesuffix_(cx_strcast(string), cx_strcast(suffix)) /** * Replaces a string with another string. @@ -1060,16 +991,9 @@ * @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 search, - cxstring replacement, - size_t replmax -); +cx_attr_nodiscard cx_attr_nonnull +CX_EXPORT cxmutstr cx_strreplacen_a(const CxAllocator *allocator, + cxstring str, cxstring search, cxstring replacement, size_t replmax); /** * Replaces a string with another string. @@ -1089,7 +1013,7 @@ * @return (@c cxmutstr) the resulting string after applying the replacements */ #define cx_strreplacen(str, search, replacement, replmax) \ -cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax) + cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, replmax) /** * Replaces a string with another string. @@ -1107,7 +1031,7 @@ * @return (@c cxmutstr) the resulting string after applying the replacements */ #define cx_strreplace_a(allocator, str, search, replacement) \ -cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX) + cx_strreplacen_a(allocator, str, search, replacement, SIZE_MAX) /** * Replaces a string with another string. @@ -1124,7 +1048,7 @@ * @return (@c cxmutstr) the resulting string after applying the replacements */ #define cx_strreplace(str, search, replacement) \ -cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX) + cx_strreplacen_a(cxDefaultAllocator, str, search, replacement, SIZE_MAX) /** * Creates a string tokenization context. @@ -1135,12 +1059,7 @@ * @return a new string tokenization context */ cx_attr_nodiscard -cx_attr_export -CxStrtokCtx cx_strtok_( - cxstring str, - cxstring delim, - size_t limit -); +CX_EXPORT CxStrtokCtx cx_strtok_(cxstring str, cxstring delim, size_t limit); /** * Creates a string tokenization context. @@ -1151,7 +1070,7 @@ * @return (@c CxStrtokCtx) a new string tokenization context */ #define cx_strtok(str, delim, limit) \ - cx_strtok_(cx_strcast(str), cx_strcast(delim), (limit)) + cx_strtok_(cx_strcast(str), cx_strcast(delim), (limit)) /** * Returns the next token. @@ -1163,14 +1082,8 @@ * @return true if successful, false if the limit or the end of the string * has been reached */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_access_w(2) -cx_attr_export -bool cx_strtok_next( - CxStrtokCtx *ctx, - cxstring *token -); +cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2) +CX_EXPORT bool cx_strtok_next(CxStrtokCtx *ctx, cxstring *token); /** * Returns the next token of a mutable string. @@ -1186,14 +1099,8 @@ * @return true if successful, false if the limit or the end of the string * has been reached */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_access_w(2) -cx_attr_export -bool cx_strtok_next_m( - CxStrtokCtx *ctx, - cxmutstr *token -); +cx_attr_nonnull cx_attr_nodiscard cx_attr_access_w(2) +CX_EXPORT bool cx_strtok_next_m(CxStrtokCtx *ctx, cxmutstr *token); /** * Defines an array of more delimiters for the specified tokenization context. @@ -1202,14 +1109,8 @@ * @param delim array of more delimiters * @param count number of elements in the array */ -cx_attr_nonnull -cx_attr_access_r(2, 3) -cx_attr_export -void cx_strtok_delim( - CxStrtokCtx *ctx, - const cxstring *delim, - size_t count -); +cx_attr_nonnull cx_attr_access_r(2, 3) +CX_EXPORT void cx_strtok_delim(CxStrtokCtx *ctx, const cxstring *delim, size_t count); /* ------------------------------------------------------------------------- * * string to number conversion functions * @@ -1229,8 +1130,8 @@ * @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_strtos_lc_(cxstring str, short *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtos_lc_(cxstring str, short *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1246,8 +1147,8 @@ * @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_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi_lc_(cxstring str, int *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1263,8 +1164,8 @@ * @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_strtol_lc_(cxstring str, long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtol_lc_(cxstring str, long *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1280,8 +1181,8 @@ * @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_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoll_lc_(cxstring str, long long *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1297,8 +1198,8 @@ * @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_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi8_lc_(cxstring str, int8_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1314,8 +1215,8 @@ * @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_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi16_lc_(cxstring str, int16_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1331,8 +1232,8 @@ * @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_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi32_lc_(cxstring str, int32_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1348,8 +1249,8 @@ * @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_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoi64_lc_(cxstring str, int64_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1365,8 +1266,8 @@ * @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_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtous_lc_(cxstring str, unsigned short *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1382,8 +1283,8 @@ * @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_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou_lc_(cxstring str, unsigned int *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1399,8 +1300,8 @@ * @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_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoul_lc_(cxstring str, unsigned long *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1416,8 +1317,8 @@ * @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_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtoull_lc_(cxstring str, unsigned long long *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1433,8 +1334,8 @@ * @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_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou8_lc_(cxstring str, uint8_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1450,8 +1351,8 @@ * @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_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou16_lc_(cxstring str, uint16_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1467,8 +1368,8 @@ * @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_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou32_lc_(cxstring str, uint32_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1484,8 +1385,8 @@ * @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_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtou64_lc_(cxstring str, uint64_t *output, int base, const char *groupsep); /** * Converts a string to a number. @@ -1501,8 +1402,8 @@ * @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); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_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. @@ -1518,8 +1419,8 @@ * @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); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtof_lc_(cxstring str, float *output, char decsep, const char *groupsep); /** * Converts a string to a double precision floating-point number. @@ -1535,8 +1436,8 @@ * @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); +cx_attr_access_w(2) cx_attr_nonnull_arg(2) +CX_EXPORT int cx_strtod_lc_(cxstring str, double *output, char decsep, const char *groupsep); /** * Converts a string to a number.
--- a/ucx/cx/test.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/test.h Mon Nov 10 21:52:51 2025 +0100 @@ -136,10 +136,7 @@ * @param name optional name of the suite * @return a new test suite */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_cstr_arg(1) -cx_attr_malloc +cx_attr_nonnull cx_attr_nodiscard cx_attr_cstr_arg(1) cx_attr_malloc static inline CxTestSuite* cx_test_suite_new(const char *name) { CxTestSuite* suite = (CxTestSuite*) malloc(sizeof(CxTestSuite)); if (suite != NULL) { @@ -157,7 +154,7 @@ * * @param suite the test suite to free */ -static inline void cx_test_suite_free(CxTestSuite* suite) { +CX_INLINE void cx_test_suite_free(CxTestSuite* suite) { if (suite == NULL) return; CxTestSet *l = suite->tests; while (l != NULL) { @@ -177,7 +174,7 @@ * @retval non-zero failure */ cx_attr_nonnull -static inline int cx_test_register(CxTestSuite* suite, CxTest test) { +CX_INLINE int cx_test_register(CxTestSuite* suite, CxTest test) { CxTestSet *t = (CxTestSet*) malloc(sizeof(CxTestSet)); if (t) { t->test = test; @@ -204,8 +201,7 @@ * @param out_writer the write function writing to @p out_target */ cx_attr_nonnull -static inline void cx_test_run(CxTestSuite *suite, - void *out_target, cx_write_func out_writer) { +CX_INLINE void cx_test_run(CxTestSuite *suite, void *out_target, cx_write_func out_writer) { if (suite->name == NULL) { out_writer("*** Test Suite ***\n", 1, 19, out_target); } else {
--- a/ucx/cx/tree.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/cx/tree.h Mon Nov 10 21:52:51 2025 +0100 @@ -212,24 +212,14 @@ * @param iter the iterator */ cx_attr_nonnull -static inline void cxTreeIteratorDispose(CxTreeIterator *iter) { - cxFreeDefault(iter->stack); - iter->stack = NULL; -} +CX_EXPORT void cxTreeIteratorDispose(CxTreeIterator *iter); /** * Releases internal memory of the given tree visitor. * @param visitor the visitor */ cx_attr_nonnull -static inline void cxTreeVisitorDispose(CxTreeVisitor *visitor) { - struct cx_tree_visitor_queue_s *q = visitor->queue_next; - while (q != NULL) { - struct cx_tree_visitor_queue_s *next = q->next; - cxFreeDefault(q); - q = next; - } -} +CX_EXPORT void cxTreeVisitorDispose(CxTreeVisitor *visitor); /** * Advises the iterator to skip the subtree below the current node and @@ -265,16 +255,9 @@ * @see cx_tree_unlink() */ cx_attr_nonnull -cx_attr_export -void cx_tree_link( - void *parent, - void *node, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_tree_link(void *parent, void *node, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Unlinks a node from its parent. @@ -291,15 +274,9 @@ * @see cx_tree_link() */ cx_attr_nonnull -cx_attr_export -void cx_tree_unlink( - void *node, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +CX_EXPORT void cx_tree_unlink(void *node, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Macro that can be used instead of the magic value for infinite search depth. @@ -332,7 +309,6 @@ * positive if one of the children might contain the data, * negative if neither the node nor the children contains the data */ -cx_attr_nonnull typedef int (*cx_tree_search_data_func)(const void *node, const void *data); @@ -362,7 +338,6 @@ * positive if one of the children might contain the data, * negative if neither the node nor the children contains the data */ -cx_attr_nonnull typedef int (*cx_tree_search_func)(const void *node, const void *new_node); /** @@ -389,18 +364,10 @@ * could contain the node (but doesn't right now), negative if the tree does not * contain any node that might be related to the searched data */ -cx_attr_nonnull -cx_attr_access_w(5) -cx_attr_export -int cx_tree_search_data( - const void *root, - size_t depth, - const void *data, - cx_tree_search_data_func sfunc, - void **result, - ptrdiff_t loc_children, - ptrdiff_t loc_next -); +cx_attr_nonnull cx_attr_access_w(5) +CX_EXPORT int cx_tree_search_data(const void *root, size_t depth, + const void *data, cx_tree_search_data_func sfunc, + void **result, ptrdiff_t loc_children, ptrdiff_t loc_next); /** * Searches for a node in a tree. @@ -426,18 +393,10 @@ * could contain the node (but doesn't right now), negative if the tree does not * contain any node that might be related to the searched data */ -cx_attr_nonnull -cx_attr_access_w(5) -cx_attr_export -int cx_tree_search( - const void *root, - size_t depth, - const void *node, - cx_tree_search_func sfunc, - void **result, - ptrdiff_t loc_children, - ptrdiff_t loc_next -); +cx_attr_nonnull cx_attr_access_w(5) +CX_EXPORT int cx_tree_search(const void *root, size_t depth, + const void *node, cx_tree_search_func sfunc, + void **result, ptrdiff_t loc_children, ptrdiff_t loc_next); /** * Creates a depth-first iterator for a tree with the specified root node. @@ -460,13 +419,8 @@ * @see cxTreeIteratorDispose() */ cx_attr_nodiscard -cx_attr_export -CxTreeIterator cx_tree_iterator( - void *root, - bool visit_on_exit, - ptrdiff_t loc_children, - ptrdiff_t loc_next -); +CX_EXPORT CxTreeIterator cx_tree_iterator(void *root, bool visit_on_exit, + ptrdiff_t loc_children, ptrdiff_t loc_next); /** * Creates a breadth-first iterator for a tree with the specified root node. @@ -487,12 +441,8 @@ * @see cxTreeVisitorDispose() */ cx_attr_nodiscard -cx_attr_export -CxTreeVisitor cx_tree_visitor( - void *root, - ptrdiff_t loc_children, - ptrdiff_t loc_next -); +CX_EXPORT CxTreeVisitor cx_tree_visitor(void *root, + ptrdiff_t loc_children, ptrdiff_t loc_next); /** * Describes a function that creates a tree node from the specified data. @@ -504,7 +454,6 @@ * @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) typedef void *(*cx_tree_node_create_func)(const void *, void *); /** @@ -513,8 +462,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; +CX_EXPORT extern unsigned int cx_tree_add_look_around_depth; /** * Adds multiple elements efficiently to a tree. @@ -554,23 +502,12 @@ * @return the number of nodes created and added * @see cx_tree_add() */ -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, - cx_tree_search_func sfunc, - cx_tree_node_create_func cfunc, - void *cdata, - void **failed, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(1, 3, 4, 6, 7) cx_attr_access_w(6) +CX_EXPORT size_t cx_tree_add_iter(struct cx_iterator_base_s *iter, size_t num, + cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, + void *cdata, void **failed, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Adds multiple elements efficiently to a tree. @@ -609,24 +546,12 @@ * @return the number of array elements successfully processed * @see cx_tree_add() */ -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, - size_t elem_size, - cx_tree_search_func sfunc, - cx_tree_node_create_func cfunc, - void *cdata, - void **failed, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(1, 4, 5, 7, 8) cx_attr_access_w(7) +CX_EXPORT size_t cx_tree_add_array(const void *src, size_t num, size_t elem_size, + cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, + void *cdata, void **failed, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Adds data to a tree. @@ -673,22 +598,12 @@ * @return zero when a new node was created and added to the tree, * non-zero otherwise */ -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, - cx_tree_node_create_func cfunc, - void *cdata, - void **cnode, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(1, 2, 3, 5, 6) cx_attr_access_w(5) +CX_EXPORT int cx_tree_add(const void *src, + cx_tree_search_func sfunc, cx_tree_node_create_func cfunc, + void *cdata, void **cnode, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** @@ -850,10 +765,7 @@ * Implementations SHALL NOT simply invoke @p insert_many as this comes * with too much overhead. */ - int (*insert_element)( - struct cx_tree_s *tree, - const void *data - ); + int (*insert_element)(struct cx_tree_s *tree, const void *data); /** * Member function for inserting multiple elements. @@ -861,21 +773,12 @@ * Implementations SHALL avoid performing a full search in the tree for * every element even though the source data MAY be unsorted. */ - size_t (*insert_many)( - struct cx_tree_s *tree, - struct cx_iterator_base_s *iter, - size_t n - ); + size_t (*insert_many)(struct cx_tree_s *tree, struct cx_iterator_base_s *iter, size_t n); /** * Member function for finding a node. */ - void *(*find)( - struct cx_tree_s *tree, - const void *subtree, - const void *data, - size_t depth - ); + void *(*find)(struct cx_tree_s *tree, const void *subtree, const void *data, size_t depth); }; /** @@ -906,8 +809,7 @@ * @see cxTreeFree() */ cx_attr_nonnull -cx_attr_export -void cxTreeDestroySubtree(CxTree *tree, void *node); +CX_EXPORT void cxTreeDestroySubtree(CxTree *tree, void *node); /** @@ -945,8 +847,7 @@ * * @param tree the tree to free */ -cx_attr_export -void cxTreeFree(CxTree *tree); +CX_EXPORT void cxTreeFree(CxTree *tree); /** * Creates a new tree structure based on the specified layout. @@ -972,22 +873,11 @@ * @see cxTreeCreateSimple() * @see cxTreeCreateWrapped() */ -cx_attr_nonnull_arg(2, 3, 4) -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, - cx_tree_search_func search_func, - cx_tree_search_data_func search_data_func, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(2, 3, 4) cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1) +CX_EXPORT CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func, + cx_tree_search_func search_func, cx_tree_search_data_func search_data_func, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Creates a new tree structure based on a default layout. @@ -1006,10 +896,8 @@ * @return (@c CxTree*) the new tree * @see cxTreeCreate() */ -#define cxTreeCreateSimple(\ - allocator, create_func, search_func, search_data_func \ -) cxTreeCreate(allocator, create_func, search_func, search_data_func, \ -cx_tree_node_base_layout) +#define cxTreeCreateSimple(allocator, create_func, search_func, search_data_func) \ + cxTreeCreate(allocator, create_func, search_func, search_data_func, cx_tree_node_base_layout) /** * Creates a new tree structure based on an existing tree. @@ -1033,20 +921,10 @@ * @return the new tree * @see cxTreeCreate() */ -cx_attr_nonnull_arg(2) -cx_attr_nodiscard -cx_attr_malloc -cx_attr_dealloc(cxTreeFree, 1) -cx_attr_export -CxTree *cxTreeCreateWrapped( - const CxAllocator *allocator, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -); +cx_attr_nonnull_arg(2) cx_attr_nodiscard cx_attr_malloc cx_attr_dealloc(cxTreeFree, 1) +CX_EXPORT CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next); /** * Inserts data into the tree. @@ -1061,12 +939,7 @@ * @retval non-zero failure */ cx_attr_nonnull -static inline int cxTreeInsert( - CxTree *tree, - const void *data -) { - return tree->cl->insert_element(tree, data); -} +CX_EXPORT int cxTreeInsert(CxTree *tree, const void *data); /** * Inserts elements provided by an iterator efficiently into the tree. @@ -1081,13 +954,7 @@ * @return the number of elements that could be successfully inserted */ cx_attr_nonnull -static inline size_t cxTreeInsertIter( - CxTree *tree, - CxIteratorBase *iter, - size_t n -) { - return tree->cl->insert_many(tree, iter, n); -} +CX_EXPORT size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n); /** * Inserts an array of data efficiently into the tree. @@ -1103,17 +970,7 @@ * @return the number of elements that could be successfully inserted */ cx_attr_nonnull -static inline size_t cxTreeInsertArray( - CxTree *tree, - const void *data, - size_t elem_size, - size_t n -) { - if (n == 0) return 0; - if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0; - CxIterator iter = cxIterator(data, elem_size, n); - return cxTreeInsertIter(tree, cxIteratorRef(iter), n); -} +CX_EXPORT size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n); /** * Searches the data in the specified tree. @@ -1126,14 +983,8 @@ * @param data the data to search for * @return the first matching node, or @c NULL when the data cannot be found */ -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cxTreeFind( - CxTree *tree, - const void *data -) { - return tree->cl->find(tree, tree->root, data, 0); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT void *cxTreeFind(CxTree *tree, const void *data); /** * Searches the data in the specified subtree. @@ -1154,16 +1005,8 @@ * @param max_depth the maximum search depth * @return the first matching node, or @c NULL when the data cannot be found */ -cx_attr_nonnull -cx_attr_nodiscard -static inline void *cxTreeFindInSubtree( - CxTree *tree, - const void *data, - void *subtree_root, - size_t max_depth -) { - return tree->cl->find(tree, subtree_root, data, max_depth); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth); /** * Determines the size of the specified subtree. @@ -1172,10 +1015,8 @@ * @param subtree_root the root node of the subtree * @return the number of nodes in the specified subtree */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root); /** * Determines the depth of the specified subtree. @@ -1184,10 +1025,8 @@ * @param subtree_root the root node of the subtree * @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); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxTreeSubtreeDepth(CxTree *tree, void *subtree_root); /** * Determines the size of the entire tree. @@ -1195,11 +1034,8 @@ * @param tree the tree * @return the tree size, counting the root as one */ -cx_attr_nonnull -cx_attr_nodiscard -static inline size_t cxTreeSize(CxTree *tree) { - return tree->size; -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxTreeSize(CxTree *tree); /** * Determines the depth of the entire tree. @@ -1207,10 +1043,8 @@ * @param tree the tree * @return the tree depth, counting the root as one */ -cx_attr_nonnull -cx_attr_nodiscard -cx_attr_export -size_t cxTreeDepth(CxTree *tree); +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT size_t cxTreeDepth(CxTree *tree); /** * Creates a depth-first iterator for the specified tree starting in @p node. @@ -1224,18 +1058,8 @@ * @return a tree iterator (depth-first) * @see cxTreeVisit() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxTreeIterator cxTreeIterateSubtree( - CxTree *tree, - void *node, - bool visit_on_exit -) { - return cx_tree_iterator( - node, visit_on_exit, - tree->loc_children, tree->loc_next - ); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit); /** * Creates a breadth-first iterator for the specified tree starting in @p node. @@ -1247,13 +1071,8 @@ * @return a tree visitor (a.k.a. breadth-first iterator) * @see cxTreeIterate() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) { - return cx_tree_visitor( - node, tree->loc_children, tree->loc_next - ); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node); /** * Creates a depth-first iterator for the specified tree. @@ -1264,14 +1083,8 @@ * @return a tree iterator (depth-first) * @see cxTreeVisit() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxTreeIterator cxTreeIterate( - CxTree *tree, - bool visit_on_exit -) { - return cxTreeIterateSubtree(tree, tree->root, visit_on_exit); -} +cx_attr_nonnull cx_attr_nodiscard +CX_EXPORT CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit); /** * Creates a breadth-first iterator for the specified tree. @@ -1280,11 +1093,8 @@ * @return a tree visitor (a.k.a. breadth-first iterator) * @see cxTreeIterate() */ -cx_attr_nonnull -cx_attr_nodiscard -static inline CxTreeVisitor cxTreeVisit(CxTree *tree) { - return cxTreeVisitSubtree(tree, tree->root); -} +cx_attr_nonnull cx_attr_nodiscard +CxTreeVisitor cxTreeVisit(CxTree *tree); /** * Sets the (new) parent of the specified child. @@ -1298,12 +1108,7 @@ * @see cxTreeAddChildNode() */ cx_attr_nonnull -cx_attr_export -void cxTreeSetParent( - CxTree *tree, - void *parent, - void *child -); +CX_EXPORT void cxTreeSetParent(CxTree *tree, void *parent, void *child); /** * Adds a new node to the tree. @@ -1321,12 +1126,7 @@ * @see cxTreeSetParent() */ cx_attr_nonnull -cx_attr_export -void cxTreeAddChildNode( - CxTree *tree, - void *parent, - void *child -); +CX_EXPORT void cxTreeAddChildNode(CxTree *tree, void *parent, void *child); /** * Creates a new node and adds it to the tree. @@ -1346,12 +1146,7 @@ * @see cxTreeInsert() */ cx_attr_nonnull -cx_attr_export -int cxTreeAddChild( - CxTree *tree, - void *parent, - const void *data -); +CX_EXPORT int cxTreeAddChild(CxTree *tree, void *parent, const void *data); /** * A function that is invoked when a node needs to be re-linked to a new parent. @@ -1365,7 +1160,6 @@ * @param old_parent the old parent of the node * @param new_parent the new parent of the node */ -cx_attr_nonnull typedef void (*cx_tree_relink_func)( void *node, const void *old_parent, @@ -1387,12 +1181,7 @@ * @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, - cx_tree_relink_func relink_func -); +CX_EXPORT int cxTreeRemoveNode(CxTree *tree, void *node, cx_tree_relink_func relink_func); /** * Removes a node and its subtree from the tree. @@ -1406,8 +1195,7 @@ * @param node the node to remove */ cx_attr_nonnull -cx_attr_export -void cxTreeRemoveSubtree(CxTree *tree, void *node); +CX_EXPORT void cxTreeRemoveSubtree(CxTree *tree, void *node); /** * Destroys a node and re-links its children to its former parent. @@ -1428,12 +1216,7 @@ * @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, - cx_tree_relink_func relink_func -); +CX_EXPORT int cxTreeDestroyNode(CxTree *tree, void *node, cx_tree_relink_func relink_func); #ifdef __cplusplus } // extern "C"
--- a/ucx/cx/utils.h Sun Oct 19 21:20:08 2025 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,194 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file utils.h - * - * \brief General purpose utility functions. - * - * \author Mike Becker - * \author Olaf Wintermann - * \copyright 2-Clause BSD License - */ - -#ifndef UCX_UTILS_H -#define UCX_UTILS_H - -#include "common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * Convenience macro for a for loop that counts from zero to n-1. - */ -#define cx_for_n(varname, n) for (size_t varname = 0 ; (varname) < (n) ; (varname)++) - -/** - * Convenience macro for swapping two pointers. - */ -#ifdef __cplusplus -#define cx_swap_ptr(left, right) do {auto cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0) -#else -#define cx_swap_ptr(left, right) do {void *cx_tmp_swap_var = left; left = right; right = cx_tmp_swap_var;} while(0) -#endif - -// cx_szmul() definition - -#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 - * be stored - * @return zero, if no overflow occurred and the result is correct, non-zero - * otherwise - */ -#define cx_szmul(a, b, result) cx_szmul_impl(a, b, result) - -/** - * Performs a multiplication of size_t values and checks for overflow. - * - * This is a custom implementation in case there is no compiler builtin - * available. - * - * @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 - */ -int cx_szmul_impl(size_t a, size_t b, size_t *result); - -#endif // cx_szmul - - -/** - * 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 - * 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 n the maximum number of bytes that shall be copied. - * If this is larger than \p bufsize, the content is copied over multiple - * iterations. - * @return the total number of bytes copied - */ -__attribute__((__nonnull__(1, 2, 3, 4))) -size_t cx_stream_bncopy( - void *src, - void *dest, - cx_read_func rfnc, - cx_write_func wfnc, - char *buf, - size_t bufsize, - size_t n -); - -/** - * 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 - * 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 - * @return total number of bytes copied - */ -#define cx_stream_bcopy(src, dest, rfnc, wfnc, buf, bufsize) \ - cx_stream_bncopy(src, dest, rfnc, wfnc, buf, bufsize, SIZE_MAX) - -/** - * Reads data from a stream and writes it to another stream. - * - * 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 n the maximum number of bytes that shall be copied. - * @return total number of bytes copied - */ -__attribute__((__nonnull__)) -size_t cx_stream_ncopy( - void *src, - void *dest, - cx_read_func rfnc, - cx_write_func wfnc, - size_t n -); - -/** - * Reads data from a stream and writes it to another stream. - * - * 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 - * @return total number of bytes copied - */ -#define cx_stream_copy(src, dest, rfnc, wfnc) \ - cx_stream_ncopy(src, dest, rfnc, wfnc, SIZE_MAX) - -#ifdef __cplusplus -} -#endif - -#endif // UCX_UTILS_H
--- a/ucx/hash_key.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/hash_key.c Mon Nov 10 21:52:51 2025 +0100 @@ -105,6 +105,22 @@ return key; } +CxHashKey cx_hash_key_ustr(unsigned const char *str) { + CxHashKey key; + key.data = str; + key.len = str == NULL ? 0 : strlen((const char*)str); + cx_hash_murmur(&key); + return key; +} + +CxHashKey cx_hash_key_cxstr(cxstring str) { + return cx_hash_key(str.ptr, str.length); +} + +CxHashKey cx_hash_key_mutstr(cxmutstr str) { + return cx_hash_key(str.ptr, str.length); +} + CxHashKey cx_hash_key_bytes( const unsigned char *bytes, size_t len @@ -143,7 +159,9 @@ return key; } -int cx_hash_key_cmp(const CxHashKey *left, const CxHashKey *right) { +int cx_hash_key_cmp(const void *l, const void *r) { + const CxHashKey *left = l; + const CxHashKey *right = r; int d; d = cx_vcmp_uint64(left->hash, right->hash); if (d != 0) return d;
--- a/ucx/hash_map.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/hash_map.c Mon Nov 10 21:52:51 2025 +0100 @@ -86,7 +86,7 @@ struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; const CxAllocator *allocator = map->collection.allocator; - unsigned hash = key.hash; + uint64_t hash = key.hash; if (hash == 0) { cx_hash_murmur(&key); hash = key.hash; @@ -203,7 +203,7 @@ ) { struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map; - unsigned hash = key.hash; + uint64_t hash = key.hash; if (hash == 0) { cx_hash_murmur(&key); hash = key.hash; @@ -281,7 +281,7 @@ static void cx_hash_map_iter_next(void *it) { CxMapIterator *iter = it; - CxMap *map = iter->map.m; + CxMap *map = iter->map; struct cx_hash_map_s *hmap = (struct cx_hash_map_s *) map; struct cx_hash_map_element_s *elm = iter->elem; @@ -329,7 +329,7 @@ // must not modify the iterator (the parameter is const) if (elm != NULL) { iter->entry.key = &elm->key; - if (iter->map.c->collection.store_pointer) { + if (map->collection.store_pointer) { iter->entry.value = *(void **) elm->data; } else { iter->entry.value = elm->data; @@ -343,7 +343,7 @@ ) { CxMapIterator iter; - iter.map.c = map; + iter.map = (CxMap*) map; iter.elem_count = map->collection.size; switch (type) { @@ -366,7 +366,7 @@ iter.base.valid = cx_hash_map_iter_valid; iter.base.next = cx_hash_map_iter_next; iter.base.remove = false; - iter.base.mutating = false; + iter.base.allow_remove = true; iter.slot = 0; iter.index = 0;
--- a/ucx/iterator.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/iterator.c Mon Nov 10 21:52:51 2025 +0100 @@ -53,7 +53,7 @@ // only move the last element when we are not currently aiming // at the last element already if (iter->index < iter->elem_count) { - void *last = ((char *) iter->src_handle.m) + void *last = ((char *) iter->src_handle) + iter->elem_count * iter->elem_size; memcpy(iter->elem_handle, last, iter->elem_size); } @@ -84,8 +84,8 @@ } } -CxIterator cxMutIterator( - void *array, +CxIterator cxIterator( + const void *array, size_t elem_size, size_t elem_count, bool remove_keeps_order @@ -93,44 +93,25 @@ CxIterator iter; iter.index = 0; - iter.src_handle.m = array; - iter.elem_handle = array; + iter.src_handle = (void*) array; + iter.elem_handle = (void*) array; iter.elem_size = elem_size; iter.elem_count = array == NULL ? 0 : elem_count; iter.base.valid = cx_iter_valid; iter.base.current = cx_iter_current; iter.base.next = remove_keeps_order ? cx_iter_next_slow : cx_iter_next_fast; iter.base.remove = false; - iter.base.mutating = true; - - return iter; -} + iter.base.allow_remove = true; -CxIterator cxIterator( - const void *array, - size_t elem_size, - size_t elem_count -) { - CxIterator iter = cxMutIterator((void*)array, elem_size, elem_count, false); - iter.base.mutating = false; - return iter; -} - -CxIterator cxMutIteratorPtr( - void *array, - size_t elem_count, - bool remove_keeps_order -) { - CxIterator iter = cxMutIterator(array, sizeof(void*), elem_count, remove_keeps_order); - iter.base.current = cx_iter_current_ptr; return iter; } CxIterator cxIteratorPtr( const void *array, - size_t elem_count + size_t elem_count, + bool remove_keeps_order ) { - CxIterator iter = cxMutIteratorPtr((void*) array, elem_count, false); - iter.base.mutating = false; + CxIterator iter = cxIterator(array, sizeof(void*), elem_count, remove_keeps_order); + iter.base.current = cx_iter_current_ptr; return iter; }
--- a/ucx/json.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/json.c Mon Nov 10 21:52:51 2025 +0100 @@ -630,6 +630,12 @@ } } +void cxJsonReset(CxJson *json) { + const CxAllocator *allocator = json->allocator; + cxJsonDestroy(json); + cxJsonInit(json, allocator); +} + int cxJsonFilln(CxJson *json, const char *buf, size_t size) { if (cxBufferEof(&json->buffer)) { // reinitialize the buffer @@ -1126,10 +1132,39 @@ return ret; } +char *cxJsonAsString(const CxJsonValue *value) { + return value->value.string.ptr; +} + +cxstring cxJsonAsCxString(const CxJsonValue *value) { + return cx_strcast(value->value.string); +} + +cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { + return value->value.string; +} + +double cxJsonAsDouble(const CxJsonValue *value) { + if (value->type == CX_JSON_INTEGER) { + return (double) value->value.integer; + } else { + return value->value.number; + } +} + +int64_t cxJsonAsInteger(const CxJsonValue *value) { + if (value->type == CX_JSON_INTEGER) { + return value->value.integer; + } else { + return (int64_t) value->value.number; + } +} + CxIterator cxJsonArrIter(const CxJsonValue *value) { return cxIteratorPtr( value->value.array.array, - value->value.array.array_size + value->value.array.array_size, + true // arrays need to keep order ); } @@ -1137,7 +1172,8 @@ return cxIterator( value->value.object.values, sizeof(CxJsonObjValue), - value->value.object.values_size + value->value.object.values_size, + true // TODO: objects do not always need to keep order ); } @@ -1157,7 +1193,7 @@ } else { CxJsonObjValue kv = value->value.object.values[index]; cx_strfree_a(value->allocator, &kv.name); - // TODO: replace with cx_array_remove() + // TODO: replace with cx_array_remove() / cx_array_remove_fast() value->value.object.values_size--; memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue)); return kv.value;
--- a/ucx/kv_list.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/kv_list.c Mon Nov 10 21:52:51 2025 +0100 @@ -152,7 +152,7 @@ const void *elem, int prepend ) { - cx_kv_list *kv_list = iter->src_handle.m; + cx_kv_list *kv_list = iter->src_handle; return kv_list->list_methods->insert_iter(iter, elem, prepend); } @@ -259,7 +259,7 @@ struct cx_iterator_s *iter = it; if (iter->base.remove) { // remove the assigned key from the map before calling the actual function - cx_kv_list *kv_list = iter->src_handle.m; + cx_kv_list *kv_list = iter->src_handle; cx_kv_list_update_destructors(kv_list); char *node = iter->elem_handle; CxHashKey *key = cx_kv_list_loc_key(kv_list, node + kv_list->list.loc_data); @@ -267,6 +267,7 @@ kv_list->map_methods->remove(&kv_list->map->map_base.base, *key, NULL); } } + // note that we do not clear the remove flag, because the next_impl will do that iter->base.next_impl(it); } @@ -397,7 +398,7 @@ static void cx_kvl_iter_next(void *it) { CxMapIterator *iter = it; - cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)iter->map.m)->list; + cx_kv_list *kv_list = ((struct cx_kv_list_map_s*)iter->map)->list; // find the next list entry that has a key assigned CxHashKey *key = NULL; @@ -458,7 +459,7 @@ CxMapIterator iter = {0}; iter.type = type; - iter.map.c = map; + iter.map = (CxMap*)map; // although we iterate over the list, we only report that many elements that have a key in the map iter.elem_count = map->collection.size; @@ -479,6 +480,7 @@ assert(false); // LCOV_EXCL_LINE } + iter.base.allow_remove = true; iter.base.next = cx_kvl_iter_next; iter.base.valid = cx_kvl_iter_valid; @@ -507,6 +509,15 @@ return iter; } +static int cx_kvl_change_capacity(struct cx_list_s *list, + cx_attr_unused size_t cap) { + // since our backing list is a linked list, we don't need to do much here, + // but rehashing the map is quite useful + cx_kv_list *kv_list = (cx_kv_list*)list; + cxMapRehash(&kv_list->map->map_base.base); + return 0; +} + static cx_list_class cx_kv_list_class = { cx_kvl_deallocate, cx_kvl_insert_element, @@ -522,6 +533,7 @@ cx_kvl_sort, NULL, cx_kvl_reverse, + cx_kvl_change_capacity, cx_kvl_iterator, };
--- a/ucx/linked_list.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/linked_list.c Mon Nov 10 21:52:51 2025 +0100 @@ -30,7 +30,6 @@ #include "cx/compare.h" #include <string.h> #include <assert.h> -#include <unistd.h> // LOW LEVEL LINKED LIST FUNCTIONS @@ -475,6 +474,16 @@ return removed; } +void cx_linked_list_remove( + void **begin, + void **end, + ptrdiff_t loc_prev, + ptrdiff_t loc_next, + void *node +) { + cx_linked_list_remove_chain(begin, end, loc_prev, loc_next, node, 1); +} + size_t cx_linked_list_size( const void *node, ptrdiff_t loc_next @@ -742,7 +751,9 @@ // we can add the remaining nodes and immediately advance to the inserted node const char *source = array; for (size_t i = 1; i < n; i++) { - source += list->collection.elem_size; + if (source != NULL) { + source += list->collection.elem_size; + } if (0 != cx_ll_insert_at(list, node, source)) return i; node = CX_LL_PTR(node, ll->loc_next); } @@ -1116,10 +1127,10 @@ static void cx_ll_iter_next(void *it) { struct cx_iterator_s *iter = it; + cx_linked_list *ll = iter->src_handle; if (iter->base.remove) { iter->base.remove = false; - struct cx_list_s *list = iter->src_handle.m; - cx_linked_list *ll = iter->src_handle.m; + struct cx_list_s *list = iter->src_handle; char *node = iter->elem_handle; iter->elem_handle = CX_LL_PTR(node, ll->loc_next); cx_invoke_destructor(list, node + ll->loc_data); @@ -1129,7 +1140,6 @@ iter->elem_count--; cxFree(list->collection.allocator, node); } else { - const cx_linked_list *ll = iter->src_handle.c; iter->index++; void *node = iter->elem_handle; iter->elem_handle = CX_LL_PTR(node, ll->loc_next); @@ -1138,10 +1148,10 @@ static void cx_ll_iter_prev(void *it) { struct cx_iterator_s *iter = it; + cx_linked_list *ll = iter->src_handle; if (iter->base.remove) { iter->base.remove = false; - struct cx_list_s *list = iter->src_handle.m; - cx_linked_list *ll = iter->src_handle.m; + struct cx_list_s *list = iter->src_handle; char *node = iter->elem_handle; iter->elem_handle = CX_LL_PTR(node, ll->loc_prev); iter->index--; @@ -1152,7 +1162,6 @@ iter->elem_count--; cxFree(list->collection.allocator, node); } else { - const cx_linked_list *ll = iter->src_handle.c; iter->index--; char *node = iter->elem_handle; iter->elem_handle = CX_LL_PTR(node, ll->loc_prev); @@ -1161,7 +1170,7 @@ static void *cx_ll_iter_current(const void *it) { const struct cx_iterator_s *iter = it; - const cx_linked_list *ll = iter->src_handle.c; + const cx_linked_list *ll = iter->src_handle; char *node = iter->elem_handle; return node + ll->loc_data; } @@ -1173,14 +1182,14 @@ ) { CxIterator iter; iter.index = index; - iter.src_handle.c = list; + iter.src_handle = (void*)list; iter.elem_handle = cx_ll_node_at((const cx_linked_list *) list, index); iter.elem_size = list->collection.elem_size; iter.elem_count = list->collection.size; iter.base.valid = cx_ll_iter_valid; iter.base.current = cx_ll_iter_current; iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; - iter.base.mutating = false; + iter.base.allow_remove = true; iter.base.remove = false; return iter; } @@ -1190,8 +1199,8 @@ const void *elem, int prepend ) { - struct cx_list_s *list = iter->src_handle.m; - cx_linked_list *ll = iter->src_handle.m; + struct cx_list_s *list = iter->src_handle; + cx_linked_list *ll = iter->src_handle; void *node = iter->elem_handle; if (node != NULL) { assert(prepend >= 0 && prepend <= 1); @@ -1243,6 +1252,7 @@ cx_ll_sort, cx_ll_compare, cx_ll_reverse, + NULL, // no overallocation supported cx_ll_iterator, };
--- a/ucx/list.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/list.c Mon Nov 10 21:52:51 2025 +0100 @@ -29,6 +29,7 @@ #include "cx/list.h" #include <string.h> +#include <assert.h> // <editor-fold desc="Store Pointers Functionality"> @@ -114,7 +115,7 @@ const void *elem, int prepend ) { - struct cx_list_s *list = iter->src_handle.m; + struct cx_list_s *list = iter->src_handle; return list->climpl->insert_iter(iter, &elem, prepend); } @@ -184,6 +185,10 @@ return ptr == NULL ? NULL : *ptr; } +static int cx_pl_change_capacity(struct cx_list_s *list, size_t cap) { + return list->climpl->change_capacity(list, cap); +} + static struct cx_iterator_s cx_pl_iterator( const struct cx_list_s *list, size_t index, @@ -210,6 +215,7 @@ cx_pl_sort, cx_pl_compare, cx_pl_reverse, + cx_pl_change_capacity, cx_pl_iterator, }; // </editor-fold> @@ -245,7 +251,7 @@ cx_attr_unused bool backwards ) { CxIterator iter = {0}; - iter.src_handle.c = list; + iter.src_handle = (void*) list; iter.index = index; iter.base.valid = cx_emptyl_iter_valid; return iter; @@ -266,6 +272,7 @@ cx_emptyl_noop, NULL, cx_emptyl_noop, + NULL, cx_emptyl_iterator, }; @@ -299,16 +306,17 @@ const void *data, size_t n ) { - size_t elem_size = list->collection.elem_size; const char *src = data; size_t i = 0; for (; i < n; i++) { if (NULL == invoke_list_func( - insert_element, list, index + i, - src + i * elem_size) + insert_element, list, index + i, src) ) { return i; // LCOV_EXCL_LINE } + if (src != NULL) { + src += list->collection.elem_size; + } } return i; } @@ -566,36 +574,174 @@ } } -CxIterator cxListMutIteratorAt( - CxList *list, - size_t index -) { - if (list == NULL) list = cxEmptyList; - CxIterator it = list->cl->iterator(list, index, false); - it.base.mutating = true; - return it; +size_t cxListSize(const CxList *list) { + return list->collection.size; +} + +int cxListAdd(CxList *list, const void *elem) { + list->collection.sorted = false; + return list->cl->insert_element(list, list->collection.size, elem) == NULL; +} + +size_t cxListAddArray(CxList *list, const void *array, size_t n) { + list->collection.sorted = false; + return list->cl->insert_array(list, list->collection.size, array, n); +} + +int cxListInsert(CxList *list, size_t index, const void *elem) { + list->collection.sorted = false; + return list->cl->insert_element(list, index, elem) == NULL; +} + +void *cxListEmplaceAt(CxList *list, size_t index) { + list->collection.sorted = false; + return list->cl->insert_element(list, index, NULL); +} + +void *cxListEmplace(CxList *list) { + list->collection.sorted = false; + return list->cl->insert_element(list, list->collection.size, NULL); +} + +static bool cx_list_emplace_iterator_valid(const void *it) { + const CxIterator *iter = it; + return iter->index < iter->elem_count; +} + +CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n) { + list->collection.sorted = false; + size_t c = list->cl->insert_array(list, index, NULL, n); + CxIterator iter = list->cl->iterator(list, index, false); + // tweak the fields of this iterator + iter.elem_count = c; + iter.index = 0; + // replace the valid function to abort iteration when c is reached + iter.base.valid = cx_list_emplace_iterator_valid; + // if we are storing pointers, we want to return the pure pointers. + // therefore, we must unwrap the "current" method + if (list->collection.store_pointer) { + iter.base.current = iter.base.current_impl; + } + return iter; +} + +CxIterator cxListEmplaceArray(CxList *list, size_t n) { + return cxListEmplaceArrayAt(list, list->collection.size, n); +} + +int cxListInsertSorted(CxList *list, const void *elem) { + assert(cxCollectionSorted(list)); + list->collection.sorted = true; + const void *data = list->collection.store_pointer ? &elem : elem; + return list->cl->insert_sorted(list, data, 1) == 0; +} + +int cxListInsertUnique(CxList *list, const void *elem) { + if (cxCollectionSorted(list)) { + list->collection.sorted = true; + const void *data = list->collection.store_pointer ? &elem : elem; + return list->cl->insert_unique(list, data, 1) == 0; + } else { + if (cxListContains(list, elem)) { + return 0; + } else { + return cxListAdd(list, elem); + } + } +} + +size_t cxListInsertArray(CxList *list, size_t index, const void *array, size_t n) { + list->collection.sorted = false; + return list->cl->insert_array(list, index, array, n); } -CxIterator cxListMutBackwardsIteratorAt( - CxList *list, - size_t index -) { - if (list == NULL) list = cxEmptyList; - CxIterator it = list->cl->iterator(list, index, true); - it.base.mutating = true; - return it; +size_t cxListInsertSortedArray(CxList *list, const void *array, size_t n) { + assert(cxCollectionSorted(list)); + list->collection.sorted = true; + return list->cl->insert_sorted(list, array, n); +} + +size_t cxListInsertUniqueArray(CxList *list, const void *array, size_t n) { + if (cxCollectionSorted(list)) { + list->collection.sorted = true; + return list->cl->insert_unique(list, array, n); + } else { + const char *source = array; + for (size_t i = 0 ; i < n; i++) { + // note: this also checks elements added in a previous iteration + const void *data = list->collection.store_pointer ? + *((const void**)source) : source; + if (!cxListContains(list, data)) { + if (cxListAdd(list, data)) { + return i; // LCOV_EXCL_LINE + } + } + source += list->collection.elem_size; + } + return n; + } +} + +int cxListInsertAfter(CxIterator *iter, const void *elem) { + CxList* list = (CxList*)iter->src_handle; + list->collection.sorted = false; + return list->cl->insert_iter(iter, elem, 0); +} + +int cxListInsertBefore(CxIterator *iter, const void *elem) { + CxList* list = (CxList*)iter->src_handle; + list->collection.sorted = false; + return list->cl->insert_iter(iter, elem, 1); +} + +int cxListRemove(CxList *list, size_t index) { + return list->cl->remove(list, index, 1, NULL) == 0; } -void cxListFree(CxList *list) { - if (list == NULL) return; - list->cl->deallocate(list); +int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf) { + return list->cl->remove(list, index, 1, targetbuf) == 0; +} + +int cxListRemoveAndGetFirst(CxList *list, void *targetbuf) { + return list->cl->remove(list, 0, 1, targetbuf) == 0; +} + +int cxListRemoveAndGetLast(CxList *list, void *targetbuf) { + // note: index may wrap - member function will catch that + return list->cl->remove(list, list->collection.size - 1, 1, targetbuf) == 0; +} + +size_t cxListRemoveArray(CxList *list, size_t index, size_t num) { + return list->cl->remove(list, index, num, NULL); +} + +size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, void *targetbuf) { + return list->cl->remove(list, index, num, targetbuf); } -int cxListSet( - CxList *list, - size_t index, - const void *elem -) { +void cxListClear(CxList *list) { + list->cl->clear(list); + list->collection.sorted = true; // empty lists are always sorted +} + +int cxListSwap(CxList *list, size_t i, size_t j) { + list->collection.sorted = false; + return list->cl->swap(list, i, j); +} + +void *cxListAt(const CxList *list, size_t index) { + return list->cl->at(list, index); +} + +void *cxListFirst(const CxList *list) { + return list->cl->at(list, 0); +} + +void *cxListLast(const CxList *list) { + return list->cl->at(list, list->collection.size - 1); +} + +int cxListSet(CxList *list, size_t index, const void *elem) { if (index >= list->collection.size) { return 1; } @@ -611,3 +757,371 @@ return 0; } + +CxIterator cxListIteratorAt(const CxList *list, size_t index) { + if (list == NULL) list = cxEmptyList; + return list->cl->iterator(list, index, false); +} + +CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index) { + if (list == NULL) list = cxEmptyList; + return list->cl->iterator(list, index, true); +} + +CxIterator cxListIterator(const CxList *list) { + if (list == NULL) list = cxEmptyList; + return list->cl->iterator(list, 0, false); +} + +CxIterator cxListBackwardsIterator(const CxList *list) { + if (list == NULL) list = cxEmptyList; + return list->cl->iterator(list, list->collection.size - 1, true); +} + +size_t cxListFind(const CxList *list, const void *elem) { + return list->cl->find_remove((CxList*)list, elem, false); +} + +bool cxListContains(const CxList* list, const void* elem) { + return list->cl->find_remove((CxList*)list, elem, false) < list->collection.size; +} + +bool cxListIndexValid(const CxList *list, size_t index) { + return index < list->collection.size; +} + +size_t cxListFindRemove(CxList *list, const void *elem) { + return list->cl->find_remove(list, elem, true); +} + +void cxListSort(CxList *list) { + if (list->collection.sorted) return; + list->cl->sort(list); + list->collection.sorted = true; +} + +void cxListReverse(CxList *list) { + // still sorted, but not according to the cmp_func + list->collection.sorted = false; + list->cl->reverse(list); +} + +void cxListFree(CxList *list) { + if (list == NULL) return; + list->cl->deallocate(list); +} + +static void cx_list_pop_uninitialized_elements(CxList *list, size_t n) { + cx_destructor_func destr_bak = list->collection.simple_destructor; + cx_destructor_func2 destr2_bak = list->collection.advanced_destructor; + list->collection.simple_destructor = NULL; + list->collection.advanced_destructor = NULL; + if (n == 1) { + cxListRemove(list, list->collection.size - 1); + } else { + cxListRemoveArray(list,list->collection.size - n, n); + } + list->collection.simple_destructor = destr_bak; + list->collection.advanced_destructor = destr2_bak; +} + +static void* cx_list_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { + size_t elem_size = *(size_t*)data; + if (dst == NULL) dst = cxMalloc(al, elem_size); + if (dst != NULL) memcpy(dst, src, elem_size); + return dst; +} + +#define use_simple_clone_func(list) cx_list_simple_clone_func, NULL, (void*)&((list)->collection.elem_size) + +int cxListClone(CxList *dst, const CxList *src, cx_clone_func clone_func, + const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + // remember the original size + size_t orig_size = dst->collection.size; + + // first, try to allocate the memory in the new list + CxIterator empl_iter = cxListEmplaceArray(dst, src->collection.size); + + // get an iterator over the source elements + CxIterator src_iter = cxListIterator(src); + + // now clone the elements + size_t cloned = empl_iter.elem_count; + for (size_t i = 0 ; i < empl_iter.elem_count; i++) { + void *src_elem = cxIteratorCurrent(src_iter); + void **dest_memory = cxIteratorCurrent(empl_iter); + void *target = cxCollectionStoresPointers(dst) ? NULL : dest_memory; + void *dest_ptr = clone_func(target, src_elem, clone_allocator, data); + if (dest_ptr == NULL) { + cloned = i; + break; + } + if (cxCollectionStoresPointers(dst)) { + *dest_memory = dest_ptr; + } + cxIteratorNext(src_iter); + cxIteratorNext(empl_iter); + } + + // if we could not clone everything, free the allocated memory + // (disable the destructors!) + if (cloned < src->collection.size) { + cx_list_pop_uninitialized_elements(dst, + dst->collection.size - cloned - orig_size); + return 1; + } + + // set the sorted flag when we know it's sorted + if (orig_size == 0 && src->collection.sorted) { + dst->collection.sorted = true; + } + + return 0; +} + +int cxListDifference(CxList *dst, + const CxList *minuend, const CxList *subtrahend, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + // optimize for sorted collections + if (cxCollectionSorted(minuend) && cxCollectionSorted(subtrahend)) { + bool dst_was_empty = cxCollectionSize(dst) == 0; + + CxIterator min_iter = cxListIterator(minuend); + CxIterator sub_iter = cxListIterator(subtrahend); + while (cxIteratorValid(min_iter)) { + void *min_elem = cxIteratorCurrent(min_iter); + void *sub_elem; + int d; + if (cxIteratorValid(sub_iter)) { + sub_elem = cxIteratorCurrent(sub_iter); + cx_compare_func cmp = subtrahend->collection.cmpfunc; + d = cmp(sub_elem, min_elem); + } else { + // no more elements in the subtrahend, + // i.e., the min_elem is larger than any elem of the subtrahend + d = 1; + } + if (d == 0) { + // is contained, so skip it + cxIteratorNext(min_iter); + } else if (d < 0) { + // subtrahend is smaller than minuend, + // check the next element + cxIteratorNext(sub_iter); + } else { + // subtrahend is larger than the dst element, + // clone the minuend and advance + void **dst_mem = cxListEmplace(dst); + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, min_elem, clone_allocator, data); + if (dst_ptr == NULL) { + cx_list_pop_uninitialized_elements(dst, 1); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + cxIteratorNext(min_iter); + } + } + + // if dst was empty, it is now guaranteed to be sorted + dst->collection.sorted = dst_was_empty; + } else { + CxIterator min_iter = cxListIterator(minuend); + cx_foreach(void *, elem, min_iter) { + if (cxListContains(subtrahend, elem)) { + continue; + } + void **dst_mem = cxListEmplace(dst); + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, elem, clone_allocator, data); + if (dst_ptr == NULL) { + cx_list_pop_uninitialized_elements(dst, 1); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + } + + return 0; +} + +int cxListIntersection(CxList *dst, + const CxList *src, const CxList *other, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + // optimize for sorted collections + if (cxCollectionSorted(src) && cxCollectionSorted(other)) { + bool dst_was_empty = cxCollectionSize(dst) == 0; + + CxIterator src_iter = cxListIterator(src); + CxIterator other_iter = cxListIterator(other); + while (cxIteratorValid(src_iter) && cxIteratorValid(other_iter)) { + void *src_elem = cxIteratorCurrent(src_iter); + void *other_elem = cxIteratorCurrent(other_iter); + int d = src->collection.cmpfunc(src_elem, other_elem); + if (d == 0) { + // is contained, clone it + void **dst_mem = cxListEmplace(dst); + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, src_elem, clone_allocator, data); + if (dst_ptr == NULL) { + cx_list_pop_uninitialized_elements(dst, 1); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + cxIteratorNext(src_iter); + } else if (d < 0) { + // the other element is larger, skip the source element + cxIteratorNext(src_iter); + } else { + // the source element is larger, try to find it in the other list + cxIteratorNext(other_iter); + } + } + + // if dst was empty, it is now guaranteed to be sorted + dst->collection.sorted = dst_was_empty; + } else { + CxIterator src_iter = cxListIterator(src); + cx_foreach(void *, elem, src_iter) { + if (!cxListContains(other, elem)) { + continue; + } + void **dst_mem = cxListEmplace(dst); + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, elem, clone_allocator, data); + if (dst_ptr == NULL) { + cx_list_pop_uninitialized_elements(dst, 1); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + } + + return 0; +} + +int cxListUnion(CxList *dst, + const CxList *src, const CxList *other, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + // optimize for sorted collections + if (cxCollectionSorted(src) && cxCollectionSorted(other)) { + bool dst_was_empty = cxCollectionSize(dst) == 0; + + CxIterator src_iter = cxListIterator(src); + CxIterator other_iter = cxListIterator(other); + while (cxIteratorValid(src_iter) || cxIteratorValid(other_iter)) { + void *src_elem, *other_elem; + int d; + if (!cxIteratorValid(src_iter)) { + other_elem = cxIteratorCurrent(other_iter); + d = 1; + } else if (!cxIteratorValid(other_iter)) { + src_elem = cxIteratorCurrent(src_iter); + d = -1; + } else { + src_elem = cxIteratorCurrent(src_iter); + other_elem = cxIteratorCurrent(other_iter); + d = src->collection.cmpfunc(src_elem, other_elem); + } + void *clone_from; + if (d < 0) { + // source element is smaller clone it + clone_from = src_elem; + cxIteratorNext(src_iter); + } else if (d == 0) { + // both elements are equal, clone from the source, skip other + clone_from = src_elem; + cxIteratorNext(src_iter); + cxIteratorNext(other_iter); + } else { + // the other element is smaller, clone it + clone_from = other_elem; + cxIteratorNext(other_iter); + } + void **dst_mem = cxListEmplace(dst); + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, clone_from, clone_allocator, data); + if (dst_ptr == NULL) { + cx_list_pop_uninitialized_elements(dst, 1); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + + // if dst was empty, it is now guaranteed to be sorted + dst->collection.sorted = dst_was_empty; + } else { + if (cxListClone(dst, src, clone_func, clone_allocator, data)) { + return 1; + } + CxIterator other_iter = cxListIterator(other); + cx_foreach(void *, elem, other_iter) { + if (cxListContains(src, elem)) { + continue; + } + void **dst_mem = cxListEmplace(dst); + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, elem, clone_allocator, data); + if (dst_ptr == NULL) { + cx_list_pop_uninitialized_elements(dst, 1); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + } + + return 0; +} + +int cxListCloneSimple(CxList *dst, const CxList *src) { + return cxListClone(dst, src, use_simple_clone_func(src)); +} + +int cxListDifferenceSimple(CxList *dst, const CxList *minuend, const CxList *subtrahend) { + return cxListDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend)); +} + +int cxListIntersectionSimple(CxList *dst, const CxList *src, const CxList *other) { + return cxListIntersection(dst, src, other, use_simple_clone_func(src)); +} + +int cxListUnionSimple(CxList *dst, const CxList *src, const CxList *other) { + return cxListUnion(dst, src, other, use_simple_clone_func(src)); +} + +int cxListReserve(CxList *list, size_t capacity) { + if (list->cl->change_capacity == NULL) { + return 0; + } + if (capacity <= cxCollectionSize(list)) { + return 0; + } + return list->cl->change_capacity(list, capacity); +} + +int cxListShrink(CxList *list) { + if (list->cl->change_capacity == NULL) { + return 0; + } + return list->cl->change_capacity(list, cxCollectionSize(list)); +} \ No newline at end of file
--- a/ucx/map.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/map.c Mon Nov 10 21:52:51 2025 +0100 @@ -29,6 +29,8 @@ #include "cx/map.h" #include <string.h> +#include "cx/list.h" + // <editor-fold desc="empty map implementation"> static void cx_empty_map_noop(cx_attr_unused CxMap *map) { @@ -51,7 +53,7 @@ cx_attr_unused enum cx_map_iterator_type type ) { CxMapIterator iter = {0}; - iter.map.c = map; + iter.map = (CxMap*) map; iter.base.valid = cx_empty_map_iter_valid; return iter; } @@ -84,28 +86,243 @@ // </editor-fold> -CxMapIterator cxMapMutIteratorValues(CxMap *map) { +void cxMapClear(CxMap *map) { + map->cl->clear(map); +} + +size_t cxMapSize(const CxMap *map) { + return map->collection.size; +} + +CxMapIterator cxMapIteratorValues(const CxMap *map) { if (map == NULL) map = cxEmptyMap; - CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); - it.base.mutating = true; - return it; + return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES); +} + +CxMapIterator cxMapIteratorKeys(const CxMap *map) { + if (map == NULL) map = cxEmptyMap; + return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); } -CxMapIterator cxMapMutIteratorKeys(CxMap *map) { +CxMapIterator cxMapIterator(const CxMap *map) { if (map == NULL) map = cxEmptyMap; - CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_KEYS); - it.base.mutating = true; - return it; + return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); +} + +int cx_map_put(CxMap *map, CxHashKey key, void *value) { + return map->cl->put(map, key, value) == NULL; } -CxMapIterator cxMapMutIterator(CxMap *map) { - if (map == NULL) map = cxEmptyMap; - CxMapIterator it = map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS); - it.base.mutating = true; - return it; +void *cx_map_emplace(CxMap *map, CxHashKey key) { + return map->cl->put(map, key, NULL); +} + +void *cx_map_get(const CxMap *map, CxHashKey key) { + return map->cl->get(map, key); +} + +int cx_map_remove(CxMap *map, CxHashKey key, void *targetbuf) { + return map->cl->remove(map, key, targetbuf); } void cxMapFree(CxMap *map) { if (map == NULL) return; map->cl->deallocate(map); } + +static void cx_map_remove_uninitialized_entry(CxMap *map, CxHashKey key) { + cx_destructor_func destr_bak = map->collection.simple_destructor; + cx_destructor_func2 destr2_bak = map->collection.advanced_destructor; + map->collection.simple_destructor = NULL; + map->collection.advanced_destructor = NULL; + cxMapRemove(map, key); + map->collection.simple_destructor = destr_bak; + map->collection.advanced_destructor = destr2_bak; +} + +static void* cx_map_simple_clone_func(void *dst, const void *src, const CxAllocator *al, void *data) { + size_t elem_size = *(size_t*)data; + if (dst == NULL) dst = cxMalloc(al, elem_size); + if (dst != NULL) memcpy(dst, src, elem_size); + return dst; +} + +#define use_simple_clone_func(map) cx_map_simple_clone_func, NULL, (void*)&((map)->collection.elem_size) + +int cxMapClone(CxMap *dst, const CxMap *src, cx_clone_func clone_func, + const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + CxMapIterator src_iter = cxMapIterator(src); + for (size_t i = 0; i < cxMapSize(src); i++) { + const CxMapEntry *entry = cxIteratorCurrent(src_iter); + void **dst_mem = cxMapEmplace(dst, *(entry->key)); + if (dst_mem == NULL) { + return 1; // LCOV_EXCL_LINE + } + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void *dst_ptr = clone_func(target, entry->value, clone_allocator, data); + if (dst_ptr == NULL) { + cx_map_remove_uninitialized_entry(dst, *(entry->key)); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + cxIteratorNext(src_iter); + } + return 0; +} + +int cxMapDifference(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + CxMapIterator src_iter = cxMapIterator(minuend); + cx_foreach(const CxMapEntry *, entry, src_iter) { + if (cxMapContains(subtrahend, *entry->key)) { + continue; + } + void** dst_mem = cxMapEmplace(dst, *entry->key); + if (dst_mem == NULL) { + return 1; // LCOV_EXCL_LINE + } + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, entry->value, clone_allocator, data); + if (dst_ptr == NULL) { + cx_map_remove_uninitialized_entry(dst, *(entry->key)); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + return 0; +} + +int cxMapListDifference(CxMap *dst, const CxMap *src, const CxList *keys, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + CxMapIterator src_iter = cxMapIterator(src); + cx_foreach(const CxMapEntry *, entry, src_iter) { + if (cxListContains(keys, entry->key)) { + continue; + } + void** dst_mem = cxMapEmplace(dst, *entry->key); + if (dst_mem == NULL) { + return 1; // LCOV_EXCL_LINE + } + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, entry->value, clone_allocator, data); + if (dst_ptr == NULL) { + cx_map_remove_uninitialized_entry(dst, *(entry->key)); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + return 0; +} + +int cxMapIntersection(CxMap *dst, const CxMap *src, const CxMap *other, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + CxMapIterator src_iter = cxMapIterator(src); + cx_foreach(const CxMapEntry *, entry, src_iter) { + if (!cxMapContains(other, *entry->key)) { + continue; + } + void** dst_mem = cxMapEmplace(dst, *entry->key); + if (dst_mem == NULL) { + return 1; // LCOV_EXCL_LINE + } + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, entry->value, clone_allocator, data); + if (dst_ptr == NULL) { + cx_map_remove_uninitialized_entry(dst, *(entry->key)); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + return 0; +} + +int cxMapListIntersection(CxMap *dst, const CxMap *src, const CxList *keys, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + CxMapIterator src_iter = cxMapIterator(src); + cx_foreach(const CxMapEntry *, entry, src_iter) { + if (!cxListContains(keys, entry->key)) { + continue; + } + void** dst_mem = cxMapEmplace(dst, *entry->key); + if (dst_mem == NULL) { + return 1; // LCOV_EXCL_LINE + } + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, entry->value, clone_allocator, data); + if (dst_ptr == NULL) { + cx_map_remove_uninitialized_entry(dst, *(entry->key)); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + return 0; +} + +int cxMapUnion(CxMap *dst, const CxMap *src, + cx_clone_func clone_func, const CxAllocator *clone_allocator, void *data) { + if (clone_allocator == NULL) clone_allocator = cxDefaultAllocator; + + CxMapIterator src_iter = cxMapIterator(src); + cx_foreach(const CxMapEntry *, entry, src_iter) { + if (cxMapContains(dst, *entry->key)) { + continue; + } + void** dst_mem = cxMapEmplace(dst, *entry->key); + if (dst_mem == NULL) { + return 1; // LCOV_EXCL_LINE + } + void *target = cxCollectionStoresPointers(dst) ? NULL : dst_mem; + void* dst_ptr = clone_func(target, entry->value, clone_allocator, data); + if (dst_ptr == NULL) { + cx_map_remove_uninitialized_entry(dst, *(entry->key)); + return 1; + } + if (cxCollectionStoresPointers(dst)) { + *dst_mem = dst_ptr; + } + } + return 0; +} + +int cxMapCloneSimple(CxMap *dst, const CxMap *src) { + return cxMapClone(dst, src, use_simple_clone_func(src)); +} + +int cxMapDifferenceSimple(CxMap *dst, const CxMap *minuend, const CxMap *subtrahend) { + return cxMapDifference(dst, minuend, subtrahend, use_simple_clone_func(minuend)); +} + +int cxMapListDifferenceSimple(CxMap *dst, const CxMap *src, const CxList *keys) { + return cxMapListDifference(dst, src, keys, use_simple_clone_func(src)); +} + +int cxMapIntersectionSimple(CxMap *dst, const CxMap *src, const CxMap *other) { + return cxMapIntersection(dst, src, other, use_simple_clone_func(src)); +} + +int cxMapListIntersectionSimple(CxMap *dst, const CxMap *src, const CxList *keys) { + return cxMapListIntersection(dst, src, keys, use_simple_clone_func(src)); +} + +int cxMapUnionSimple(CxMap *dst, const CxMap *src) { + return cxMapUnion(dst, src, use_simple_clone_func(src)); +}
--- a/ucx/mempool.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/mempool.c Mon Nov 10 21:52:51 2025 +0100 @@ -116,6 +116,9 @@ if (!ptr) return; struct cx_mempool_s *pool = p; + cx_destructor_func destr = pool->destr; + cx_destructor_func2 destr2 = pool->destr2; + struct cx_mempool_memory_s *mem = (void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s)); @@ -124,11 +127,11 @@ if (mem->destructor) { mem->destructor(mem->c); } - if (pool->destr) { - pool->destr(mem->c); + if (destr != NULL) { + destr(mem->c); } - if (pool->destr2) { - pool->destr2(pool->destr2_data, mem->c); + if (destr2 != NULL) { + destr2(pool->destr2_data, mem->c); } cxFree(pool->base_allocator, mem); size_t last_index = pool->size - 1; @@ -179,18 +182,18 @@ } static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) { - const bool has_destr = pool->destr; - const bool has_destr2 = pool->destr2; + cx_destructor_func destr = pool->destr; + cx_destructor_func2 destr2 = pool->destr2; for (size_t i = 0; i < pool->size; i++) { struct cx_mempool_memory_s *mem = pool->data[i]; if (mem->destructor) { mem->destructor(mem->c); } - if (has_destr) { - pool->destr(mem->c); + if (destr != NULL) { + destr(mem->c); } - if (has_destr2) { - pool->destr2(pool->destr2_data, mem->c); + if (destr2 != NULL) { + destr2(pool->destr2_data, mem->c); } cxFree(pool->base_allocator, mem); } @@ -247,6 +250,9 @@ if (!ptr) return; struct cx_mempool_s *pool = p; + cx_destructor_func destr = pool->destr; + cx_destructor_func2 destr2 = pool->destr2; + struct cx_mempool_memory2_s *mem = (void*) ((char *) ptr - sizeof(struct cx_mempool_memory2_s)); @@ -255,11 +261,11 @@ if (mem->destructor) { mem->destructor(mem->data, mem->c); } - if (pool->destr) { - pool->destr(mem->c); + if (destr != NULL) { + destr(mem->c); } - if (pool->destr2) { - pool->destr2(pool->destr2_data, mem->c); + if (destr2 != NULL) { + destr2(pool->destr2_data, mem->c); } cxFree(pool->base_allocator, mem); size_t last_index = pool->size - 1; @@ -310,18 +316,18 @@ } static void cx_mempool_free_all_advanced(const struct cx_mempool_s *pool) { - const bool has_destr = pool->destr; - const bool has_destr2 = pool->destr2; + cx_destructor_func destr = pool->destr; + cx_destructor_func2 destr2 = pool->destr2; for (size_t i = 0; i < pool->size; i++) { struct cx_mempool_memory2_s *mem = pool->data[i]; if (mem->destructor) { mem->destructor(mem->data, mem->c); } - if (has_destr) { - pool->destr(mem->c); + if (destr != NULL) { + destr(mem->c); } - if (has_destr2) { - pool->destr2(pool->destr2_data, mem->c); + if (destr2 != NULL) { + destr2(pool->destr2_data, mem->c); } cxFree(pool->base_allocator, mem); } @@ -376,13 +382,16 @@ if (!ptr) return; struct cx_mempool_s *pool = p; + cx_destructor_func destr = pool->destr; + cx_destructor_func2 destr2 = pool->destr2; + for (size_t i = 0; i < pool->size; i++) { if (ptr == pool->data[i]) { - if (pool->destr) { - pool->destr(ptr); + if (destr != NULL) { + destr(ptr); } - if (pool->destr2) { - pool->destr2(pool->destr2_data, ptr); + if (destr2 != NULL) { + destr2(pool->destr2_data, ptr); } cxFree(pool->base_allocator, ptr); size_t last_index = pool->size - 1; @@ -427,15 +436,15 @@ } static void cx_mempool_free_all_pure(const struct cx_mempool_s *pool) { - const bool has_destr = pool->destr; - const bool has_destr2 = pool->destr2; + cx_destructor_func destr = pool->destr; + cx_destructor_func2 destr2 = pool->destr2; for (size_t i = 0; i < pool->size; i++) { void *mem = pool->data[i]; - if (has_destr) { - pool->destr(mem); + if (destr != NULL) { + destr(mem); } - if (has_destr2) { - pool->destr2(pool->destr2_data, mem); + if (destr2 != NULL) { + destr2(pool->destr2_data, mem); } cxFree(pool->base_allocator, mem); }
--- a/ucx/properties.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/properties.c Mon Nov 10 21:52:51 2025 +0100 @@ -51,6 +51,12 @@ cxBufferDestroy(&prop->buffer); } +void cxPropertiesReset(CxProperties *prop) { + CxPropertiesConfig config = prop->config; + cxPropertiesDestroy(prop); + cxPropertiesInit(prop, config); +} + int cxPropertiesFilln( CxProperties *prop, const char *buf,
--- a/ucx/string.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/string.c Mon Nov 10 21:52:51 2025 +0100 @@ -461,7 +461,7 @@ delim, limit, (cxstring **) output); } -int cx_strcmp( +int cx_strcmp_( cxstring s1, cxstring s2 ) { @@ -478,7 +478,7 @@ } } -int cx_strcasecmp( +int cx_strcasecmp_( cxstring s1, cxstring s2 ) { @@ -547,7 +547,7 @@ return (cxmutstr) {(char *) result.ptr, result.length}; } -bool cx_strprefix( +bool cx_strprefix_( cxstring string, cxstring prefix ) { @@ -555,7 +555,7 @@ return memcmp(string.ptr, prefix.ptr, prefix.length) == 0; } -bool cx_strsuffix( +bool cx_strsuffix_( cxstring string, cxstring suffix ) { @@ -564,7 +564,7 @@ suffix.ptr, suffix.length) == 0; } -bool cx_strcaseprefix( +bool cx_strcaseprefix_( cxstring string, cxstring prefix ) { @@ -576,7 +576,7 @@ #endif } -bool cx_strcasesuffix( +bool cx_strcasesuffix_( cxstring string, cxstring suffix ) {
--- a/ucx/tree.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ucx/tree.c Mon Nov 10 21:52:51 2025 +0100 @@ -375,7 +375,7 @@ iter.skip = false; // assign base iterator functions - iter.base.mutating = false; + iter.base.allow_remove = false; iter.base.remove = false; iter.base.current_impl = NULL; iter.base.valid = cx_tree_iter_valid; @@ -496,7 +496,7 @@ iter.queue_last = NULL; // assign base iterator functions - iter.base.mutating = false; + iter.base.allow_remove = false; iter.base.remove = false; iter.base.current_impl = NULL; iter.base.valid = cx_tree_visitor_valid; @@ -717,7 +717,7 @@ } // otherwise, create iterator and hand over to other function - CxIterator iter = cxIterator(src, elem_size, num); + CxIterator iter = cxIterator(src, elem_size, num, false); return cx_tree_add_iter(cxIteratorRef(iter), num, sfunc, cfunc, cdata, failed, root, loc_parent, loc_children, loc_last_child, @@ -804,16 +804,12 @@ cx_tree_default_find }; -CxTree *cxTreeCreate( - const CxAllocator *allocator, +CxTree *cxTreeCreate(const CxAllocator *allocator, cx_tree_node_create_func create_func, cx_tree_search_func search_func, cx_tree_search_data_func search_data_func, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next ) { if (allocator == NULL) { allocator = cxDefaultAllocator; @@ -852,15 +848,9 @@ cxFree(tree->allocator, tree); } -CxTree *cxTreeCreateWrapped( - const CxAllocator *allocator, - void *root, - ptrdiff_t loc_parent, - ptrdiff_t loc_children, - ptrdiff_t loc_last_child, - ptrdiff_t loc_prev, - ptrdiff_t loc_next -) { +CxTree *cxTreeCreateWrapped(const CxAllocator *allocator, void *root, + ptrdiff_t loc_parent, ptrdiff_t loc_children, ptrdiff_t loc_last_child, + ptrdiff_t loc_prev, ptrdiff_t loc_next) { if (allocator == NULL) { allocator = cxDefaultAllocator; } @@ -888,11 +878,7 @@ return tree; } -void cxTreeSetParent( - CxTree *tree, - void *parent, - void *child -) { +void cxTreeSetParent(CxTree *tree, void *parent, void *child) { size_t loc_parent = tree->loc_parent; if (tree_parent(child) == NULL) { tree->size++; @@ -900,19 +886,12 @@ cx_tree_link(parent, child, cx_tree_node_layout(tree)); } -void cxTreeAddChildNode( - CxTree *tree, - void *parent, - void *child -) { +void cxTreeAddChildNode(CxTree *tree, void *parent, void *child) { cx_tree_link(parent, child, cx_tree_node_layout(tree)); tree->size++; } -int cxTreeAddChild( - CxTree *tree, - void *parent, - const void *data) { +int cxTreeAddChild(CxTree *tree, void *parent, const void *data) { void *node = tree->node_create(data, tree); if (node == NULL) return 1; cx_tree_zero_pointers(node, cx_tree_node_layout(tree)); @@ -921,6 +900,29 @@ return 0; } +int cxTreeInsert(CxTree *tree, const void *data) { + return tree->cl->insert_element(tree, data); +} + +size_t cxTreeInsertIter(CxTree *tree, CxIteratorBase *iter, size_t n) { + return tree->cl->insert_many(tree, iter, n); +} + +size_t cxTreeInsertArray(CxTree *tree, const void *data, size_t elem_size, size_t n) { + if (n == 0) return 0; + if (n == 1) return 0 == cxTreeInsert(tree, data) ? 1 : 0; + CxIterator iter = cxIterator(data, elem_size, n, false); + return cxTreeInsertIter(tree, cxIteratorRef(iter), n); +} + +void *cxTreeFind( CxTree *tree, const void *data) { + return tree->cl->find(tree, tree->root, data, 0); +} + +void *cxTreeFindInSubtree(CxTree *tree, const void *data, void *subtree_root, size_t max_depth) { + return tree->cl->find(tree, subtree_root, data, max_depth); +} + size_t cxTreeSubtreeSize(CxTree *tree, void *subtree_root) { CxTreeVisitor visitor = cx_tree_visitor( subtree_root, @@ -945,6 +947,10 @@ return visitor.depth; } +size_t cxTreeSize(CxTree *tree) { + return tree->size; +} + size_t cxTreeDepth(CxTree *tree) { CxTreeVisitor visitor = cx_tree_visitor( tree->root, tree->loc_children, tree->loc_next @@ -1052,3 +1058,38 @@ tree->root = NULL; } } + +void cxTreeIteratorDispose(CxTreeIterator *iter) { + cxFreeDefault(iter->stack); + iter->stack = NULL; +} + +void cxTreeVisitorDispose(CxTreeVisitor *visitor) { + struct cx_tree_visitor_queue_s *q = visitor->queue_next; + while (q != NULL) { + struct cx_tree_visitor_queue_s *next = q->next; + cxFreeDefault(q); + q = next; + } +} + +CxTreeIterator cxTreeIterateSubtree(CxTree *tree, void *node, bool visit_on_exit) { + return cx_tree_iterator( + node, visit_on_exit, + tree->loc_children, tree->loc_next + ); +} + +CxTreeVisitor cxTreeVisitSubtree(CxTree *tree, void *node) { + return cx_tree_visitor( + node, tree->loc_children, tree->loc_next + ); +} + +CxTreeIterator cxTreeIterate(CxTree *tree, bool visit_on_exit) { + return cxTreeIterateSubtree(tree, tree->root, visit_on_exit); +} + +CxTreeVisitor cxTreeVisit(CxTree *tree) { + return cxTreeVisitSubtree(tree, tree->root); +}
--- a/ui/cocoa/ListDataSource.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/ListDataSource.m Mon Nov 10 21:52:51 2025 +0100 @@ -98,6 +98,12 @@ case UI_ICON_TEXT_FREE: { break; } + case UI_STRING_EDITABLE: { + break; + } + case UI_BOOL_EDITABLE: { + break; + } } if(freeResult) {
--- a/ui/cocoa/MainWindow.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/MainWindow.h Mon Nov 10 21:52:51 2025 +0100 @@ -31,6 +31,8 @@ @interface MainWindow : NSWindow<UiToplevelObject> +@property UiObject *obj; +@property (strong) NSSplitView *splitview; @property (strong) NSView *sidebar; @property (strong) NSView *leftPanel; @property (strong) NSView *rightPanel;
--- a/ui/cocoa/MainWindow.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/MainWindow.m Mon Nov 10 21:52:51 2025 +0100 @@ -50,12 +50,8 @@ NSWindowStyleMaskMiniaturizable backing:NSBackingStoreBuffered defer:false]; - + _obj = obj; - if(uic_toolbar_isenabled()) { - UiToolbar *toolbar = [[UiToolbar alloc]initWithObject:obj]; - [self setToolbar:toolbar]; - } int top = 4; NSView *content = self.contentView; @@ -72,6 +68,7 @@ splitview.dividerStyle = NSSplitViewDividerStyleThin; splitview.translatesAutoresizingMaskIntoConstraints = false; [self.contentView addSubview:splitview]; + _splitview = splitview; [NSLayoutConstraint activateConstraints:@[ [splitview.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:0], @@ -135,6 +132,12 @@ } _topOffset = top; + if(uic_toolbar_isenabled()) { + UiToolbar *toolbar = [[UiToolbar alloc]initWithWindow:self]; + [self setToolbar:toolbar]; + } + + return self; }
--- a/ui/cocoa/Toolbar.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/Toolbar.h Mon Nov 10 21:52:51 2025 +0100 @@ -28,6 +28,7 @@ #import "toolkit.h" #import "../common/toolbar.h" +#import "MainWindow.h" /* * UiToolbarDelegate @@ -49,9 +50,10 @@ NSMutableArray<NSString*> *defaultItems; } +@property MainWindow *window; @property UiObject *obj; -- (UiToolbar*) initWithObject:(UiObject*)object; +- (UiToolbar*) initWithWindow:(MainWindow*)window; @end
--- a/ui/cocoa/Toolbar.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/Toolbar.m Mon Nov 10 21:52:51 2025 +0100 @@ -45,13 +45,21 @@ @implementation UiToolbar -- (UiToolbar*) initWithObject:(UiObject*)object { +- (UiToolbar*) initWithWindow:(MainWindow*)window { self = [super initWithIdentifier:@"UiToolbar"]; - _obj = object; + _window = window; + _obj = window.obj; allowedItems = [[NSMutableArray alloc]initWithCapacity:16]; defaultItems = [[NSMutableArray alloc]initWithCapacity:16]; + if(window.sidebar) { + [allowedItems addObject:@"sidebar_separator"]; + } + if(window.leftPanel) { + [allowedItems addObject:@"splitview_separator"]; + } + CxMap *toolbarItems = uic_get_toolbar_items(); CxMapIterator i = cxMapIteratorKeys(toolbarItems); cx_foreach(CxHashKey *, key, i) { @@ -61,18 +69,59 @@ [allowedItems addObject: NSToolbarFlexibleSpaceItemIdentifier]; [allowedItems addObject: NSToolbarSpaceItemIdentifier]; - CxList *tbitems[3]; - tbitems[0] = uic_get_toolbar_defaults(UI_TOOLBAR_LEFT); - tbitems[1] = uic_get_toolbar_defaults(UI_TOOLBAR_CENTER); - tbitems[2] = uic_get_toolbar_defaults(UI_TOOLBAR_RIGHT); - for(int t=0;t<3;t++) { - CxIterator iter = cxListIterator(tbitems[t]); + // UI_TOOLBAR_LEFT = 0, + // UI_TOOLBAR_CENTER, + // UI_TOOLBAR_RIGHT, + // UI_TOOLBAR_SIDEBAR_LEFT, + // UI_TOOLBAR_SIDEBAR_RIGHT, + // UI_TOOLBAR_RIGHTPANEL_LEFT, + // UI_TOOLBAR_RIGHTPANEL_CENTER, + // UI_TOOLBAR_RIGHTPANEL_RIGHT + CxList *tbitems[8]; + for(int i=0;i<8;i++) { + tbitems[i] = uic_get_toolbar_defaults(i); + } + + if(window.sidebar) { + CxIterator iter = cxListIterator(tbitems[UI_TOOLBAR_SIDEBAR_LEFT]); cx_foreach(char *, name, iter) { NSString *s = [[NSString alloc] initWithUTF8String:name]; [defaultItems addObject:s]; } + + CxList *sidebarRight = tbitems[UI_TOOLBAR_SIDEBAR_RIGHT]; + if(cxListSize(sidebarRight) > 0) { + [defaultItems addObject:NSToolbarFlexibleSpaceItemIdentifier]; + iter = cxListIterator(sidebarRight); + cx_foreach(char *, name, iter) { + NSString *s = [[NSString alloc] initWithUTF8String:name]; + [defaultItems addObject:s]; + } + } + + [defaultItems addObject:@"sidebar_separator"]; } + int start_pos = UI_TOOLBAR_LEFT; + for(int x=0;x<2;x++) { + for(int t=start_pos;t<start_pos+3;t++) { + CxIterator iter = cxListIterator(tbitems[t]); + cx_foreach(char *, name, iter) { + NSString *s = [[NSString alloc] initWithUTF8String:name]; + [defaultItems addObject:s]; + } + if(t < start_pos+2 && cxListSize(tbitems[t+1]) > 0) { + [defaultItems addObject:NSToolbarFlexibleSpaceItemIdentifier]; + } + } + + if(x == 0 && window.rightPanel) { + [defaultItems addObject:@"splitview_separator"]; + } + start_pos = UI_TOOLBAR_RIGHTPANEL_LEFT; + } + + [self setDelegate:self]; [self setAllowsUserCustomization:YES]; return self; @@ -94,6 +143,18 @@ CxMap *items = uic_get_toolbar_items(); UiToolbarItemI *item = cxMapGet(items, itemIdentifier.UTF8String); if(!item) { + if([itemIdentifier isEqualToString:@"sidebar_separator"]) { + NSTrackingSeparatorToolbarItem *sep = [NSTrackingSeparatorToolbarItem trackingSeparatorToolbarItemWithIdentifier:itemIdentifier + splitView:_window.splitview + dividerIndex:0]; + return sep; + } else if([itemIdentifier isEqualToString:@"splitview_separator"]) { + int dividerIndex = _window.sidebar != nil ? 1 : 0; + NSTrackingSeparatorToolbarItem *sep = [NSTrackingSeparatorToolbarItem trackingSeparatorToolbarItemWithIdentifier:itemIdentifier + splitView:_window.splitview + dividerIndex:dividerIndex]; + return sep; + } return nil; }
--- a/ui/cocoa/appdelegate.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/appdelegate.m Mon Nov 10 21:52:51 2025 +0100 @@ -35,6 +35,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { ui_menu_init(); + NSLog(@"toolkit applicationDidFinishLaunching"); ui_cocoa_onstartup(); }
--- a/ui/cocoa/button.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/button.m Mon Nov 10 21:52:51 2025 +0100 @@ -29,6 +29,7 @@ #import "button.h" #import "EventData.h" #import "Container.h" +#import "image.h" #import <objc/runtime.h> #import <cx/buffer.h> @@ -41,6 +42,9 @@ NSString *label = [[NSString alloc] initWithUTF8String:args->label]; button.title = label; } + if(args->icon) { + button.image = ui_cocoa_named_icon(args->icon);; + } if(args->onclick) { EventData *event = [[EventData alloc] init:args->onclick userdata:args->onclickdata]; @@ -72,6 +76,9 @@ NSString *label = [[NSString alloc] initWithUTF8String:args->label]; button.title = label; } + if(args->icon) { + button.image = ui_cocoa_named_icon(args->icon); + } UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_INTEGER); if(var) {
--- a/ui/cocoa/container.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/container.m Mon Nov 10 21:52:51 2025 +0100 @@ -185,6 +185,22 @@ } +UIWIDGET ui_headerbar_create(UiObject *obj, UiHeaderbarArgs *args) { + return NULL; // TODO +} + +UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args) { + return NULL; // TODO +} + +UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { + return NULL; // TODO +} + +UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { + return NULL; // TODO +} + void ui_container_begin_close(UiObject *obj) {
--- a/ui/cocoa/image.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/image.m Mon Nov 10 21:52:51 2025 +0100 @@ -30,21 +30,71 @@ static NSDictionary *standardIconNames; +#define UI_ICON_ENTRY(x) @#x : x + void ui_icon_init(void) { standardIconNames = @{ - @"NSImageNameActionTemplate": NSImageNameActionTemplate, - @"NSImageNameAddTemplate": NSImageNameAddTemplate, - @"NSImageNameAdvanced": NSImageNameAdvanced, - @"NSImageNameApplicationIcon": NSImageNameApplicationIcon, - @"NSImageNameBluetoothTemplate": NSImageNameBluetoothTemplate, - @"NSImageNameBonjour": NSImageNameBonjour, - @"NSImageNameBookmarksTemplate": NSImageNameBookmarksTemplate, - @"NSImageNameCaution": NSImageNameCaution, - // TODO - @"NSImageNameRefreshTemplate": NSImageNameRefreshTemplate, - @"NSImageNameFolder": NSImageNameFolder, - @"NSImageNameGoForwardTemplate": NSImageNameGoForwardTemplate, - @"NSImageNameGoBackTemplate": NSImageNameGoBackTemplate + UI_ICON_ENTRY(NSImageNameAddTemplate), + UI_ICON_ENTRY(NSImageNameBluetoothTemplate), + UI_ICON_ENTRY(NSImageNameBonjour), + UI_ICON_ENTRY(NSImageNameBookmarksTemplate), + UI_ICON_ENTRY(NSImageNameCaution), + UI_ICON_ENTRY(NSImageNameComputer), + UI_ICON_ENTRY(NSImageNameEnterFullScreenTemplate), + UI_ICON_ENTRY(NSImageNameExitFullScreenTemplate), + UI_ICON_ENTRY(NSImageNameFolder), + UI_ICON_ENTRY(NSImageNameFolderBurnable), + UI_ICON_ENTRY(NSImageNameFolderSmart), + UI_ICON_ENTRY(NSImageNameFollowLinkFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameHomeTemplate), + UI_ICON_ENTRY(NSImageNameIChatTheaterTemplate), + UI_ICON_ENTRY(NSImageNameLockLockedTemplate), + UI_ICON_ENTRY(NSImageNameLockUnlockedTemplate), + UI_ICON_ENTRY(NSImageNameNetwork), + UI_ICON_ENTRY(NSImageNamePathTemplate), + UI_ICON_ENTRY(NSImageNameQuickLookTemplate), + UI_ICON_ENTRY(NSImageNameRefreshFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameRefreshTemplate), + UI_ICON_ENTRY(NSImageNameRemoveTemplate), + UI_ICON_ENTRY(NSImageNameRevealFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameShareTemplate), + UI_ICON_ENTRY(NSImageNameSlideshowTemplate), + UI_ICON_ENTRY(NSImageNameStatusAvailable), + UI_ICON_ENTRY(NSImageNameStatusNone), + UI_ICON_ENTRY(NSImageNameStatusPartiallyAvailable), + UI_ICON_ENTRY(NSImageNameStatusUnavailable), + UI_ICON_ENTRY(NSImageNameStopProgressFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameStopProgressTemplate), + UI_ICON_ENTRY(NSImageNameTrashEmpty), + UI_ICON_ENTRY(NSImageNameTrashFull), + UI_ICON_ENTRY(NSImageNameActionTemplate), + UI_ICON_ENTRY(NSImageNameSmartBadgeTemplate), + UI_ICON_ENTRY(NSImageNameIconViewTemplate), + UI_ICON_ENTRY(NSImageNameListViewTemplate), + UI_ICON_ENTRY(NSImageNameColumnViewTemplate), + UI_ICON_ENTRY(NSImageNameFlowViewTemplate), + UI_ICON_ENTRY(NSImageNameInvalidDataFreestandingTemplate), + UI_ICON_ENTRY(NSImageNameGoForwardTemplate), + UI_ICON_ENTRY(NSImageNameGoBackTemplate), + UI_ICON_ENTRY(NSImageNameGoRightTemplate), + UI_ICON_ENTRY(NSImageNameGoLeftTemplate), + UI_ICON_ENTRY(NSImageNameRightFacingTriangleTemplate), + UI_ICON_ENTRY(NSImageNameLeftFacingTriangleTemplate), + UI_ICON_ENTRY(NSImageNameMobileMe), + UI_ICON_ENTRY(NSImageNameMultipleDocuments), + UI_ICON_ENTRY(NSImageNameUserAccounts), + UI_ICON_ENTRY(NSImageNamePreferencesGeneral), + UI_ICON_ENTRY(NSImageNameAdvanced), + UI_ICON_ENTRY(NSImageNameInfo), + UI_ICON_ENTRY(NSImageNameFontPanel), + UI_ICON_ENTRY(NSImageNameColorPanel), + UI_ICON_ENTRY(NSImageNameUser), + UI_ICON_ENTRY(NSImageNameUserGroup), + UI_ICON_ENTRY(NSImageNameEveryone), + UI_ICON_ENTRY(NSImageNameUserGuest), + UI_ICON_ENTRY(NSImageNameMenuOnStateTemplate), + UI_ICON_ENTRY(NSImageNameMenuMixedStateTemplate), + UI_ICON_ENTRY(NSImageNameApplicationIcon) }; }
--- a/ui/cocoa/label.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/label.m Mon Nov 10 21:52:51 2025 +0100 @@ -27,7 +27,7 @@ */ #import "label.h" -#import "container.h" +#import "Container.h" #import <string.h>
--- a/ui/cocoa/list.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/list.m Mon Nov 10 21:52:51 2025 +0100 @@ -363,6 +363,18 @@ /* --------------------------- SourceList --------------------------- */ +static ui_sourcelist_update_func sclist_update_callback = NULL; + +void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb) { + sclist_update_callback = cb; +} + +void ui_sourcelist_updated(void) { + if(sclist_update_callback) { + sclist_update_callback(); + } +} + static void sublist_free(const CxAllocator *a, UiSubList *sl) { cxFree(a, (char*)sl->varname); cxFree(a, (char*)sl->header);
--- a/ui/cocoa/objs.mk Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/objs.mk Mon Nov 10 21:52:51 2025 +0100 @@ -50,6 +50,7 @@ COCOAOBJ += widget.o COCOAOBJ += image.o COCOAOBJ += entry.o +COCOAOBJ += TabView.o TOOLKITOBJS += $(COCOAOBJ:%=$(COCOA_OBJPRE)%) TOOLKITSOURCE += $(COCOAOBJ:%.o=cocoa/%.m)
--- a/ui/cocoa/toolkit.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/toolkit.h Mon Nov 10 21:52:51 2025 +0100 @@ -31,10 +31,10 @@ #include "../common/context.h" #include "../common/object.h" -@interface UiAppCallback : NSObject { - ui_threadfunc callback; - void *userdata; -} +@interface UiAppCallback : NSObject + +@property ui_threadfunc callback; +@property void *userdata; - (id) initWithCallback:(ui_threadfunc)func userdata:(void*)userdata;
--- a/ui/cocoa/toolkit.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/toolkit.m Mon Nov 10 21:52:51 2025 +0100 @@ -57,6 +57,7 @@ /* ------------------- App Init / Event Loop functions ------------------- */ + void ui_init(const char *appname, int argc, char **argv) { application_name = appname; app_argc = argc; @@ -69,7 +70,9 @@ uic_load_app_properties(); - [NSApplication sharedApplication]; + NSApplication *app = [NSApplication sharedApplication]; + //[app setActivationPolicy:NSApplicationActivationPolicyRegular]; + //[NSBundle loadNibNamed:@"MainMenu" owner:NSApp ]; //[[NSBundle mainBundle] loadNibNamed:@"MainMenu" owner:NSApp topLevelObjects:&topLevelObjects]; @@ -178,8 +181,8 @@ @implementation UiAppCallback - (id) initWithCallback:(ui_threadfunc)func userdata:(void*)userdata { - self->callback = func; - self->userdata = userdata; + _callback = func; + _userdata = userdata; return self; } @@ -190,7 +193,9 @@ } - (void) mainThread:(id)n { - callback(userdata); + if(_callback) { + _callback(_userdata); + } } @end
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/webview.h Mon Nov 10 21:52:51 2025 +0100 @@ -0,0 +1,58 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "toolkit.h" +#import "../ui/webview.h" + +#import <WebKit/WebKit.h> + + +enum WebViewDataType { + WEBVIEW_CONTENT_URL, + WEBVIEW_CONTENT_CONTENT +}; + +struct UiWebViewData { + void *webview; + char *uri; + char *mimetype; + char *encoding; + char *content; + size_t contentlength; + enum WebViewDataType type; + + double zoom; + UiBool javascript; +}; + +UiWebViewData* ui_webview_data_clone(UiWebViewData *data); + +void* ui_webview_get(UiGeneric *g); +const char* ui_webview_get_type(UiGeneric *g); +int ui_webview_set(UiGeneric *g, void *data, const char *type); +void ui_webview_destroy(UiGeneric *g);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/cocoa/webview.m Mon Nov 10 21:52:51 2025 +0100 @@ -0,0 +1,273 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#import "webview.h" +#import "Container.h" + +UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args) { + UiVar *var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_GENERIC); + + WKWebView *webview = [[WKWebView alloc]init]; + + UiLayout layout = UI_ARGS2LAYOUT(args); + ui_container_add(obj, webview, &layout); + + if(var) { + UiGeneric *value = var->value; + value->get = ui_webview_get; + value->get_type = ui_webview_get_type; + value->set = ui_webview_set; + value->destroy = ui_webview_destroy; + value->obj = (__bridge void*)webview; + if(value->value) { + ui_webview_set(value, value->value, UI_WEBVIEW_OBJECT_TYPE); + } else { + UiWebViewData *data = malloc(sizeof(UiWebViewData)); + memset(data, 0, sizeof(UiWebViewData)); + data->webview = (__bridge void*)webview; + data->javascript = TRUE; + data->zoom = 1; + value->value = value; + } + } + + return (__bridge void*)webview; +} + +UiWebViewData* ui_webview_data_clone(UiWebViewData *data) { + UiWebViewData *newdata = malloc(sizeof(UiWebViewData)); + memset(newdata, 0, sizeof(UiWebViewData)); + newdata->zoom = 1; + newdata->javascript = TRUE; + + if(data) { + newdata->uri = data->uri ? strdup(data->uri) : NULL; + newdata->mimetype = data->mimetype ? strdup(data->mimetype) : NULL; + newdata->encoding = data->encoding ? strdup(data->encoding) : NULL; + newdata->type = data->type; + newdata->javascript = data->javascript; + newdata->zoom = data->zoom; + if(data->content && data->contentlength > 0) { + newdata->content = malloc(data->contentlength + 1); + memcpy(newdata->content, data->content, data->contentlength); + newdata->content[data->contentlength] = 0; + newdata->contentlength = data->contentlength; + } + } + + return newdata; +} + +void ui_webview_data_free(UiWebViewData *data) { + if(!data) { + return; + } + free(data->uri); + free(data->mimetype); + free(data->encoding); + free(data->content); + free(data); +} + +void* ui_webview_get(UiGeneric *g) { + UiWebViewData *data = g->value; + WKWebView *webview = (__bridge WKWebView*)g->obj; + + if(data->type == WEBVIEW_CONTENT_URL) { + (void)ui_webview_get_uri(g); // this updates data->uri + } + data->zoom = webview.pageZoom; + + return ui_webview_data_clone(g->value); +} + +const char* ui_webview_get_type(UiGeneric *g) { + return UI_WEBVIEW_OBJECT_TYPE; +} + +int ui_webview_set(UiGeneric *g, void *data, const char *type) { + if(!data || !type) { + return 1; + } + if(strcmp(type, UI_WEBVIEW_OBJECT_TYPE)) { + return 1; + } + ui_webview_data_free(g->value); + g->value = ui_webview_data_clone(data); + + WKWebView *webview = (__bridge WKWebView*)g->obj; + UiWebViewData *webd = data; + if(webd->type == WEBVIEW_CONTENT_URL) { + const char *uri = webd->uri ? webd->uri : "about:blank"; + NSURL *url = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:uri]]; + if(url) { + NSURLRequest *req = [NSURLRequest requestWithURL:url]; + if(req) { + [webview loadRequest:req]; + } + } + } else { + NSString *mimetype = [[NSString alloc]initWithUTF8String: webd->mimetype ? webd->mimetype : "text/plain"]; + NSString *encoding = [[NSString alloc]initWithUTF8String: webd->encoding ? webd->encoding : "UTF-8"]; + NSString *urlStr = [[NSString alloc]initWithUTF8String: webd->uri ? webd->uri : "file:///"]; + NSURL *url = [NSURL URLWithString:urlStr]; + NSData *content = [NSData dataWithBytes:webd->content length:webd->contentlength]; + if(!url) { + url = [NSURL URLWithString:@"about:blank"]; + } + [webview loadData:content MIMEType:mimetype characterEncodingName:encoding baseURL:url]; + } + + webview.pageZoom = webd->zoom; + + return 1; +} + +void ui_webview_destroy(UiGeneric *g) { + ui_webview_data_free(g->value); + g->value = NULL; +} + + +void ui_webview_load_url(UiGeneric *g, const char *url) { + WKWebView *webview = (__bridge WKWebView*)g->obj; + UiWebViewData *data = g->value; + data->type = WEBVIEW_CONTENT_URL; + + if(!url) { + url = "about:blank"; + } + + NSURL *nsurl = [NSURL URLWithString:[[NSString alloc] initWithUTF8String:url]]; + if(nsurl) { + NSURLRequest *req = [NSURLRequest requestWithURL:nsurl]; + if(req) { + [webview loadRequest:req]; + } + } +} + +void ui_webview_load_content( + UiGeneric *g, + const char *uri, + const char *content, + size_t contentlength, + const char *mimetype, + const char *encoding) +{ + UiWebViewData *data = g->value; + WKWebView *webview = (__bridge WKWebView*)g->obj; + + data->type = WEBVIEW_CONTENT_CONTENT; + + free(data->uri); + data->uri = NULL; + free(data->mimetype); + free(data->encoding); + free(data->content); + data->type = WEBVIEW_CONTENT_URL; + + data->content = malloc(contentlength+1); + memcpy(data->content, content, contentlength); + data->content[contentlength] = 0; + + if(!mimetype) { + mimetype = "text/plain"; + } + if(!encoding) { + encoding = "UTF-8"; + } + + data->mimetype = strdup(mimetype); + data->encoding = strdup(encoding); + + NSString *mtype = [[NSString alloc]initWithUTF8String:mimetype]; + NSString *enc = [[NSString alloc]initWithUTF8String:encoding]; + NSData *ct = [NSData dataWithBytes:content length:contentlength]; + NSURL *url; + if(uri) { + NSString *uriStr = [[NSString alloc]initWithUTF8String:uri]; + url = [NSURL URLWithString:uriStr]; + } else { + url = [NSURL URLWithString:@"file:///"]; + } + [webview loadData:ct MIMEType:mtype characterEncodingName:enc baseURL:url]; +} + + +void ui_webview_reload(UiGeneric *g) { + WKWebView *webview = (__bridge WKWebView*)g->obj; + [webview reload]; +} + +UiBool ui_webview_can_go_back(UiGeneric *g) { + return FALSE; +} + +UiBool ui_webview_can_go_forward(UiGeneric *g) { + return FALSE; +} + +void ui_webview_go_back(UiGeneric *g) { + +} + +void ui_webview_go_forward(UiGeneric *g) { + +} + +const char * ui_webview_get_uri(UiGeneric *g) { + UiWebViewData *data = g->value; + WKWebView *webview = (__bridge WKWebView*)g->obj; + + free(data->uri); + data->uri = NULL; + + NSURL *url = webview.URL; + if(url) { + NSString *s = [url absoluteString]; + if(s) { + data->uri = strdup(s.UTF8String); + } + } + return data->uri; +} + +void ui_webview_enable_javascript(UiGeneric *g, UiBool enable) { + // unsupported +} + +void ui_webview_set_zoom(UiGeneric *g, double zoom) { + WKWebView *webview = (__bridge WKWebView*)g->obj; + webview.pageZoom = zoom; +} + +double ui_webview_get_zoom(UiGeneric *g) { + WKWebView *webview = (__bridge WKWebView*)g->obj; + return webview.pageZoom; +}
--- a/ui/cocoa/widget.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/widget.h Mon Nov 10 21:52:51 2025 +0100 @@ -27,5 +27,5 @@ */ #import "toolkit.h" -#import "container.h" +#import "Container.h" #import "../ui/widget.h"
--- a/ui/cocoa/widget.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/widget.m Mon Nov 10 21:52:51 2025 +0100 @@ -45,6 +45,11 @@ } +UIWIDGET ui_separator_create(UiObject *obj, UiWidgetArgs *args) { + // TODO + return NULL; +} + /* custom widget */
--- a/ui/cocoa/window.m Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/cocoa/window.m Mon Nov 10 21:52:51 2025 +0100 @@ -44,6 +44,11 @@ #include <cx/mempool.h> +static int window_default_width = 650; +static int window_default_height = 550; + +static int splitview_window_default_pos = -1; +static UiBool splitview_window_use_prop = TRUE; static UiObject* create_window(const char *title, BOOL simple, BOOL sidebar, BOOL splitview) { UiObject *obj = uic_object_new_toplevel(); @@ -81,9 +86,19 @@ } UiObject* ui_splitview_window(const char *title, UiBool sidebar) { + sleep(1); return create_window(title, FALSE, sidebar, TRUE); } +void ui_window_size(UiObject *obj, int width, int height) { + // TODO +} + +void ui_window_default_size(int width, int height) { + window_default_width = width; + window_default_height = height; +} + /* --------------------------------- File Dialogs --------------------------------- */ void ui_openfiledialog(UiObject *obj, unsigned int mode, ui_callback file_selected_callback, void *cbdata) {
--- a/ui/common/context.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/common/context.c Mon Nov 10 21:52:51 2025 +0100 @@ -549,7 +549,7 @@ cxListFree(groups); } -void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups) { +void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups) { if(enable == NULL) { enable = (ui_enablefunc)ui_set_enabled; } @@ -561,7 +561,7 @@ cxListFree(ls); } -void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates) { +void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates) { ui_widget_set_groups2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates); } @@ -622,7 +622,7 @@ return ctx ? cxRealloc(ctx->allocator, ptr, size) : NULL; } -UIEXPORT char* ui_strdup(UiContext *ctx, const char *str) { +char* ui_strdup(UiContext *ctx, const char *str) { if(!ctx) { return NULL; } @@ -630,3 +630,11 @@ cxmutstr d = cx_strdup_a(ctx->allocator, s); return d.ptr; } + +void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr) { + cxMempoolRegister(ctx->mp, data, (cx_destructor_func)destr); +} + +void ui_set_destructor(void *mem, ui_destructor_func destr) { + cxMempoolSetDestructor(mem, (cx_destructor_func)destr); +}
--- a/ui/common/menu.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/common/menu.c Mon Nov 10 21:52:51 2025 +0100 @@ -41,6 +41,22 @@ static int menu_item_counter = 0; +static void *tmp_eventdata; +static int tmp_eventdata_type; + +void uic_set_tmp_eventdata(void *eventdata, int type) { + tmp_eventdata = eventdata; + tmp_eventdata_type = type; +} + +void* uic_get_tmp_eventdata(void) { + return tmp_eventdata; +} + +int uic_get_tmp_eventdata_type(void) { + return tmp_eventdata_type; +} + void uic_menu_init(void) { global_builder.current = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); current_builder = &global_builder;
--- a/ui/common/menu.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/common/menu.h Mon Nov 10 21:52:51 2025 +0100 @@ -131,6 +131,10 @@ int* uic_copy_groups(const int* groups, size_t *ngroups); +void uic_set_tmp_eventdata(void *eventdata, int type); +void* uic_get_tmp_eventdata(void); +int uic_get_tmp_eventdata_type(void); + #ifdef __cplusplus } #endif
--- a/ui/common/object.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/common/object.c Mon Nov 10 21:52:51 2025 +0100 @@ -28,7 +28,6 @@ #include <stdio.h> #include <stdlib.h> - #include "object.h" #include "context.h" @@ -107,11 +106,15 @@ } UiObject* uic_object_new_toplevel(void) { + fflush(stdout); CxMempool *mp = cxMempoolCreateSimple(256); UiObject *obj = cxCalloc(mp->allocator, 1, sizeof(UiObject)); + fflush(stdout); obj->ctx = uic_context(obj, mp); obj->ctx->parent = ui_global_context(); + fflush(stdout); uic_object_created(obj); + fflush(stdout); return obj; } @@ -141,4 +144,29 @@ } else { toplevel->container_begin = NULL; } + + // TODO: free container? } + +/* + * This might look like a weird function, but in case a container creates a + * sub-container, 2 container objects are added to the list, however we want + * only one container, otherwise ui_container_finish() would not work + */ +void uic_object_remove_second_last_container(UiObject *toplevel) { + if(toplevel->container_end && toplevel->container_end->prev) { + UiContainerX *end = toplevel->container_end; + UiContainerX *rm = toplevel->container_end->prev; + + end->prev = rm->prev; + if(rm->prev) { + rm->prev->next = end; + } else { + toplevel->container_begin = end; + } + + // TODO: free container? + } else { + fprintf(stderr, "Error: uic_object_remove_second_last_container expected at least 2 containers\n"); + } +}
--- a/ui/common/object.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/common/object.h Mon Nov 10 21:52:51 2025 +0100 @@ -51,6 +51,7 @@ void uic_object_push_container(UiObject *toplevel, UiContainerX *newcontainer); void uic_object_pop_container(UiObject *toplevel); +void uic_object_remove_second_last_container(UiObject *toplevel);
--- a/ui/common/toolbar.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/common/toolbar.c Mon Nov 10 21:52:51 2025 +0100 @@ -50,7 +50,7 @@ return str ? strdup(str) : NULL; } -static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs *args, size_t *ngroups) { +static UiToolbarItemArgs itemargs_copy(UiToolbarItemArgs *args, size_t *ngroups, size_t *nvstates) { UiToolbarItemArgs newargs; newargs.label = nl_strdup(args->label); newargs.icon = nl_strdup(args->icon); @@ -58,18 +58,19 @@ newargs.onclick = args->onclick; newargs.onclickdata = args->onclickdata; newargs.groups = uic_copy_groups(args->groups, ngroups); + newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); return newargs; } void ui_toolbar_item_create(const char* name, UiToolbarItemArgs *args) { UiToolbarItem* item = malloc(sizeof(UiToolbarItem)); item->item.type = UI_TOOLBAR_ITEM; - item->args = itemargs_copy(args, &item->ngroups); + item->args = itemargs_copy(args, &item->ngroups, &item->nvstates); cxMapPut(toolbar_items, name, item); } -static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs *args, size_t *ngroups) { +static UiToolbarToggleItemArgs toggleitemargs_copy(UiToolbarToggleItemArgs *args, size_t *ngroups, size_t *nvstates) { UiToolbarToggleItemArgs newargs; newargs.label = nl_strdup(args->label); newargs.icon = nl_strdup(args->icon); @@ -78,21 +79,23 @@ newargs.onchange = args->onchange; newargs.onchangedata = args->onchangedata; newargs.groups = uic_copy_groups(args->groups, ngroups); + newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); return newargs; } void ui_toolbar_toggleitem_create(const char* name, UiToolbarToggleItemArgs *args) { UiToolbarToggleItem* item = malloc(sizeof(UiToolbarToggleItem)); item->item.type = UI_TOOLBAR_TOGGLEITEM; - item->args = toggleitemargs_copy(args, &item->ngroups); + item->args = toggleitemargs_copy(args, &item->ngroups, &item->nvstates); cxMapPut(toolbar_items, name, item); } -static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs *args) { +static UiToolbarMenuArgs menuargs_copy(UiToolbarMenuArgs *args, size_t *nvstates) { UiToolbarMenuArgs newargs; newargs.label = nl_strdup(args->label); newargs.icon = nl_strdup(args->icon); newargs.tooltip = nl_strdup(args->tooltip); + newargs.visibility_states = uic_copy_groups(args->visibility_states, nvstates); return newargs; } @@ -100,7 +103,7 @@ UiToolbarMenuItem* item = malloc(sizeof(UiToolbarMenuItem)); item->item.type = UI_TOOLBAR_MENU; memset(&item->menu, 0, sizeof(UiMenu)); - item->args = menuargs_copy(args); + item->args = menuargs_copy(args, &item->nvstates); item->end = 0;
--- a/ui/common/toolbar.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/common/toolbar.h Mon Nov 10 21:52:51 2025 +0100 @@ -63,18 +63,21 @@ UiToolbarItemI item; UiToolbarItemArgs args; size_t ngroups; + size_t nvstates; }; struct UiToolbarToggleItem { UiToolbarItemI item; UiToolbarToggleItemArgs args; size_t ngroups; + size_t nvstates; }; struct UiToolbarMenuItem { UiToolbarItemI item; UiMenu menu; UiToolbarMenuArgs args; + size_t nvstates; int end; };
--- a/ui/common/types.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/common/types.c Mon Nov 10 21:52:51 2025 +0100 @@ -313,18 +313,32 @@ return d; } +static void string_destroy(UiString *s) { + if(s->value.free && s->value.ptr) { + s->value.free(s->value.ptr); + } +} + UiString* ui_string_new(UiContext *ctx, const char *name) { UiString *s = ui_malloc(ctx, sizeof(UiString)); memset(s, 0, sizeof(UiString)); + ui_set_destructor(s, (ui_destructor_func)string_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_STRING, s); } return s; } +static void text_destroy(UiText *t) { + if(t->destroy) { + t->destroy(t); + } +} + UiText* ui_text_new(UiContext *ctx, const char *name) { UiText *t = ui_malloc(ctx, sizeof(UiText)); memset(t, 0, sizeof(UiText)); + ui_set_destructor(t, (ui_destructor_func)text_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_TEXT, t); } @@ -340,9 +354,16 @@ return r; } -UIEXPORT UiGeneric* ui_generic_new(UiContext *ctx, const char *name) { +static void generic_destroy(UiGeneric *g) { + if(g->destroy) { + g->destroy(g); + } +} + +UiGeneric* ui_generic_new(UiContext *ctx, const char *name) { UiGeneric *g = ui_malloc(ctx, sizeof(UiGeneric)); memset(g, 0, sizeof(UiGeneric)); + ui_set_destructor(g, (ui_destructor_func)generic_destroy); if(name) { uic_reg_var(ctx, name, UI_VAR_GENERIC, g); }
--- a/ui/gtk/headerbar.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/gtk/headerbar.c Mon Nov 10 21:52:51 2025 +0100 @@ -165,6 +165,7 @@ { GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, 0, FALSE); ui_set_widget_groups(obj->ctx, button, item->args.groups); + ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states); WIDGET_ADD_CSS_CLASS(button, "flat"); headerbar_add(headerbar, box, button, pos); } @@ -178,6 +179,7 @@ { GtkWidget *button = gtk_toggle_button_new(); ui_set_widget_groups(obj->ctx, button, item->args.groups); + ui_set_widget_visibility_states(obj->ctx, button, item->args.visibility_states); WIDGET_ADD_CSS_CLASS(button, "flat"); ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.tooltip, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0); headerbar_add(headerbar, box, button, pos); @@ -194,6 +196,7 @@ #if GTK_MAJOR_VERSION >= 4 GtkWidget *menubutton = gtk_menu_button_new(); + ui_set_widget_visibility_states(obj->ctx, menubutton, item->args.visibility_states); if(item->args.label) { gtk_menu_button_set_label(GTK_MENU_BUTTON(menubutton), item->args.label); }
--- a/ui/gtk/list.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/gtk/list.c Mon Nov 10 21:52:51 2025 +0100 @@ -2318,19 +2318,19 @@ static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) { UiListBoxSubList *sublist = data->customdata0; - UiSubListEventData eventdata; - eventdata.list = sublist->var->value; - eventdata.sublist_index = sublist->index; - eventdata.row_index = data->value0; - eventdata.sublist_userdata = sublist->userdata; - eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index); - eventdata.event_data = data->customdata2; + UiSubListEventData *eventdata = &sublist->listbox->current_eventdata; + eventdata->list = sublist->var->value; + eventdata->sublist_index = sublist->index; + eventdata->row_index = data->value0; + eventdata->sublist_userdata = sublist->userdata; + eventdata->row_data = eventdata->list->get(eventdata->list, eventdata->row_index); + eventdata->event_data = data->customdata2; UiEvent event; event.obj = data->obj; event.window = event.obj->window; event.document = event.obj->ctx->document; - event.eventdata = &eventdata; + event.eventdata = eventdata; event.eventdatatype = UI_EVENT_DATA_SUBLIST; event.intval = data->value0; event.set = ui_get_setop(); @@ -2340,13 +2340,15 @@ } if(data->customdata3) { + uic_set_tmp_eventdata(eventdata, UI_EVENT_DATA_SUBLIST); + UIMENU menu = data->customdata3; g_object_set_data(G_OBJECT(button), "ui-button-popup", menu); gtk_popover_popup(GTK_POPOVER(menu)); } } -#if GTK_CHECK_VERSION(3, 0, 0) +#if GTK_CHECK_VERSION(4, 0, 0) static void button_popover_closed(GtkPopover *popover, GtkWidget *button) { g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL); if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) { @@ -2354,6 +2356,14 @@ gtk_widget_set_visible(button, FALSE); } } +#else +static void popup_hide(GtkWidget *self, GtkWidget *button) { + g_object_set_data(G_OBJECT(button), "ui-button-popup", NULL); + if(g_object_get_data(G_OBJECT(button), "ui-button-invisible")) { + g_object_set_data(G_OBJECT(button), "ui-button-invisible", NULL); + gtk_widget_set_visible(button, FALSE); + } +} #endif static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) { @@ -2433,7 +2443,11 @@ if(item->button_menu) { UIMENU menu = ui_contextmenu_create(item->button_menu, listbox->obj, button); event->customdata3 = menu; - g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button); +#if GTK_CHECK_VERSION(4, 0, 0) + g_signal_connect(menu, "closed", G_CALLBACK(button_popover_closed), button); +#else + g_signal_connect(menu, "hide", G_CALLBACK(popup_hide), button); +#endif ui_menubuilder_unref(item->button_menu); } }
--- a/ui/gtk/list.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/gtk/list.h Mon Nov 10 21:52:51 2025 +0100 @@ -127,6 +127,7 @@ void *onbuttonclickdata; GtkListBoxRow *first_row; UiBool header_is_item; + UiSubListEventData current_eventdata; };
--- a/ui/gtk/menu.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/gtk/menu.c Mon Nov 10 21:52:51 2025 +0100 @@ -284,6 +284,7 @@ event->callback = list->callback; event->value = i - 1; event->customdata = elm; + event->customint = UI_EVENT_DATA_LIST_ELM; g_signal_connect( widget, @@ -309,9 +310,17 @@ evt.obj = event->obj; evt.window = event->obj->window; evt.document = event->obj->ctx->document; + if(event->customdata) { + evt.eventdata = event->customdata; + evt.eventdatatype = event->customint; + } else { + evt.eventdata = uic_get_tmp_eventdata(); + evt.eventdatatype = uic_get_tmp_eventdata_type(); + } evt.eventdata = event->customdata; evt.intval = event->value; - event->callback(&evt, event->userdata); + event->callback(&evt, event->userdata); + uic_set_tmp_eventdata(NULL, 0); } void ui_menu_event_toggled(GtkCheckMenuItem *ci, UiEventData *event) { @@ -747,10 +756,16 @@ evt.obj = event->obj; evt.window = event->obj->window; evt.document = event->obj->ctx->document; - evt.eventdata = event->customdata; - evt.eventdatatype = event->customint; + if(event->customdata) { + evt.eventdata = event->customdata; + evt.eventdatatype = event->customint; + } else { + evt.eventdata = uic_get_tmp_eventdata(); + evt.eventdatatype = uic_get_tmp_eventdata_type(); + } evt.intval = intval; - event->callback(&evt, event->userdata); + event->callback(&evt, event->userdata); + uic_set_tmp_eventdata(NULL, 0); } void ui_menu_list_item_activate_event_wrapper(GSimpleAction* self, GVariant* parameter, UiEventData *event) {
--- a/ui/gtk/toolkit.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/gtk/toolkit.c Mon Nov 10 21:52:51 2025 +0100 @@ -40,7 +40,6 @@ #include "../common/toolbar.h" #include "../common/threadpool.h" -#include <cx/utils.h> #include <cx/string.h> #include <cx/printf.h> @@ -544,3 +543,19 @@ ui_set_enabled(widget, FALSE); } } + +void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states) { + if(!states) { + return; + } + size_t nstates = uic_group_array_size(states); + ui_set_widget_nvisibility_states(ctx, widget, states, nstates); +} + + +void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups) { + if(ngroups > 0) { + uic_add_group_widget_i(ctx, widget, (ui_enablefunc)ui_set_visible, states, ngroups); + ui_set_visible(widget, FALSE); + } +}
--- a/ui/gtk/toolkit.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/gtk/toolkit.h Mon Nov 10 21:52:51 2025 +0100 @@ -180,6 +180,8 @@ void ui_set_name_and_style(GtkWidget *widget, const char *name, const char *style); void ui_set_widget_groups(UiContext *ctx, GtkWidget *widget, const int *groups); void ui_set_widget_ngroups(UiContext *ctx, GtkWidget *widget, const int *groups, size_t ngroups); +void ui_set_widget_visibility_states(UiContext *ctx, GtkWidget *widget, const int *states); +void ui_set_widget_nvisibility_states(UiContext *ctx, GtkWidget *widget, const int *states, size_t ngroups); void ui_destroy_userdata(GtkWidget *object, void *userdata); void ui_destroy_vardata(GtkWidget *object, UiVarEventData *data);
--- a/ui/motif/container.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/motif/container.c Mon Nov 10 21:52:51 2025 +0100 @@ -227,6 +227,96 @@ grid->container.container.newline = FALSE; } +/* -------------------------- Frame Container -------------------------- */ + +UIWIDGET ui_frame_create(UiObject *obj, UiFrameArgs *args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UiLayout layout = UI_ARGS2LAYOUT(args); + + Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); + + char *name = args->name ? (char*)args->name : "frame"; + Widget frame = XmCreateFrame(parent, name, xargs, 6); + XtManageChild(frame); + ui_container_add(ctn, frame); + + if(args->label) { + XmString s = XmStringCreateLocalized((char*)args->label); + n = 0; + XtSetArg(xargs[n], XmNlabelString, s); n++; + XtSetArg(xargs[n], XmNchildType, XmFRAME_TITLE_CHILD); n++; + Widget label = XmCreateLabel(frame, "frame_label", xargs, n); + XtManageChild(label); + XmStringFree(s); + } + + UiContainerX *container = ui_frame_container(obj, frame); + uic_object_push_container(obj, container); + + UiContainerArgs sub_args = { + .spacing = args->spacing, + .columnspacing = args->columnspacing, + .rowspacing = args->rowspacing + }; + switch(args->subcontainer) { + default: break; + case UI_CONTAINER_VBOX: { + ui_vbox_create(obj, &sub_args); + uic_object_remove_second_last_container(obj); + break; + } + case UI_CONTAINER_HBOX: { + ui_hbox_create(obj, &sub_args); + uic_object_remove_second_last_container(obj); + break; + } + case UI_CONTAINER_GRID: { + ui_grid_create(obj, &sub_args); + uic_object_remove_second_last_container(obj); + break; + } + } + + + return frame; +} + +UiContainerX* ui_frame_container(UiObject *obj, Widget frame) { + UiContainerPrivate *ctn = ui_malloc(obj->ctx, sizeof(UiContainerPrivate)); + memset(ctn, 0, sizeof(UiContainerPrivate)); + ctn->prepare = ui_frame_container_prepare; + ctn->add = ui_frame_container_add; + ctn->widget = frame; + return (UiContainerX*)ctn; +} + +Widget ui_frame_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n) { + int a = *n; + XtSetArg(args[a], XmNchildType, XmFRAME_WORKAREA_CHILD); + *n = a+1; + return ctn->widget; +} + +void ui_frame_container_add(UiContainerPrivate *ctn, Widget widget) { + // NOOP +} + +/* -------------------------- SplitPane -------------------------- */ + +UIWIDGET ui_splitpane_create(UiObject *obj, UiSplitPaneArgs *args, int orientation) { + return NULL; // TODO +} + +UIWIDGET ui_hsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { + return ui_splitpane_create(obj, args, XmHORIZONTAL); +} + +UIWIDGET ui_vsplitpane_create(UiObject *obj, UiSplitPaneArgs *args) { + return ui_splitpane_create(obj, args, XmVERTICAL); +} /* -------------------------- TabView Container -------------------------- */
--- a/ui/motif/container.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/motif/container.h Mon Nov 10 21:52:51 2025 +0100 @@ -156,6 +156,10 @@ Widget ui_grid_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n); void ui_grid_container_add(UiContainerPrivate *ctn, Widget widget); +UiContainerX* ui_frame_container(UiObject *obj, Widget frame); +Widget ui_frame_container_prepare(UiContainerPrivate *ctn, UiLayout *layout, Arg *args, int *n); +void ui_frame_container_add(UiContainerPrivate *ctn, Widget widget); + #ifdef __cplusplus } #endif
--- a/ui/motif/graphics.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/motif/graphics.c Mon Nov 10 21:52:51 2025 +0100 @@ -34,3 +34,87 @@ #include "graphics.h" #include "container.h" + +UIWIDGET ui_drawingarea_create(UiObject *obj, UiDrawingAreaArgs *args) { + Arg xargs[16]; + int n = 0; + + UiContainerPrivate *ctn = ui_obj_container(obj); + UiLayout layout = UI_ARGS2LAYOUT(args); + + Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); + char *name = args->name ? (char*)args->name : "drawingarea"; + + Widget widget = XmCreateDrawingArea(parent, name, xargs, n); + XtManageChild(widget); + ui_container_add(ctn, widget); + + UiDrawingArea *drawingarea = malloc(sizeof(UiDrawingArea)); + drawingarea->obj = obj; + drawingarea->draw = args->draw; + drawingarea->drawdata = args->drawdata; + drawingarea->onclick = args->onclick; + drawingarea->onclickdata = args->onclickdata; + drawingarea->onmotion = args->onmotion; + drawingarea->onmotiondata = args->onmotiondata; + drawingarea->gc = NULL; + + XtAddCallback( + widget, + XmNdestroyCallback, + (XtCallbackProc)ui_drawingarea_destroy, + drawingarea); + XtAddCallback( + widget, + XmNexposeCallback, + (XtCallbackProc)ui_drawingarea_expose, + drawingarea); + + return widget; +} + +void ui_drawingarea_destroy(Widget w, UiDrawingArea *drawingarea, XtPointer d) { + if(drawingarea->gc) { + XFreeGC(XtDisplay(w), drawingarea->gc); + } + free(drawingarea); +} + +void ui_drawingarea_expose(Widget w, UiDrawingArea *drawingarea, XtPointer d) { + Display *dp = XtDisplay(w); + + if(!drawingarea->gc) { + XGCValues gcvals; + gcvals.foreground = BlackPixelOfScreen(XtScreen(w)); + drawingarea->gc = XCreateGC(dp, XtWindow(w), (GCForeground), &gcvals); + } + + if(drawingarea->draw) { + UiEvent event; + event.obj = drawingarea->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = NULL; + event.eventdatatype = 0; + event.intval = 0; + event.set = 0; + + UiXlibGraphics g; + g.g.width = w->core.width; + g.g.height = w->core.height; + g.widget = w; + g.display = dp; + g.colormap = w->core.colormap; + g.gc = drawingarea->gc; + + drawingarea->draw(&event, (UiGraphics*)&g, drawingarea->drawdata); + } +} + +void ui_drawingarea_getsize(UIWIDGET drawingarea, int *width, int *height) { + +} + +void ui_drawingarea_redraw(UIWIDGET drawingarea) { + +}
--- a/ui/motif/graphics.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/motif/graphics.h Mon Nov 10 21:52:51 2025 +0100 @@ -35,7 +35,30 @@ #ifdef __cplusplus extern "C" { #endif + +typedef struct UiDrawingArea { + UiObject *obj; + ui_drawfunc draw; + void *drawdata; + ui_callback onclick; + void *onclickdata; + ui_callback onmotion; + void *onmotiondata; + + GC gc; +} UiDrawingArea; +typedef struct UiXlibGraphics { + UiGraphics g; + Display *display; + Widget widget; + Colormap colormap; + GC gc; +} UiXlibGraphics; + +void ui_drawingarea_destroy(Widget w, UiDrawingArea *drawingarea, XtPointer d); + +void ui_drawingarea_expose(Widget w, UiDrawingArea *drawingarea, XtPointer d); #ifdef __cplusplus
--- a/ui/motif/label.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/motif/label.c Mon Nov 10 21:52:51 2025 +0100 @@ -47,17 +47,34 @@ XtSetArg(xargs[n], XmNalignment, align); n++; XmString label = NULL; - if(args->label) { - label = XmStringCreateLocalized((char*)args->label); - XtSetArg(xargs[n], XmNlabelString, label); n++; + char *lbl = (char*)args->label; + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); + if(var) { + UiString *s = var->value; + lbl = s->value.ptr; } + if(lbl) { + label = XmStringCreateLocalized(lbl); + } else { + label = XmStringCreateLocalized(""); + } + + XtSetArg(xargs[n], XmNlabelString, label); n++; char *name = args->name ? (char*)args->name : "label"; Widget w = XmCreateLabel(parent, name, xargs, n); XtManageChild(w); ui_container_add(ctn, w); XmStringFree(label); + + if(var) { + UiString *s = var->value; + s->obj = w; + s->get = ui_label_get; + s->set = ui_label_set; + } + return w; } @@ -73,6 +90,36 @@ return label_create(obj, args, XmALIGNMENT_END); } +char* ui_label_get(UiString *s) { + if(s->value.free) { + s->value.free(s->value.ptr); + s->value.free = NULL; + s->value.ptr = NULL; + } + Widget w = s->obj; + XmString s1 = NULL; + XtVaGetValues(w, XmNlabelString, &s1, NULL); + if(s1) { + char *value; + if(XmStringGetLtoR(s1, XmFONTLIST_DEFAULT_TAG, &value)) { + s->value.ptr = value; + s->value.free = (cx_destructor_func)XtFree; + } + } + return s->value.ptr; +} + +void ui_label_set(UiString *s, const char *str) { + if(s->value.free) { + s->value.free(s->value.ptr); + s->value.free = NULL; + s->value.ptr = NULL; + } + Widget w = s->obj; + XmString s1 = XmStringCreateLocalized(str ? (char*)str : ""); + XtVaSetValues(w, XmNlabelString, s1, NULL); + XmStringFree(s1); +} /* -------------------------- progressbar/spiner -------------------------- */
--- a/ui/motif/label.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/motif/label.h Mon Nov 10 21:52:51 2025 +0100 @@ -46,6 +46,8 @@ Pixel color; } UiProgressBar; +char* ui_label_get(UiString *s); +void ui_label_set(UiString *s, const char *str); double ui_progressbar_get(UiDouble *d); void ui_progressbar_set(UiDouble *d, double value);
--- a/ui/motif/toolkit.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/motif/toolkit.c Mon Nov 10 21:52:51 2025 +0100 @@ -120,6 +120,10 @@ return application_name; } +XtAppContext ui_motif_get_app(void) { + return app; +} + Display* ui_motif_get_display() { return display; }
--- a/ui/motif/toolkit.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/motif/toolkit.h Mon Nov 10 21:52:51 2025 +0100 @@ -84,6 +84,7 @@ void ui_exit_mainloop(); +XtAppContext ui_motif_get_app(void); Display* ui_motif_get_display(void); void ui_set_active_window(Widget w);
--- a/ui/ui/toolbar.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/ui/toolbar.h Mon Nov 10 21:52:51 2025 +0100 @@ -45,6 +45,7 @@ void* onclickdata; const int *groups; + const int *visibility_states; } UiToolbarItemArgs; typedef struct UiToolbarToggleItemArgs { @@ -57,12 +58,14 @@ void *onchangedata; const int *groups; + const int *visibility_states; } UiToolbarToggleItemArgs; typedef struct UiToolbarMenuArgs { const char *label; const char *icon; const char *tooltip; + const int *visibility_states; } UiToolbarMenuArgs; enum UiToolbarPos {
--- a/ui/ui/toolkit.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/ui/toolkit.h Mon Nov 10 21:52:51 2025 +0100 @@ -256,6 +256,9 @@ typedef void(*ui_enablefunc)(void*, int); +typedef void (*ui_destructor_func)(void *memory); + + struct UiObject { /* * native widget @@ -405,6 +408,7 @@ void* (*get)(UiGeneric*); const char* (*get_type)(UiGeneric*); int (*set)(UiGeneric*, void *, const char *type); + void (*destroy)(UiGeneric*); void *obj; void *value; @@ -557,8 +561,8 @@ UIEXPORT void ui_detach_document(UiContext *ctx, void *document); UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...); -UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups); -UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates); +UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, const int *groups, int ngroups); +UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, const int *states, int nstates); UIEXPORT void ui_set_group(UiContext *ctx, int group); UIEXPORT void ui_unset_group(UiContext *ctx, int group); @@ -572,6 +576,8 @@ UIEXPORT void ui_free(UiContext *ctx, void *ptr); UIEXPORT void* ui_realloc(UiContext *ctx, void *ptr, size_t size); UIEXPORT char* ui_strdup(UiContext *ctx, const char *str); +UIEXPORT void ui_reg_destructor(UiContext *ctx, void *data, ui_destructor_func destr); +UIEXPORT void ui_set_destructor(void *mem, ui_destructor_func destr); // types
--- a/ui/ui/webview.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/ui/webview.h Mon Nov 10 21:52:51 2025 +0100 @@ -36,8 +36,16 @@ extern "C" { #endif +/* + * WebView type string used by UiGeneric + */ #define UI_WEBVIEW_OBJECT_TYPE "webview" - + +/* + * UiWebViewData* is returned by a webviews UiGeneric->get function + */ +typedef struct UiWebViewData UiWebViewData; + typedef struct UiWebviewArgs { UiBool fill; UiBool hexpand; @@ -63,11 +71,11 @@ #define ui_webview(obj, ...) ui_webview_create(obj, &(UiWebviewArgs){ __VA_ARGS__ } ) -UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args); +UIEXPORT UIWIDGET ui_webview_create(UiObject *obj, UiWebviewArgs *args); -void ui_webview_load_url(UiGeneric *g, const char *url); +UIEXPORT void ui_webview_load_url(UiGeneric *g, const char *url); -void ui_webview_load_content( +UIEXPORT void ui_webview_load_content( UiGeneric *g, const char *uri, const char *content, @@ -75,16 +83,20 @@ const char *mimetype, const char *encoding); +/* + * Frees a UiWebViewData object returned by a webviews UiGeneric->get function + */ +UIEXPORT void ui_webview_data_free(UiWebViewData *data); -void ui_webview_reload(UiGeneric *g); -UiBool ui_webview_can_go_back(UiGeneric *g); -UiBool ui_webview_can_go_forward(UiGeneric *g); -void ui_webview_go_back(UiGeneric *g); -void ui_webview_go_forward(UiGeneric *g); -const char * ui_webview_get_uri(UiGeneric *g); -void ui_webview_enable_javascript(UiGeneric *g, UiBool enable); -void ui_webview_set_zoom(UiGeneric *g, double zoom); -double ui_webview_get_zoom(UiGeneric *g); +UIEXPORT void ui_webview_reload(UiGeneric *g); +UIEXPORT UiBool ui_webview_can_go_back(UiGeneric *g); +UIEXPORT UiBool ui_webview_can_go_forward(UiGeneric *g); +UIEXPORT void ui_webview_go_back(UiGeneric *g); +UIEXPORT void ui_webview_go_forward(UiGeneric *g); +UIEXPORT const char * ui_webview_get_uri(UiGeneric *g); +UIEXPORT void ui_webview_enable_javascript(UiGeneric *g, UiBool enable); +UIEXPORT void ui_webview_set_zoom(UiGeneric *g, double zoom); +UIEXPORT double ui_webview_get_zoom(UiGeneric *g); #ifdef __cplusplus
--- a/ui/win32/button.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/win32/button.c Mon Nov 10 21:52:51 2025 +0100 @@ -51,10 +51,10 @@ HWND hwnd = CreateWindow( "BUTTON", args->label, - WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, + WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON, 0, 0, 100, 30, parent, - (HMENU)0, + (HMENU)1, hInstance, NULL); ui_win32_set_ui_font(hwnd); @@ -85,6 +85,9 @@ void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { UiWidget *w = (UiWidget*)widget; + printf("button eventproc\n"); + fflush(stdout); + UiEvent e; e.obj = w->obj; e.document = e.obj->ctx->document;
--- a/ui/win32/container.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/win32/container.c Mon Nov 10 21:52:51 2025 +0100 @@ -139,6 +139,7 @@ NULL, hInstance, NULL); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)ui_default_eventproc); W32Widget *widget = w32_widget_new(&grid_layout_widget_class, hwnd); ui_container_add(container, widget, &layout);
--- a/ui/win32/objs.mk Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/win32/objs.mk Mon Nov 10 21:52:51 2025 +0100 @@ -37,6 +37,7 @@ WIN32OBJ += container.obj WIN32OBJ += button.obj WIN32OBJ += grid.obj +WIN32OBJ += text.obj TOOLKITOBJS += $(WIN32OBJ:%=$(WIN32_OBJPRE)%) TOOLKITSOURCE += $(WIN32OBJ:%.obj=win32/%.c)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/win32/text.c Mon Nov 10 21:52:51 2025 +0100 @@ -0,0 +1,112 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "text.h" + +static W32WidgetClass textfield_widget_class = { + .eventproc = ui_textfield_eventproc, + .enable = w32_widget_default_enable, + .show = w32_widget_default_show, + .get_preferred_size = ui_textfield_get_preferred_size, + .destroy = w32_widget_default_destroy +}; + +UIWIDGET ui_textfield_create(UiObject *obj, UiTextFieldArgs *args) { + HINSTANCE hInstance = GetModuleHandle(NULL); + UiContainerPrivate *container = ui_obj_container(obj); + HWND parent = ui_container_get_parent(container); + UiLayout layout = UI_ARGS2LAYOUT(args); + + int width = args->width >= 0 ? args->width : 100; + + HWND hwnd = CreateWindowEx( + WS_EX_CLIENTEDGE, + "EDIT", + "", + WS_VISIBLE | WS_CHILD | ES_LEFT | ES_AUTOHSCROLL, + 0, 0, width, 25, + parent, + (HMENU)0, + hInstance, + NULL); + ui_win32_set_ui_font(hwnd); + + W32Widget *widget = w32_widget_create(&textfield_widget_class, hwnd, sizeof(UiTextField)); + ui_container_add(container, widget, &layout); + + UiTextField *textfield = (UiTextField*)widget; + textfield->width = width; + textfield->widget.var = uic_widget_var(obj->ctx, obj->ctx, args->value, args->varname, UI_VAR_STRING); + textfield->widget.callback = args->onchange; + textfield->widget.callbackdata = args->onchangedata; + + if (textfield->widget.var) { + UiString *s = textfield->widget.var->value; + + if (s->value.ptr) { + // TODO: set textfield string + } + s->obj = widget; + s->get = ui_textfield_get; + s->set = ui_textfield_set; + } + + return widget; +} + +W32Size ui_textfield_get_preferred_size(W32Widget *widget) { + UiTextField *textfield = (UiTextField *)widget; + return (W32Size){ .width = textfield->width, .height = 32}; +} + +void ui_textfield_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + +} + +char* ui_textfield_get(UiString *s) { + UiTextField *textfield = s->obj; + + if (s->value.free) { + s->value.free(s->value.ptr); + } + + int len = GetWindowTextLength(textfield->widget.widget.hwnd); + s->value.ptr = calloc(len+1, 1); + GetWindowText(textfield->widget.widget.hwnd, s->value.ptr, len+1); + + return s->value.ptr; +} + +void ui_textfield_set(UiString *s, const char *value) { + UiTextField *textfield = s->obj; + if (s->value.free) { + s->value.free(s->value.ptr); + } + s->value.ptr = NULL; + SetWindowText(textfield->widget.widget.hwnd, value); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/win32/text.h Mon Nov 10 21:52:51 2025 +0100 @@ -0,0 +1,47 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TEXT_H +#define TEXT_H + +#include "../ui/text.h" +#include "container.h" +#include "toolkit.h" + +typedef struct UiTextField { + UiWidget widget; + int width; +} UiTextField; + +W32Size ui_textfield_get_preferred_size(W32Widget *widget); +void ui_textfield_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +char* ui_textfield_get(UiString *s); +void ui_textfield_set(UiString *s, const char *value); + +#endif /* TEXT_H */ \ No newline at end of file
--- a/ui/win32/toolkit.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/win32/toolkit.c Mon Nov 10 21:52:51 2025 +0100 @@ -123,4 +123,34 @@ void ui_show(UiObject *obj) { ui_set_visible(obj->widget, TRUE); +} + +LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + W32Widget *widget = (W32Widget*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (widget && widget->wclass->eventproc) { + widget->wclass->eventproc(widget, hwnd, uMsg, wParam, lParam); + } + switch(uMsg) { + case WM_DESTROY: { + PostQuitMessage(0); + break; + } + case WM_COMMAND: { + HWND hwndCtrl = (HWND)lParam; + W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA); + if (cmdWidget && cmdWidget->wclass->eventproc) { + cmdWidget->wclass->eventproc(cmdWidget, hwnd, uMsg, wParam, lParam); + } + } + case WM_SIZE: { + int width = LOWORD(lParam); + int height = HIWORD(lParam); + if (widget->layout) { + widget->layout(widget->layoutmanager, width, height); + } + break; + } + default: return DefWindowProc(hwnd, uMsg, wParam, lParam); + } + return 0; } \ No newline at end of file
--- a/ui/win32/toolkit.h Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/win32/toolkit.h Mon Nov 10 21:52:51 2025 +0100 @@ -53,6 +53,8 @@ int64_t intvalue; } UiWidget; +LRESULT CALLBACK ui_default_eventproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + HFONT ui_win32_get_font(void); void ui_win32_set_ui_font(HWND control);
--- a/ui/win32/window.c Sun Oct 19 21:20:08 2025 +0200 +++ b/ui/win32/window.c Mon Nov 10 21:52:51 2025 +0100 @@ -52,41 +52,12 @@ static const char *mainWindowClass = "UiMainWindow"; -LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { - W32Widget *widget = (W32Widget*)GetWindowLongPtr(hwnd, GWLP_USERDATA); - if (widget && widget->wclass->eventproc) { - widget->wclass->eventproc(widget, hwnd, uMsg, wParam, lParam); - } - switch(uMsg) { - case WM_DESTROY: { - PostQuitMessage(0); - break; - } - case WM_COMMAND: { - HWND hwndCtrl = (HWND)lParam; - W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA); - if (cmdWidget && cmdWidget->wclass->eventproc) { - cmdWidget->wclass->eventproc(cmdWidget, hwnd, uMsg, wParam, lParam); - } - } - case WM_SIZE: { - int width = LOWORD(lParam); - int height = HIWORD(lParam); - if (widget->layout) { - widget->layout(widget->layoutmanager, width, height); - } - break; - } - default: return DefWindowProc(hwnd, uMsg, wParam, lParam); - } - return 0; -} void ui_window_init(void) { hInstance = GetModuleHandle(NULL); WNDCLASSEX wc = { sizeof(WNDCLASSEX) }; - wc.lpfnWndProc = WindowProc; + wc.lpfnWndProc = ui_default_eventproc; wc.hInstance = hInstance; wc.lpszClassName = mainWindowClass; wc.hCursor = LoadCursor(NULL, IDC_ARROW);