ucx 3 update, basic dav commands work, most stuff is still broken

Fri, 21 Apr 2023 21:25:32 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Fri, 21 Apr 2023 21:25:32 +0200
changeset 747
efbd59642577
parent 746
a569148841ff
child 748
49a284f61e8c

ucx 3 update, basic dav commands work, most stuff is still broken

dav/Makefile file | annotate | diff | comparison | revisions
dav/assistant.c file | annotate | diff | comparison | revisions
dav/assistant.h file | annotate | diff | comparison | revisions
dav/config.c file | annotate | diff | comparison | revisions
dav/config.h file | annotate | diff | comparison | revisions
dav/db.c file | annotate | diff | comparison | revisions
dav/db.h file | annotate | diff | comparison | revisions
dav/error.c file | annotate | diff | comparison | revisions
dav/error.h file | annotate | diff | comparison | revisions
dav/finfo.c file | annotate | diff | comparison | revisions
dav/finfo.h file | annotate | diff | comparison | revisions
dav/main.c file | annotate | diff | comparison | revisions
dav/main.h file | annotate | diff | comparison | revisions
dav/opt.h file | annotate | diff | comparison | revisions
dav/optparser.c file | annotate | diff | comparison | revisions
dav/optparser.h file | annotate | diff | comparison | revisions
dav/pwd.c file | annotate | diff | comparison | revisions
dav/pwd.h file | annotate | diff | comparison | revisions
dav/scfg.c file | annotate | diff | comparison | revisions
dav/scfg.h file | annotate | diff | comparison | revisions
dav/sopt.c file | annotate | diff | comparison | revisions
dav/sopt.h file | annotate | diff | comparison | revisions
dav/sync.c file | annotate | diff | comparison | revisions
dav/sync.h file | annotate | diff | comparison | revisions
dav/system.c file | annotate | diff | comparison | revisions
dav/tags.c file | annotate | diff | comparison | revisions
dav/tags.h file | annotate | diff | comparison | revisions
dav/tar.c file | annotate | diff | comparison | revisions
libidav/Makefile file | annotate | diff | comparison | revisions
libidav/crypto.c file | annotate | diff | comparison | revisions
libidav/crypto.h file | annotate | diff | comparison | revisions
libidav/davqlexec.c file | annotate | diff | comparison | revisions
libidav/davqlexec.h file | annotate | diff | comparison | revisions
libidav/davqlparser.c file | annotate | diff | comparison | revisions
libidav/davqlparser.h file | annotate | diff | comparison | revisions
libidav/methods.c file | annotate | diff | comparison | revisions
libidav/methods.h file | annotate | diff | comparison | revisions
libidav/resource.c file | annotate | diff | comparison | revisions
libidav/resource.h file | annotate | diff | comparison | revisions
libidav/session.c file | annotate | diff | comparison | revisions
libidav/session.h file | annotate | diff | comparison | revisions
libidav/utils.c file | annotate | diff | comparison | revisions
libidav/utils.h file | annotate | diff | comparison | revisions
libidav/versioning.c file | annotate | diff | comparison | revisions
libidav/webdav.c file | annotate | diff | comparison | revisions
libidav/webdav.h file | annotate | diff | comparison | revisions
libidav/xml.c file | annotate | diff | comparison | revisions
libidav/xml.h file | annotate | diff | comparison | revisions
test/Makefile file | annotate | diff | comparison | revisions
test/base64.c file | annotate | diff | comparison | revisions
test/base64.h file | annotate | diff | comparison | revisions
test/crypto.c file | annotate | diff | comparison | revisions
test/crypto.h file | annotate | diff | comparison | revisions
test/main.c file | annotate | diff | comparison | revisions
test/test.c file | annotate | diff | comparison | revisions
test/test.h file | annotate | diff | comparison | revisions
ucx/Makefile file | annotate | diff | comparison | revisions
ucx/README file | annotate | diff | comparison | revisions
ucx/allocator.c file | annotate | diff | comparison | revisions
ucx/array_list.c file | annotate | diff | comparison | revisions
ucx/avl.c file | annotate | diff | comparison | revisions
ucx/basic_mempool.c file | annotate | diff | comparison | revisions
ucx/buffer.c file | annotate | diff | comparison | revisions
ucx/compare.c file | annotate | diff | comparison | revisions
ucx/cx/allocator.h file | annotate | diff | comparison | revisions
ucx/cx/array_list.h file | annotate | diff | comparison | revisions
ucx/cx/basic_mempool.h file | annotate | diff | comparison | revisions
ucx/cx/buffer.h file | annotate | diff | comparison | revisions
ucx/cx/collection.h file | annotate | diff | comparison | revisions
ucx/cx/common.h file | annotate | diff | comparison | revisions
ucx/cx/compare.h file | annotate | diff | comparison | revisions
ucx/cx/hash_key.h file | annotate | diff | comparison | revisions
ucx/cx/hash_map.h file | annotate | diff | comparison | revisions
ucx/cx/iterator.h file | annotate | diff | comparison | revisions
ucx/cx/linked_list.h file | annotate | diff | comparison | revisions
ucx/cx/list.h file | annotate | diff | comparison | revisions
ucx/cx/map.h file | annotate | diff | comparison | revisions
ucx/cx/mempool.h file | annotate | diff | comparison | revisions
ucx/cx/printf.h file | annotate | diff | comparison | revisions
ucx/cx/string.h file | annotate | diff | comparison | revisions
ucx/cx/tree.h file | annotate | diff | comparison | revisions
ucx/cx/utils.h file | annotate | diff | comparison | revisions
ucx/hash_key.c file | annotate | diff | comparison | revisions
ucx/hash_map.c file | annotate | diff | comparison | revisions
ucx/linked_list.c file | annotate | diff | comparison | revisions
ucx/list.c file | annotate | diff | comparison | revisions
ucx/logging.c file | annotate | diff | comparison | revisions
ucx/map.c file | annotate | diff | comparison | revisions
ucx/mempool.c file | annotate | diff | comparison | revisions
ucx/printf.c file | annotate | diff | comparison | revisions
ucx/properties.c file | annotate | diff | comparison | revisions
ucx/stack.c file | annotate | diff | comparison | revisions
ucx/string.c file | annotate | diff | comparison | revisions
ucx/szmul.c file | annotate | diff | comparison | revisions
ucx/test.c file | annotate | diff | comparison | revisions
ucx/tree.c file | annotate | diff | comparison | revisions
ucx/ucx.c file | annotate | diff | comparison | revisions
ucx/ucx/allocator.h file | annotate | diff | comparison | revisions
ucx/ucx/avl.h file | annotate | diff | comparison | revisions
ucx/ucx/buffer.h file | annotate | diff | comparison | revisions
ucx/ucx/list.h file | annotate | diff | comparison | revisions
ucx/ucx/logging.h file | annotate | diff | comparison | revisions
ucx/ucx/map.h file | annotate | diff | comparison | revisions
ucx/ucx/mempool.h file | annotate | diff | comparison | revisions
ucx/ucx/properties.h file | annotate | diff | comparison | revisions
ucx/ucx/stack.h file | annotate | diff | comparison | revisions
ucx/ucx/string.h file | annotate | diff | comparison | revisions
ucx/ucx/test.h file | annotate | diff | comparison | revisions
ucx/ucx/ucx.h file | annotate | diff | comparison | revisions
ucx/ucx/utils.h file | annotate | diff | comparison | revisions
ucx/utils.c file | annotate | diff | comparison | revisions
--- a/dav/Makefile	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/Makefile	Fri Apr 21 21:25:32 2023 +0200
@@ -67,14 +67,14 @@
 
 all: ../build/bin/dav ../build/bin/dav-sync ../build/bin/xattrtool
 
-$(DAV_BIN): $(DAV_OBJ) ../build/libidav$(LIB_EXT)
+$(DAV_BIN): $(DAV_OBJ) ../build/lib/libidav$(LIB_EXT)
 	$(LD) -o $(DAV_BIN) $(DAV_OBJ) \
-		../build/libidav$(LIB_EXT) ../build/libucx$(LIB_EXT)  \
+		../build/lib/libidav$(LIB_EXT) ../build/lib/libucx$(LIB_EXT)  \
 		$(LDFLAGS) $(DAV_LDFLAGS)
 
-$(DAV_SYNC_BIN): $(SYNC_OBJ) ../build/libidav$(LIB_EXT)
+$(DAV_SYNC_BIN): $(SYNC_OBJ) ../build/lib/libidav$(LIB_EXT)
 	$(LD) -o $(DAV_SYNC_BIN) $(SYNC_OBJ) \
-		../build/libidav$(LIB_EXT) ../build/libucx$(LIB_EXT)  \
+		../build/lib/libidav$(LIB_EXT) ../build/lib/libucx$(LIB_EXT)  \
 		$(LDFLAGS) $(DAV_LDFLAGS)
 
 $(XATTRTOOL_BIN): $(XATTR_OBJ)
--- a/dav/assistant.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/assistant.c	Fri Apr 21 21:25:32 2023 +0200
@@ -28,16 +28,17 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
-#include <ucx/string.h>
-#include <ucx/utils.h>
+#include <cx/string.h>
+#include <cx/utils.h>
 
 #include <libidav/utils.h>
 
 #include "assistant.h"
 
-char* assistant_getcfg(char *cfgname) {
-    sstr_t line;
+char* assistant_getcfg(const char *cfgname) {
+    cxmutstr line;
     char *value = NULL;
     while(!value) {
         printf("%s: ", cfgname);
@@ -54,8 +55,8 @@
     return value;
 }
 
-char* assistant_getoptcfg(char *cfgname) {
-    sstr_t line;
+char* assistant_getoptcfg(const char *cfgname) {
+    cxmutstr line;
     char *value = NULL;
     while(!value) {
         printf("%s (optional): ", cfgname);
@@ -74,7 +75,7 @@
     return value;
 }
 
-char* assistant_gethiddenoptcfg(char *cfgname) {
+char* assistant_gethiddenoptcfg(const char *cfgname) {
     printf("%s (optional): ", cfgname);
     fflush(stdout);
     char *pw = util_password_input("");
@@ -86,8 +87,8 @@
 }
 
 
-char* assistant_getdefcfg(char *cfgname, char *defval) {
-    sstr_t line;
+char* assistant_getdefcfg(const char *cfgname, const char *defval) {
+    cxmutstr line;
     char *value = NULL;
     while(!value) {
         printf("%s (default: %s): ", cfgname, defval);
--- a/dav/assistant.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/assistant.h	Fri Apr 21 21:25:32 2023 +0200
@@ -37,23 +37,23 @@
  * ask for a configuration value with the name cfgname
  * which must be not empty
  */
-char* assistant_getcfg(char *cfgname);
+char* assistant_getcfg(const char *cfgname);
 
 /*
  * ask for an optional configuration value with the name cfgname
  */
-char* assistant_getoptcfg(char *cfgname);
+char* assistant_getoptcfg(const char *cfgname);
 
 /*
  * ask for an optional configuration value with the name cfgname
  * with hidden input
  */
-char* assistant_gethiddenoptcfg(char *cfgname);
+char* assistant_gethiddenoptcfg(const char *cfgname);
 
 /*
  * ask for an configuration value with the default value defval
  */
-char* assistant_getdefcfg(char *cfgname, char *defval);
+char* assistant_getdefcfg(const char *cfgname, const char *defval);
 
 #ifdef __cplusplus
 }
--- a/dav/config.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/config.c	Fri Apr 21 21:25:32 2023 +0200
@@ -30,7 +30,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/types.h>
-#include <ucx/map.h>
+#include <cx/hash_map.h>
 #include <errno.h>
 #include <libxml/tree.h>
 
@@ -61,8 +61,8 @@
 #define ENV_HOME getenv("HOME")
 #endif /* _WIN32 */
 
-static UcxMap *repos;
-static UcxMap *keys;
+static CxMap *repos;
+static CxMap *keys;
 
 static PwdStore *pstore;
 
@@ -104,8 +104,8 @@
 int load_config(DavContext *ctx) {
     context = ctx;
     // TODO: free the config somewhere
-    repos = ucx_map_new(16);
-    keys = ucx_map_new(16);
+    repos = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+    keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     
     char *pwfile = util_concat_path(ENV_HOME, ".dav/secrets.crypt");
     pstore = pwdstore_open(pwfile);
@@ -164,10 +164,9 @@
 
 void free_config(void) {
     if(repos) {
-        UcxMapIterator i = ucx_map_iterator(repos);
-        UcxKey k;
+        CxIterator i = cxMapIteratorValues(repos);
         Repository *repo;
-        UCX_MAP_FOREACH(k, repo, i) {
+        cx_foreach(Repository*, repo, i) {
             if(repo->default_key) {
                 free(repo->default_key);
             }
@@ -188,10 +187,10 @@
             }      
             free(repo);
         }
-        ucx_map_free(repos);
+        cxMapDestroy(repos);
     }
     if(keys) {
-        ucx_map_free(keys);
+        cxMapDestroy(keys);
     }
 }
 
@@ -348,7 +347,7 @@
         return 1;
     }
     
-    ucx_map_cstr_put(repos, repo->name, repo);
+    cxMapPut(repos, cx_hash_key_str(repo->name), repo);
     return 0;
 }
 
@@ -426,7 +425,7 @@
                 key->name = strdup(value);
             } else if(xstreq(node->name, "file")) {
                 // load key file
-                sstr_t key_data = load_key_file(value);
+                cxmutstr key_data = load_key_file(value);
                 if(key_data.length > 0) {
                     key->data = key_data.ptr;
                     key->length = key_data.length;
@@ -469,7 +468,7 @@
         
         // add key to context
         if(!error) {
-            ucx_map_cstr_put(keys, key->name, key);
+            cxMapPut(keys, cx_hash_key_str(key->name), key);
             dav_context_add_key(context, key);
         }
     }
@@ -486,8 +485,8 @@
     }
 }
 
-sstr_t load_key_file(char *filename) {
-    sstr_t k;
+cxmutstr load_key_file(const char *filename) {
+    cxmutstr k;
     k.ptr = NULL;
     k.length = 0;
     
@@ -597,11 +596,11 @@
     return error;
 }
 
-Repository* get_repository(sstr_t name) {
+Repository* get_repository(cxstring name) {
     if(!name.ptr) {
         return NULL;
     }
-    return ucx_map_sstr_get(repos, name);
+    return cxMapGet(repos, cx_hash_key(name.ptr, name.length));
 }
 
 int get_repository_flags(Repository *repo) {
@@ -628,11 +627,11 @@
 }
 
 
-Key* get_key(char *name) {
+Key* get_key(const char *name) {
     if(!name) {
         return NULL;
     }
-    return ucx_map_cstr_get(keys, name);
+    return cxMapGet(keys, cx_hash_key_str(name));
 }
 
 int add_repository(Repository *repo) {
@@ -753,17 +752,17 @@
         xmlNodePtr prev = matchedRepoNode->prev;
         xmlNodePtr next = matchedRepoNode->next;
         if(prev && prev->type == XML_TEXT_NODE) {
-            sstr_t content = sstr((char*)prev->content);
-            sstr_t lf = sstrrchr(content, '\n');
+            cxstring content = cx_str((char*)prev->content);
+            cxstring lf = cx_strrchr(content, '\n');
             if(lf.length > 0) {
-                *lf.ptr = '\0';
-                char* newcontent = sstrdup(content).ptr;
+                content.length = lf.ptr - content.ptr;
+                char* newcontent = cx_strdup(content).ptr;
                 xmlNodeSetContent(prev, (xmlChar*)newcontent);
                 free(newcontent);
             }
         }
         if(next && next->type == XML_TEXT_NODE) {
-            sstr_t lf = sstrchr(sstr((char*)next->content), '\n');
+            cxstring lf = cx_strchr(cx_str((char*)next->content), '\n');
             if(lf.length > 0) {
                 char* newcontent = malloc(lf.length);
                 memcpy(newcontent, lf.ptr+1, lf.length-1);
@@ -785,22 +784,16 @@
 }
 
 int list_repositories(void) {
-    UcxMapIterator i = ucx_map_iterator(repos);
+    CxIterator i = cxMapIteratorValues(repos);
     Repository *repo;
-    UCX_MAP_FOREACH(key, repo, i) {
+    cx_foreach(Repository *, repo, i) {
         printf("%s\n", repo->name);
     }
     return 0;
 }
 
-UcxList* get_repositories(void) {
-    UcxList *list = NULL;
-    UcxMapIterator i = ucx_map_iterator(repos);
-    Repository *repo;
-    UCX_MAP_FOREACH(key, repo, i) {
-        list = ucx_list_append(list, repo);
-    }
-    return list;
+CxIterator get_repositories(void) {
+    return cxMapIteratorValues(repos);
 }
 
 PwdStore* get_pwdstore(void) {
@@ -821,29 +814,29 @@
 
 
 
-Repository* url2repo_s(sstr_t url, char **path) {
+Repository* url2repo_s(cxstring url, char **path) {
     *path = NULL;
     
     int s;
-    if(sstrprefix(url, SC("http://"))) {
+    if(cx_strprefix(url, CX_STR("http://"))) {
         s = 7;
-    } else if(sstrprefix(url, SC("https://"))) {
+    } else if(cx_strprefix(url, CX_STR("https://"))) {
         s = 8;
     } else {
         s = 1;
     }
 
     // split URL into repository and path
-    sstr_t r = sstrsubs(url, s);
-    sstr_t p = sstrchr(r, '/');
-    r = sstrsubsl(url, 0, url.length-p.length);
+    cxstring r = cx_strsubs(url, s);
+    cxstring p = cx_strchr(r, '/');
+    r = cx_strsubsl(url, 0, url.length-p.length);
     if(p.length == 0) {
-        p = sstrn("/", 1);
+        p = cx_strn("/", 1);
     }
     
     Repository *repo = get_repository(r);
     if(repo) {
-        *path = sstrdup(p).ptr;
+        *path = cx_strdup(p).ptr;
     } else {
         // TODO: who is responsible for freeing this repository?
         // how can the callee know, if he has to call free()?
@@ -853,17 +846,17 @@
         repo->verification = true;
         repo->authmethods = CURLAUTH_BASIC;
         if(url.ptr[url.length-1] == '/') {
-            repo->url = sstrdup(url).ptr;
+            repo->url = cx_strdup(url).ptr;
             *path = strdup("/");
-        } else if (sstrchr(url, '/').length > 0) {
+        } else if (cx_strchr(url, '/').length > 0) {
             // TODO: fix the following workaround after
             //       fixing the inconsistent behavior of util_url_*()
             repo->url = util_url_base_s(url);
-            sstr_t truncated = sstrdup(url);
+            cxmutstr truncated = cx_strdup(url);
             *path = strdup(util_url_path(truncated.ptr));
             free(truncated.ptr);
         } else {
-            repo->url = sstrdup(url).ptr;
+            repo->url = cx_strdup(url).ptr;
             *path = strdup("/");
         }
     }
@@ -871,8 +864,8 @@
     return repo;
 }
 
-Repository* url2repo(char *url, char **path) {
-    return url2repo_s(sstr(url), path);
+Repository* url2repo(const char *url, char **path) {
+    return url2repo_s(cx_str(url), path);
 }
 
 static int decrypt_secrets(CmdArgs *a, PwdStore *secrets) {
@@ -882,7 +875,7 @@
     
     char *ps_password = NULL;
     if(secrets->unlock_cmd && strlen(secrets->unlock_cmd) > 0) {
-        UcxBuffer *cmd_out = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
+        CxBuffer *cmd_out = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
         if(!util_exec_command(secrets->unlock_cmd, cmd_out)) {
             // command successful, get first line from output without newline
             // and use that as password for the secretstore
@@ -899,7 +892,7 @@
                 ps_password[len] = 0;
             }
         }
-        ucx_buffer_free(cmd_out);
+        cxBufferFree(cmd_out);
     }
     
     if(!ps_password) {
@@ -977,47 +970,48 @@
      * The list secrets->location contains urls or repo names as
      * location strings. We need a list, that contains only urls
      */
-    UcxList *locations = NULL;
-    UCX_FOREACH(elm, secrets->locations) {
-        PwdIndexEntry *e = elm->data;
-        
-        UCX_FOREACH(loc, e->locations) {
+    CxList *locations = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)cmp_url_cred_entry, CX_STORE_POINTERS);
+    locations->simple_destructor = (cx_destructor_func)free_cred_location;
+    CxIterator i = cxListIterator(secrets->locations);
+    cx_foreach(PwdIndexEntry*, e, i) {
+        CxIterator entry_iter = cxListIterator(e->locations);
+        cx_foreach(char *, loc, entry_iter) {
             char *path;
-            Repository *r = url2repo(loc->data, &path);
+            Repository *r = url2repo(loc, &path);
             CredLocation *urlentry = calloc(1, sizeof(CredLocation));
             urlentry->id = e->id;
             urlentry->location = util_concat_path(r->url, path);
-            locations = ucx_list_append(locations, urlentry);
+            cxListAdd(locations, urlentry);
         }
     }
     // the list must be sorted
-    locations = ucx_list_sort(locations, (cmp_func)cmp_url_cred_entry, NULL);
+    cxListSort(locations);
     
     // create full request url string and remove protocol prefix
-    sstr_t req_url_proto = sstr(util_concat_path(repo->url, path));
-    sstr_t req_url = req_url_proto;
-    if(sstrprefix(req_url, S("http://"))) {
-        req_url = sstrsubs(req_url, 7);
-    } else if(sstrprefix(req_url, S("https://"))) {
-        req_url = sstrsubs(req_url, 8);
+    cxmutstr req_url_proto = cx_mutstr(util_concat_path(repo->url, path));
+    cxstring req_url = cx_strcast(req_url_proto);
+    if(cx_strprefix(req_url, CX_STR("http://"))) {
+        req_url = cx_strsubs(req_url, 7);
+    } else if(cx_strprefix(req_url, CX_STR("https://"))) {
+        req_url = cx_strsubs(req_url, 8);
     }
     
     // iterate over sorted locations and check if a location is a prefix
     // of the requested url
     char *id = NULL;
     int ret = 0;
-    UCX_FOREACH(elm, locations) {
-        CredLocation *cred = elm->data;
-        sstr_t cred_url = sstr(cred->location);
+    i = cxListIterator(locations);
+    cx_foreach(CredLocation*, cred, i) {
+        cxstring cred_url = cx_str(cred->location);
         
         // remove protocol prefix
-        if(sstrprefix(cred_url, S("http://"))) {
-            cred_url = sstrsubs(cred_url, 7);
-        } else if(sstrprefix(cred_url, S("https://"))) {
-            cred_url = sstrsubs(cred_url, 8);
+        if(cx_strprefix(cred_url, CX_STR("http://"))) {
+            cred_url = cx_strsubs(cred_url, 7);
+        } else if(cx_strprefix(cred_url, CX_STR("https://"))) {
+            cred_url = cx_strsubs(cred_url, 8);
         }
         
-        if(sstrprefix(req_url, cred_url)) {
+        if(cx_strprefix(req_url, cred_url)) {
             id = cred->id;
             break;
         }
@@ -1035,8 +1029,7 @@
     }
     
     free(req_url_proto.ptr);
-    ucx_list_free_content(locations, (ucx_destructor)free_cred_location);
-    ucx_list_free(locations);
+    cxListDestroy(locations);
     
     return ret;
 }
--- a/dav/config.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/config.h	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #ifndef CONFIG_H
 #define	CONFIG_H
 
-#include <ucx/string.h>
+#include <cx/string.h>
 #include <stdbool.h>
 #include <libidav/webdav.h>
 #include "pwd.h"
@@ -81,28 +81,28 @@
 int load_repository(const xmlNode *reponode);
 int load_key(const xmlNode *keynode);
 int load_proxy(DavProxy*, const xmlNode *proxynode, int type);
-sstr_t load_key_file(char *filename);
+cxmutstr load_key_file(const char *filename);
 int load_namespace(const xmlNode *node);
 int load_secretstore(const xmlNode *node);
 
 Repository* repository_new(void);
 
-Repository* get_repository(sstr_t name);
+Repository* get_repository(cxstring name);
 int get_repository_flags(Repository *repo);
 DavSession *repository_session(Repository *repo);
-Key* get_key(char *name);
+Key* get_key(const char *name);
 
 int add_repository(Repository *repo);
 int remove_repository(Repository *repo);
 int list_repositories(void);
-UcxList* get_repositories(void);
+CxIterator get_repositories(void);
 
 PwdStore* get_pwdstore(void);
 int pwdstore_save(PwdStore *pwdstore);
 
 
-Repository* url2repo_s(sstr_t url, char **path);
-Repository* url2repo(char *url, char **path);
+Repository* url2repo_s(cxstring url, char **path);
+Repository* url2repo(const char *url, char **path);
 
 DavSession* connect_to_repo(DavContext *ctx, Repository *repo, char *path, dav_auth_func authfunc, CmdArgs *a);
 
--- a/dav/db.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/db.c	Fri Apr 21 21:25:32 2023 +0200
@@ -33,7 +33,7 @@
 
 #include "db.h"
 
-#include <ucx/utils.h>
+#include <cx/utils.h>
 
 #include <libidav/utils.h>
 
@@ -55,8 +55,11 @@
     free(dav_dir);
     
     SyncDatabase *db = malloc(sizeof(SyncDatabase));
-    db->resources = ucx_map_new(2048);
-    db->conflict = ucx_map_new(16);
+    db->resources = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 2048);
+    db->conflict = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+    
+    db->resources->destructor_data = (cx_destructor_func)local_resource_free;
+    db->conflict->destructor_data = (cx_destructor_func)local_resource_free;
     
     xmlTextReaderPtr reader = xmlReaderForFile(db_file, NULL, 0);
     if(!reader) {
@@ -82,7 +85,7 @@
             if(xstreq(name, "resource")) {
                 LocalResource *res = process_resource(reader);
                 if(res) {
-                    ucx_map_cstr_put(db->resources, res->path, res);
+                    cxMapPut(db->resources, cx_hash_key_str(res->path), res);
                 } else {
                     error = 1;
                     break;
@@ -90,7 +93,7 @@
             } else if(xstreq(name, "conflict")) {
                 LocalResource *res = process_conflict(reader);
                 if(res) {
-                    ucx_map_cstr_put(db->conflict, res->path, res);
+                    cxMapPut(db->conflict, cx_hash_key_str(res->path), res);
                 } else {
                     error = 1;
                     break;
@@ -109,7 +112,10 @@
 }
 
 void process_parts(xmlTextReaderPtr reader, LocalResource *res) {
-    UcxList *parts = NULL;
+    // TODO: rewrite using low level array
+    
+    CxList *parts = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    parts->destructor_data = (cx_destructor_func)filepart_free;
     
     FilePart *current_part = NULL;
     
@@ -121,12 +127,11 @@
         const xmlChar *name = xmlTextReaderConstName(reader);
         int depth = xmlTextReaderDepth(reader);
         
-        int part = TRUE;
         if(type == XML_READER_TYPE_ELEMENT) {
             if(depth == 3 && xstreq(name, "part")) {
                 current_part = calloc(1, sizeof(FilePart));
                 current_part->block = count;
-                parts = ucx_list_append(parts, current_part);
+                cxListAdd(parts, current_part);
                 count++;
             } else if(depth == 4) {
                 if(xstreq(name, "hash")) {
@@ -159,25 +164,21 @@
         }
     }
     
-    if(err) {
-        ucx_list_free_content(parts, (ucx_destructor)filepart_free);
-        ucx_list_free(parts);
-        return;
-    }
+    if(!err) {
+        FilePart *file_parts = calloc(count, sizeof(FilePart));
+        size_t i = 0;
+        CxIterator iter = cxListIterator(parts);
+        cx_foreach(FilePart*, p, iter) {
+            file_parts[i] = *p;
+            free(p);
+            i++;
+        }
+        
+        res->parts = file_parts;
+        res->numparts = count;
+    } 
     
-    FilePart *file_parts = calloc(count, sizeof(FilePart));
-    size_t i = 0;
-    UCX_FOREACH(elm, parts) {
-        FilePart *p = elm->data;
-        file_parts[i] = *p;
-        free(p);
-        i++;
-    }
-    
-    ucx_list_free(parts);
-    
-    res->parts = file_parts;
-    res->numparts = count;
+    cxListDestroy(parts);
 }
 
 LocalResource* process_resource(xmlTextReaderPtr reader) {
@@ -410,9 +411,9 @@
     xmlTextWriterStartElement(writer, BAD_CAST "directory");
     
     // write all resource entries
-    UcxMapIterator i = ucx_map_iterator(db->resources);
+    CxIterator i = cxMapIteratorValues(db->resources);
     LocalResource *res;
-    UCX_MAP_FOREACH(key, res, i) {
+    cx_foreach(LocalResource*, res, i) {
         // <resource>
         xmlTextWriterStartElement(writer, BAD_CAST "resource");
         
@@ -687,8 +688,8 @@
 */
     
     // write all conflict entries
-    i = ucx_map_iterator(db->conflict);
-    UCX_MAP_FOREACH(key, res, i) {
+    i = cxMapIteratorValues(db->conflict);
+    cx_foreach(LocalResource*, res, i) {
         // <conflict>
         xmlTextWriterStartElement(writer, BAD_CAST "conflict");
         
@@ -720,10 +721,8 @@
 }
 
 void destroy_db(SyncDatabase *db) {
-    ucx_map_free_content(db->resources, (ucx_destructor)local_resource_free);
-    ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free);
-    ucx_map_free(db->resources);
-    ucx_map_free(db->conflict);
+    cxMapDestroy(db->resources);
+    cxMapDestroy(db->conflict);
     free(db);
 }
 
@@ -741,7 +740,7 @@
         free(res->etag);
     }
     if(res->cached_tags) {
-        ucx_buffer_free(res->cached_tags);
+        cxBufferFree(res->cached_tags);
     }
     if(res->tags_hash) {
         free(res->tags_hash);
@@ -790,10 +789,10 @@
         xattr->hash = nullstrdup(src->xattr->hash);
         xattr->nattr = src->xattr->nattr;
         xattr->names = calloc(xattr->nattr, sizeof(char*));
-        xattr->values = calloc(xattr->nattr, sizeof(sstr_t));
+        xattr->values = calloc(xattr->nattr, sizeof(cxmutstr));
         for(int i=0;i<xattr->nattr;i++) {
             xattr->names[i] = strdup(src->xattr->names[i]);
-            xattr->values[i] = sstrdup(src->xattr->values[i]);
+            xattr->values[i] = cx_strdup(cx_strcast(src->xattr->values[i]));
         }
         newres->xattr = xattr;
     }
@@ -825,15 +824,14 @@
     free(part);
 }
 
-UcxMap* create_hash_index(SyncDatabase *db) {
-    UcxMap *hmap = ucx_map_new(db->resources->count + 64);
+CxMap* create_hash_index(SyncDatabase *db) {
+    CxMap *hmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, db->resources->size + 64);
     
-    UcxMapIterator i = ucx_map_iterator(db->resources);
-    UcxKey key;
+    CxIterator i = cxMapIteratorValues(db->resources);
     LocalResource *res;
-    UCX_MAP_FOREACH(key, res, i) {
+    cx_foreach(LocalResource*, res, i) {
         if(res->hash) {
-            ucx_map_cstr_put(hmap, res->hash, res);
+            cxMapPut(hmap, cx_hash_key_str(res->hash), res);
         }
     }
     
--- a/dav/db.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/db.h	Fri Apr 21 21:25:32 2023 +0200
@@ -31,8 +31,9 @@
 
 #include <inttypes.h>
 #include <libidav/webdav.h>
-#include <ucx/map.h>
-#include <ucx/buffer.h>
+#include <cx/hash_map.h>
+#include <cx/linked_list.h>
+#include <cx/buffer.h>
 #include <time.h>
 
 #include <libxml/xmlreader.h>
@@ -62,7 +63,7 @@
     off_t   size;
     DavBool isdirectory;
     DavBool skipped;
-    UcxBuffer *cached_tags;
+    CxBuffer *cached_tags;
     XAttributes *xattr;
     char *tags_hash;
     char *xattr_hash;
@@ -103,8 +104,8 @@
 };
 
 struct SyncDatabase {
-    UcxMap *resources;
-    UcxMap *conflict;
+    CxMap *resources;
+    CxMap *conflict;
 };
 
 SyncDatabase* load_db(char *name);
@@ -119,7 +120,7 @@
 
 void filepart_free(FilePart *part);
 
-UcxMap* create_hash_index(SyncDatabase *db);
+CxMap* create_hash_index(SyncDatabase *db);
 
 LocalResource* process_resource(xmlTextReaderPtr reader);
 LocalResource* process_conflict(xmlTextReaderPtr reader);
--- a/dav/error.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/error.c	Fri Apr 21 21:25:32 2023 +0200
@@ -33,7 +33,7 @@
 
 #include "error.h"
 
-void print_resource_error(DavSession *sn, char *path) {
+void print_resource_error(DavSession *sn, const char *path) {
     char *res_url = util_concat_path(sn->base_url, path);
     switch(sn->error) {
         default: {
--- a/dav/error.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/error.h	Fri Apr 21 21:25:32 2023 +0200
@@ -35,7 +35,7 @@
 extern "C" {
 #endif
 
-void print_resource_error(DavSession *sn, char *path);
+void print_resource_error(DavSession *sn, const char *path);
 
 
 #ifdef __cplusplus
--- a/dav/finfo.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/finfo.c	Fri Apr 21 21:25:32 2023 +0200
@@ -34,45 +34,42 @@
 #include <unistd.h>
 #include <errno.h>
 #include <sys/stat.h>
+#include <limits.h>
 
-#include <ucx/string.h>
-#include <ucx/list.h>
+#include <cx/string.h>
+#include <cx/list.h>
+#include <cx/array_list.h>
 #include <libidav/crypto.h>
 #include <libidav/utils.h>
 
 #include "libxattr.h"
 
 uint32_t parse_finfo_settings(const char *str, char **error) {
-    scstr_t s = scstr(str);
+    cxstring s = cx_str(str);
+    
+    if(!cx_strcmp(s, CX_STR("*")) || !cx_strcmp(s, CX_STR("a")) || !cx_strcmp(s, CX_STR("all"))) {
+        return FINFO_MTIME|FINFO_OWNER|FINFO_MODE|FINFO_XATTR;
+    } 
     
-    if(!sstrcmp(s, SC("*")) || !sstrcmp(s, SC("a")) || !sstrcmp(s, SC("all"))) {
-        return FINFO_MTIME|FINFO_OWNER|FINFO_MODE|FINFO_XATTR;
+    CxStrtokCtx fs = cx_strtok(s, CX_STR(","), INT_MAX);
+    cxstring f;
+    uint32_t finfo = 0;
+    char *err = NULL;
+    while(cx_strtok_next(&fs, &f)) {
+        if(!cx_strcasecmp(f, CX_STR("mtime"))) {
+            finfo |= FINFO_MTIME;
+        } else if(!cx_strcasecmp(f, CX_STR("owner"))) {
+            finfo |= FINFO_OWNER;
+        } else if(!cx_strcasecmp(f, CX_STR("mode"))) {
+            finfo |= FINFO_MODE;
+        } else if(!cx_strcasecmp(f, CX_STR("xattr"))) {
+            finfo |= FINFO_XATTR;
+        } else if(error && !err) {
+            err = cx_strdup(f).ptr;
+            continue;
+        }
     }
     
-    ssize_t count = 0;
-    sstr_t *fs = sstrsplit(s, SC(","), &count);
-    
-    char *err = NULL;
-    
-    uint32_t finfo = 0;
-    for(int i=0;i<count;i++) {
-        sstr_t f = fs[i];
-        if(!sstrcasecmp(f, SC("mtime"))) {
-            finfo |= FINFO_MTIME;
-        } else if(!sstrcasecmp(f, SC("owner"))) {
-            finfo |= FINFO_OWNER;
-        } else if(!sstrcasecmp(f, SC("mode"))) {
-            finfo |= FINFO_MODE;
-        } else if(!sstrcasecmp(f, SC("xattr"))) {
-            finfo |= FINFO_XATTR;
-        } else if(error && !err) {
-            err = fs[i].ptr;
-            continue;
-        }
-        free(f.ptr);
-    }
-    
-    free(fs);
     return err ? 0 : finfo;
 }
 
@@ -129,10 +126,26 @@
     return 0;
 }
 
+
+static void* array_realloc(void *array,
+            size_t capacity,
+            size_t elem_size,
+            struct cx_array_reallocator_s *alloc)
+{
+    return realloc(array, capacity * elem_size);
+}
+
 XAttributes* xml_get_attributes(DavXmlNode *xml) {
-    UcxList *names = NULL;
-    UcxList *values = NULL;
+    XAttributes *attributes = calloc(1, sizeof(XAttributes));
+    size_t x_names_size = 0;
+    size_t x_names_alloc = 8;
+    size_t x_values_size = 0;
+    size_t x_values_alloc = 8;
+    attributes->names = calloc(x_names_alloc, sizeof(char*));
+    attributes->values = calloc(x_values_alloc, sizeof(cxmutstr));
     
+    struct cx_array_reallocator_s re = { .realloc = array_realloc };
+     
     size_t count = 0;
     
     char *hash = NULL;
@@ -145,7 +158,16 @@
             } else if(!strcmp(node->name, "xattr")) {
                 char *xattr_name = dav_xml_get_attr(node, "name");
                 if(xattr_name) {                 
-                    names = ucx_list_append(names, strdup(xattr_name));
+                    char *xname = strdup(xattr_name);
+                    cx_array_copy(
+                            (void**)&attributes->names,
+                            &x_names_size,
+                            &x_names_alloc,
+                            count,
+                            &xname,
+                            sizeof(void*),
+                            1,
+                            &re);
                     
                     char *text = dav_xml_getstring(node->children);
                     if(!text) {
@@ -155,11 +177,19 @@
                     int len = 0;
                     char *val = util_base64decode_len(text, &len);
                     
-                    sstr_t *value = malloc(sizeof(sstr_t));
-                    value->ptr = val;
-                    value->length = len;
+                    cxmutstr value;
+                    value.ptr = val;
+                    value.length = len;
                     
-                    values = ucx_list_append(values, value);
+                    cx_array_copy(
+                            (void**)&attributes->values,
+                            &x_values_size,
+                            &x_values_alloc,
+                            count,
+                            &value,
+                            sizeof(cxmutstr),
+                            1,
+                            &re);
                     
                     count++;
                 }
@@ -167,24 +197,16 @@
         }
     }
     
-    XAttributes *attributes = NULL;
-    if(count > 0) {
-        attributes = calloc(1, sizeof(XAttributes));
-        attributes->hash = hash ? strdup(hash) : NULL;
-        attributes->nattr = count;
-        attributes->names = calloc(count, sizeof(char*));
-        attributes->values = calloc(count, sizeof(sstr_t));
-        int i=0;
-        UCX_FOREACH(elm, names) {
-            attributes->names[i] = elm->data;
-            i++;
-        }
-        i=0;
-        UCX_FOREACH(elm, values) {
-            attributes->values[i] = *(sstr_t*)elm->data;
-            i++;
-        }
-    }
+    if(count == 0) {
+        free(attributes->names);
+        free(attributes->values);
+        free(attributes);
+        return NULL;
+    } 
+    
+    attributes->hash = hash ? strdup(hash) : NULL;
+    attributes->nattr = count;
+
     return attributes;
 }
 
@@ -202,7 +224,7 @@
     XAttributes *xattr = malloc(sizeof(XAttributes));
     xattr->nattr = 0;
     xattr->names = calloc(nelm, sizeof(char*));
-    xattr->values = calloc(nelm, sizeof(sstr_t));
+    xattr->values = calloc(nelm, sizeof(cxmutstr));
     
     DAV_SHA_CTX *sha256 = dav_hash_init();
     
@@ -223,7 +245,7 @@
             dav_hash_update(sha256, value, valuelen);
             // add name and value
             xattr->names[nattr] = attributes[i];
-            sstr_t v;
+            cxmutstr v;
             v.ptr = value;
             v.length = valuelen;
             xattr->values[nattr] = v;
@@ -264,7 +286,7 @@
         last->next = attr;
         last = attr;
         
-        sstr_t value = xattr->values[i];
+        cxmutstr value = xattr->values[i];
         if(value.length > 0) {
             char *encval = util_base64encode(value.ptr, value.length);
             attr->children = dav_xml_createtextnode(encval);
--- a/dav/finfo.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/finfo.h	Fri Apr 21 21:25:32 2023 +0200
@@ -33,6 +33,8 @@
 #include <inttypes.h>
 #include <sys/stat.h>
 
+#include <cx/string.h>
+
 #include "system.h"
 
 #ifdef __cplusplus
@@ -45,21 +47,21 @@
 #define FINFO_XATTR 8
     
 typedef struct XAttributes {
-    size_t nattr;
-    char   **names;
-    sstr_t *values;
-    char   *hash;
+    size_t   nattr;
+    char     **names;
+    cxmutstr *values;
+    char     *hash;
 } XAttributes;
 
 typedef struct FileInfo {
-    time_t  last_modified;
-    mode_t  mode;
-    uid_t   uid;
-    gid_t   gid;
-    DavBool date_set;
-    DavBool mode_set;
-    DavBool uid_set;
-    DavBool gid_set;
+    time_t   last_modified;
+    mode_t   mode;
+    uid_t    uid;
+    gid_t    gid;
+    DavBool  date_set;
+    DavBool  mode_set;
+    DavBool  uid_set;
+    DavBool  gid_set;
 } FileInfo;
 
 typedef int(*xattr_filter_func)(const char*,void*);
--- a/dav/main.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/main.c	Fri Apr 21 21:25:32 2023 +0200
@@ -38,8 +38,11 @@
 #ifndef _WIN32
 #include <sys/wait.h>
 #endif
-#include <ucx/string.h>
-#include <ucx/utils.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
+#include <cx/hash_map.h>
+#include <cx/linked_list.h>
 #include <dirent.h>
 
 #include <libidav/utils.h>
@@ -160,7 +163,7 @@
         } else if(!strcasecmp(cmd, "get")) {
             ret = cmd_get(args, FALSE);
         } else if(!strcasecmp(cmd, "cat")) {
-            ucx_map_cstr_put(args->options, "output", "-");
+            cxMapPut(args->options, cx_hash_key_str("output"), "-");
             ret = cmd_get(args, FALSE);
         } else if(!strcasecmp(cmd, "edit")) {
             ret = cmd_edit(args);
@@ -274,14 +277,14 @@
 };
 
 char* find_usage_str(const char *cmd) {
-    scstr_t c = scstr(cmd);
+    cxstring c = cx_str(cmd);
     for(int i=0;;i++) {
         char *str = cmdusageinfo[i];
         if(!str) {
             break;
         }
-        scstr_t u = scstr(str);
-        if(sstrprefix(u, c)) {
+        cxstring u = cx_str(str);
+        if(cx_strprefix(u, c)) {
             return str;
         }
     }
@@ -591,7 +594,7 @@
         return -1;
     }
     if(export) {
-        ucx_map_cstr_put(a->options, "recursive", "");
+        cxMapPut(a->options, cx_hash_key_str("recursive"), "");
     }
     
     char *url = a->argv[0];
@@ -694,7 +697,8 @@
     }
     
     // get list of resources
-    UcxList *reslist = NULL;
+    CxList *reslist = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    reslist->simple_destructor = (cx_destructor_func)free_getres;
     uint64_t totalsize = 0;
     uint64_t rescount = 0;
     
@@ -705,10 +709,11 @@
     char *structure = cmd_getoption(a, "structure");
     
     // iterate over resource tree
-    UcxList *stack = ucx_list_prepend(NULL, getres);
-    while(stack) {
-        GetResource *g = stack->data;
-        stack = ucx_list_remove(stack, stack);
+    CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    cxListInsert(stack, 0, getres);
+    while(stack->size > 0) {
+        GetResource *g = cxListAt(stack, 0);
+        cxListRemove(stack, 0);
 
         if(g->res->iscollection) {
             DavResource *child = g->res->children;
@@ -720,7 +725,7 @@
                 newres->path = pathlen > 0 ?
                     util_concat_path(g->path, child->name) : strdup(child->name);
 
-                stack = ucx_list_prepend(stack, newres);
+                cxListInsert(stack, 0, newres);
                 
                 child = child->next;
             }
@@ -737,9 +742,10 @@
         if(strlen(g->path) == 0) {
             free_getres(g);
         } else {
-            reslist = ucx_list_append(reslist, g);
+            cxListAdd(reslist, g);
         }
     }
+    cxListDestroy(stack);
     
     // download resources
     pdata.total = totalsize;
@@ -758,9 +764,8 @@
     } else {
         get = get_resource;
     }
-    UCX_FOREACH(elm, reslist) {
-        GetResource *getres = elm->data;
-        
+    CxIterator i = cxListIterator(reslist);
+    cx_foreach(GetResource *, getres, i) {
         ret = get(repo, getres, a, tout);
         if(ret) {
             break;
@@ -774,8 +779,7 @@
         }
     }
     
-    ucx_list_free_content(reslist, free_getres);
-    ucx_list_free(reslist);
+    cxListDestroy(reslist);
     free(path);
     
     if(pdata.out && !pdata.isstdout) {
@@ -1135,7 +1139,7 @@
     }
     
     if(import) {
-        ucx_map_cstr_put(a->options, "resursive", "");
+        cxMapPut(a->options, cx_hash_key_str("resursive"), "");
     }
     
     char *url = a->argv[0];
@@ -1158,7 +1162,7 @@
     if(a->argc > 2) {
         printfile = TRUE;
         ignoredirerr = TRUE;
-    } else if(ucx_map_cstr_get(a->options, "recursive")) {
+    } else if(cmd_getoption(a, "recursive")) {
         printfile = TRUE;
     }
     
@@ -1289,7 +1293,7 @@
             fprintf(stderr, "cannot open input file\n");
             return -1;
         }
-        char *filename = util_resource_name(file);
+        const char *filename = util_resource_name(file);
         //path = util_concat_path(path, filename);
         ret = put_file(repo, a, sn, path, filename, finfo, file, in, s.st_size);
         //free(path);
@@ -1379,8 +1383,8 @@
         Repository *repo,
         CmdArgs *a,
         DavSession *sn,
-        char *path,
-        char *name,
+        const char *path,
+        const char *name,
         uint32_t finfo,
         const char *fpath,
         FILE *in,
@@ -1695,11 +1699,10 @@
     char **date_str = (char**)data;
     
     //printf("header: %.*s\n", s*n, header);
-    sstr_t h = sstrn(header, s*n);
-    if(sstrprefix(h, S("Date:"))) {
-        sstr_t v = sstrsubs(h, 5);
-        v = sstrdup(sstrtrim(v));
-        *date_str = v.ptr;
+    cxstring h = cx_strn(header, s*n);
+    if(cx_strprefix(h, CX_STR("Date:"))) {
+        cxstring v = cx_strsubs(h, 5);
+        *date_str = cx_strdup(cx_strtrim(v)).ptr;
     }
     return s*n;
 }
@@ -1926,7 +1929,7 @@
     char *path = NULL;
     Repository *repo = url2repo(url, &path);
     DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
-    ucx_mempool_reg_destr(sn->mp, path, free);
+    util_regdestr(sn->mp, path, free);
     
     if(set_session_config(sn, a)) {
         return -1;
@@ -1935,7 +1938,7 @@
     time_t timeout = 0;
     char *timeoutstr = cmd_getoption(a, "timeout");
     if(timeoutstr) {
-        if(!sstrcasecmp(sstr(timeoutstr), S("infinite"))) {
+        if(!cx_strcasecmp(cx_str(timeoutstr), CX_STR("infinite"))) {
             timeout = -1;
         } else {
             uint64_t i;
@@ -1970,20 +1973,21 @@
 }
 
 static char* read_line() {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     int c;
     while((c = getchar()) != EOF) {
         if(c == '\n') {
             break;
         }
-        ucx_buffer_putc(buf, c);
+        cxBufferPut(&buf, c);
     } 
     char *str = NULL;
-    sstr_t line = sstrtrim(sstrn(buf->space, buf->size));
+    cxstring line = cx_strtrim(cx_strn(buf.space, buf.size));
     if(line.length != 0) {
-        str = sstrdup(line).ptr;
+        str = cx_strdup(line).ptr;
     }
-    ucx_buffer_free(buf);
+    cxBufferDestroy(&buf);
     return str;
 }
 
@@ -1998,7 +2002,7 @@
     char *path = NULL;
     Repository *repo = url2repo(url, &path);
     DavSession *sn = connect_to_repo(ctx, repo, path, request_auth, a);
-    ucx_mempool_reg_destr(sn->mp, path, free);
+    util_regdestr(sn->mp, path, free);
     if(set_session_config(sn, a)) {
         return -1;
     }
@@ -2109,7 +2113,7 @@
 
             DavXmlNode *xval = dav_get_property_ns(res, p.ns, p.name);
             if(dav_xml_isstring(xval)) {
-                sstr_t value = sstr(dav_xml_getstring(xval));
+                cxstring value = cx_str(dav_xml_getstring(xval));
                 printf("  %s: %.*s\n", p.name, (int)value.length, value.ptr);
             } else {
                 // find some xml elements
@@ -2319,26 +2323,25 @@
 
 
 char* stdin2str() {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
-    size_t size = ucx_stream_copy(stdin, buf, fread, ucx_buffer_write);
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    size_t size = cx_stream_copy(stdin, &buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
     if(size == 0) {
-        ucx_buffer_free(buf);
+        cxBufferDestroy(&buf);
         return NULL;
     } else {
-        ucx_buffer_putc(buf, '\0');
-        char *str = buf->space;
-        free(buf);
-        return str;
+        cxBufferPut(&buf, '\0');
+        return buf.space;
     }
 }
 
-static void xml2str_i(DavXmlNode *node, UcxBuffer *buf, int indent) {
+static void xml2str_i(DavXmlNode *node, CxBuffer *buf, int indent) {
     while(node) {
         if(node->type == DAV_XML_ELEMENT) {
             if(node->children) {
                 if(dav_xml_isstring(node->children)) {
-                    sstr_t s = sstrtrim(sstr(dav_xml_getstring(node->children)));
-                    ucx_bprintf(
+                    cxstring s = cx_strtrim(cx_str(dav_xml_getstring(node->children)));
+                    cx_bprintf(
                             buf,
                             "%*s<%s>%.*s</%s>\n",
                             indent,
@@ -2348,18 +2351,18 @@
                             s.ptr,
                             node->name);
                 } else {
-                    ucx_bprintf(buf, "%*s<%s>\n", indent, "", node->name);
+                    cx_bprintf(buf, "%*s<%s>\n", indent, "", node->name);
                     xml2str_i(node->children, buf, indent+2);
-                    ucx_bprintf(buf, "%*s</%s>\n", indent, "", node->name);
+                    cx_bprintf(buf, "%*s</%s>\n", indent, "", node->name);
                 }
             } else {
-                ucx_bprintf(buf, "%*s<%s />", indent, "", node->name);
-                ucx_buffer_putc(buf, '\n');
+                cx_bprintf(buf, "%*s<%s />", indent, "", node->name);
+                cxBufferPut(buf, '\n');
             }
         } else if(node->type == DAV_XML_TEXT) {
-            sstr_t val = sstrtrim(sstrn(node->content, node->contentlength));
+            cxstring val = cx_strtrim(cx_strn(node->content, node->contentlength));
             if(val.length > 0) {
-                ucx_bprintf(buf, "%*.*s", indent, (int)val.length, val.ptr);
+                cx_bprintf(buf, "%*.*s", indent, (int)val.length, val.ptr);
             }
         }
         
@@ -2368,30 +2371,28 @@
 }
 
 char* xml2str(DavXmlNode *node) {
-    char *str = malloc(256);
-    UcxBuffer *buf = ucx_buffer_new(str, 256, UCX_BUFFER_AUTOEXTEND);
-    xml2str_i(node, buf, 0);
-    ucx_buffer_putc(buf, 0);
-    char *space = buf->space;
-    ucx_buffer_free(buf);
-    return space;
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 256, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
+    xml2str_i(node, &buf, 0);
+    cxBufferPut(&buf, 0);
+    return buf.space;
 }
 
 void printxmldoc(FILE *out, char *root, char *rootns, DavXmlNode *content) {
-    UcxMap *nsmap = ucx_map_new(16);
+    CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+    nsmap->simple_destructor = free;
     
-    ucx_map_cstr_put(nsmap, rootns, "x0");
+    cxMapPut(nsmap, cx_hash_key_str(rootns), "x0");
     fprintf(out, "%s", "<?xml version=\"1.0\"?>\n");
     fprintf(out, "<x0:%s xmlns:x0=\"%s\">", root, rootns);
     
-    dav_print_node(out, (write_func)fwrite, nsmap, content);
+    dav_print_node(out, (cx_write_func)fwrite, nsmap, content);
     
     fprintf(out, "</x0:%s>\n", root);
     
     // cleanup namespace map
-    ucx_map_cstr_remove(nsmap, rootns);
-    ucx_map_free_content(nsmap, free);
-    ucx_map_free(nsmap);
+    cxMapRemove(nsmap, cx_hash_key_str(rootns));
+    cxMapDestroy(nsmap);
 }
 
 
@@ -2404,7 +2405,7 @@
         fprintf(stderr, "Abort\n");
         return -1;
     }
-    if(get_repository(sstr(name))) {
+    if(get_repository(cx_str(name))) {
         fprintf(stderr, "Repository %s already exists.\nAbort\n", name);
         return -1;
     }
@@ -2460,7 +2461,7 @@
     }
     
     for(int i = 0 ; i < args->argc ; i++) {
-        sstr_t reponame = sstr(args->argv[i]);
+        cxstring reponame = cx_str(args->argv[i]);
         Repository* repo = get_repository(reponame);
         if(repo) {
             if(remove_repository(repo)) {
@@ -2484,16 +2485,16 @@
         return -1;
     }
     
-    sstr_t reponame = sstr(args->argv[0]);
+    cxstring reponame = cx_str(args->argv[0]);
     Repository* repo = get_repository(reponame);
     if(repo) {
-        sstr_t url = sstr(repo->url);
+        cxstring url = cx_str(repo->url);
         if(repo->user && !cmd_getoption(args, "plain")) {
             int hostindex = 0;
-            if(sstrprefix(url, S("https://"))) {
+            if(cx_strprefix(url, CX_STR("https://"))) {
                 printf("https://");
                 hostindex = 8;
-            } else if(sstrprefix(url, S("http://"))) {
+            } else if(cx_strprefix(url, CX_STR("http://"))) {
                 printf("http://");
                 hostindex = 7;
             }
@@ -2616,9 +2617,10 @@
     
     // optionally, get one or more locations
     char *location = NULL;
-    UcxList *locations = NULL;
+    CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    locations->simple_destructor = free;
     while((location = assistant_getoptcfg("Location"))) {
-        locations = ucx_list_append(locations, location);
+        cxListAdd(locations, location);
     }
     
     int ret = 1;
@@ -2635,8 +2637,7 @@
     if(user) free(user);
     if(password) free(password);
     
-    ucx_list_free_content(locations, free);
-    ucx_list_free(locations);
+    cxListDestroy(locations);
     
     return ret;
 }
@@ -2649,7 +2650,7 @@
  * called before the secret store is decrypted
  */
 static int cmd_ss_list_users_bc(CmdArgs *Args, PwdStore *secrets, int *ret) {
-    if(secrets->index->count == 0) {
+    if(secrets->index->size == 0) {
         return 1; // abort, because the secret store is empty
     }
     // set ret to 1, because decrypt could fail and this should be an error
@@ -2663,24 +2664,28 @@
 static int cmd_ss_list_users(CmdArgs *args, PwdStore *secrets, int *ret) {
     *ret = 0;
     
-    UcxList *list = secrets->locations;
+    CxList *list = secrets->locations;
     for(int i=0;i<2;i++) {
-        UCX_FOREACH(elm, list) {
-            PwdIndexEntry *index = elm->data;
-            PwdEntry *e = ucx_map_cstr_get(secrets->ids, index->id);
-            if(e) {
-                printf("Id: %s\n", e->id);
-                printf("User: %s\n", e->user);
-                UCX_FOREACH(loc, index->locations) {
-                    char *location = loc->data;
-                    printf("Location: %s\n", location);
+        if(list) {
+            CxIterator i = cxListIterator(list);
+            cx_foreach(PwdIndexEntry*, index, i) {
+                PwdEntry *e = cxMapGet(secrets->ids, cx_hash_key_str(index->id));
+                if(e) {
+                    printf("Id: %s\n", e->id);
+                    printf("User: %s\n", e->user);
+                    if(index->locations) {
+                        CxIterator loc_iter = cxListIterator(index->locations);
+                        cx_foreach(char *, location, loc_iter) {
+                            printf("Location: %s\n", location);
+                        }
+                        printf("\n");
+                    }
+                } else {
+                    // broken index
+                    fprintf(stderr,
+                            "Warning: id '%s' not in secret store.\n",
+                            index->id);
                 }
-                printf("\n");
-            } else {
-                // broken index
-                fprintf(stderr,
-                        "Warning: id '%s' not in secret store.\n",
-                        index->id);
             }
         }
         list = secrets->noloc;
@@ -2729,28 +2734,32 @@
         return;
     }
     
-    PwdIndexEntry *index = ucx_map_cstr_get(secrets->index, id);
+    PwdIndexEntry *index = cxMapGet(secrets->index, cx_hash_key_str(id));
     if(!index) {
         return;
     }
     
     printf("Id: %s\n", entry->id);
     printf("User: %s\n", entry->user);
-    UCX_FOREACH(elm, index->locations) {
-        printf("Location: %s\n", (char*)elm->data);
+    if(index->locations) {
+        CxIterator loc_iter = cxListIterator(index->locations);
+        cx_foreach(char *, location, loc_iter) {
+            printf("Location: %s\n", location);
+        }
     }
 }
 
 static void secrets_remove_location(PwdIndexEntry *index) {
-    if(!index->locations) {
+    if(!index->locations || index->locations->size == 0) {
         printf("no locations\n");
         return;
     }
     
     printf("0: abort\n");
     int i = 1;
-    UCX_FOREACH(elm, index->locations) {
-        printf("%d: %s\n", i, (char*)elm->data);
+    CxIterator loc_iter = cxListIterator(index->locations);
+    cx_foreach(char *, location, loc_iter) {
+        printf("%d: %s\n", i, location);
         i++;
     }
     
@@ -2764,10 +2773,10 @@
         if(ln == 0) {
             return;
         } else {
-            UcxList *elm = ucx_list_get(index->locations, ln - 1);
-            if(elm) {
-                free(elm->data);
-                index->locations = ucx_list_remove(index->locations, elm);
+            char *location = cxListAt(index->locations, ln - 1);
+            if(location) {
+                free(location);
+                cxListRemove(index->locations, ln - 1);
             }
         }
     } else {
@@ -2783,7 +2792,7 @@
         return 1;
     }
     PwdEntry *entry = pwdstore_get(secrets, id);
-    PwdIndexEntry *index = ucx_map_cstr_get(secrets->index, id);
+    PwdIndexEntry *index = cxMapGet(secrets->index, cx_hash_key_str(id));
     if(!entry || !index) {
         fprintf(stderr, "Credentials with this id doesn't exist.\n");
         return 1;
@@ -2837,7 +2846,7 @@
                     // add location
                     char *location = assistant_getoptcfg("Location");
                     if(location) {
-                        index->locations = ucx_list_append(index->locations, location);
+                        cxListAdd(index->locations, location);
                     }
                     break;
                 }
@@ -2848,11 +2857,12 @@
                 }
                 case 4: {
                     // list locations
-                    if(!index->locations) {
+                    if(!index->locations || index->locations->size == 0) {
                         printf("no locations\n");
                     } else {
-                        UCX_FOREACH(elm, index->locations) {
-                            printf("Location: %s\n", (char*)elm->data);
+                        CxIterator i = cxListIterator(index->locations);
+                        cx_foreach(char *, location, i) {
+                            printf("Location: %s\n", location);
                         }
                     }
                     break;
@@ -2910,12 +2920,13 @@
 
 static char** read_args_from_stdin(int *argc) {
     // read stdin into buffer
-    UcxBuffer *in = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
-    ucx_stream_copy(stdin, in, (read_func)fread, (write_func)ucx_buffer_write);
+    CxBuffer *in = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cx_stream_copy(stdin, in, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
     
     // split input into lines
     ssize_t count = 0;
-    sstr_t *lines = scstrsplit(scstrn(in->space, in->pos), SC("\n"), &count);
+    cxmutstr *lines; 
+    count = cx_strsplit_ma(cxDefaultAllocator, cx_mutstrn(in->space, in->pos), CX_STR("\n"), INT_MAX, &lines);
     
     char **args = NULL;
     if(count > 0) {
@@ -2931,7 +2942,7 @@
     }
     
     // cleanup
-    ucx_buffer_free(in);
+    cxBufferFree(in);
     
     return args;
 }
@@ -2997,9 +3008,9 @@
 
 int shell_completion(char *cmd, CmdArgs *args, int index) { 
     if(index == 1) {
-        sstr_t prefix = { NULL, 0 };
+        cxstring prefix = { NULL, 0 };
         if(cmd) {
-            prefix = sstr(cmd);
+            prefix = cx_str(cmd);
         }
         for(int i=0;;i++) {
             char *str = cmdusageinfo[i];
@@ -3015,7 +3026,7 @@
                 }
             }
             if(prefix.ptr) {
-                if(!sstrprefix(sstrn(str, maxlen), prefix)) {
+                if(!cx_strprefix(cx_strn(str, maxlen), prefix)) {
                     continue;
                 }
             }
@@ -3053,7 +3064,7 @@
 }
 
 int url_completion(CmdArgs *args, char *u) {   
-    sstr_t url;
+    cxstring url;
     url.ptr = u;
     url.length = u ? strlen(u) : 0;
     
@@ -3082,10 +3093,9 @@
         }
     }
     if(repocomp) {
-        UcxList *repos = get_repositories();
-        UCX_FOREACH(elm, repos) {
-            Repository *repo = elm->data;
-            if(sstrprefix(sstr(repo->name), url)) {
+        CxIterator i = get_repositories();
+        cx_foreach(Repository*, repo, i) {
+            if(cx_strprefix(cx_str(repo->name), url)) {
                 if(quote == '\0') {
                     printf("%s/\n", repo->name);
                 } else {
@@ -3096,7 +3106,7 @@
         }
     } else {
         // url completion
-        ucx_map_cstr_put(args->options, "noinput", "");
+        cxMapPut(args->options, cx_hash_key_str("noinput"), "");
         
         char *path = NULL;
         Repository *repo = url2repo_s(url, &path);
@@ -3110,21 +3120,21 @@
         
         size_t plen = strlen(path);
         
-        sstr_t filter;
+        cxstring filter;
         char *lspath = NULL;
         if(path[plen-1] == '/') {
             lspath = strdup(path);
-            filter = S("");
+            filter = CX_STR("");
         } else {
             lspath = util_parent_path(path);
-            filter = sstr(util_resource_name(path));
+            filter = cx_str(util_resource_name(path));
         }
         
         DavResource *ls = dav_query(sn, "select - from %s order by name", lspath);
         DavResource *elm = ls ? ls->children : NULL;
         while(elm) {
-            sstr_t name = sstr(elm->name); 
-            if(sstrprefix(name, filter)) {
+            cxstring name = cx_str(elm->name); 
+            if(cx_strprefix(name, filter)) {
                 int space = 0;
                 for(int i=0;i<name.length;i++) {
                     if(name.ptr[i] == ' ') {
@@ -3133,8 +3143,8 @@
                     }
                 }
                 
-                UcxBuffer *out = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
-                ucx_buffer_puts(out, repo->name);
+                CxBuffer *out = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+                cxBufferPutString(out, repo->name);
                 if(space) {
                     size_t l = strlen(elm->path);
                     for(int i=0;i<l;i++) {
@@ -3142,16 +3152,16 @@
                         char nextc = elm->path[i];
                         if(quote == '\0' && NULL != strchr(
                                 "!\"#$&'()*,;<>?[\\]^`{|}~ ", nextc)) {
-                            ucx_buffer_putc(out, '\\');
+                            cxBufferPut(out, '\\');
                         }
-                        ucx_buffer_putc(out, nextc);
+                        cxBufferPut(out, nextc);
                     }
                 } else {
-                    ucx_buffer_puts(out, elm->path);
+                    cxBufferPutString(out, elm->path);
                 }
                 if(elm->iscollection) {
                     if(out->space[out->pos-1] != '/') {
-                        ucx_buffer_putc(out, '/');
+                        cxBufferPut(out, '/');
                     }
                 }
                 if (quote == '\0') {
@@ -3161,7 +3171,7 @@
                             quote, (int)out->pos, out->space, quote);
                 }
                 
-                ucx_buffer_free(out);
+                cxBufferFree(out);
             }
             elm = elm->next;
         }
--- a/dav/main.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/main.h	Fri Apr 21 21:25:32 2023 +0200
@@ -88,8 +88,8 @@
         Repository *repo,
         CmdArgs *a,
         DavSession *sn,
-        char *path,
-        char *name,
+        const char *path,
+        const char *name,
         uint32_t finfo,
         const char *fpath,
         FILE *in,
--- a/dav/opt.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/opt.h	Fri Apr 21 21:25:32 2023 +0200
@@ -34,9 +34,9 @@
 #endif
 
 typedef struct {
-    UcxMap *options;
-    char   **argv;
-    int    argc;
+    CxMap *options;
+    char  **argv;
+    int   argc;
 } CmdArgs;
 
 #ifdef __cplusplus
--- a/dav/optparser.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/optparser.c	Fri Apr 21 21:25:32 2023 +0200
@@ -33,9 +33,12 @@
 #include "optparser.h"
 #include "sopt.h"
 
+#include <cx/hash_map.h>
+
+
 void cmd_args_free(CmdArgs *args) {
     if(args) {
-        ucx_map_free(args->options);
+        cxMapDestroy(args->options);
         if(args->argv) {
             free(args->argv);
         }
@@ -43,9 +46,13 @@
     }
 }
 
+static void cmd_map_put(CxMap *map, const char *key, const void *value) {
+    cxMapPut(map, key, (void*)value);
+}
+
 CmdArgs* cmd_parse_args(int argc, char **argv) {
     CmdArgs *a = malloc(sizeof(CmdArgs));
-    a->options = ucx_map_new(16);
+    a->options = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     a->argv = argc > 0 ? calloc(argc, sizeof(char*)) : NULL;
     a->argc = 0;
     
@@ -71,7 +78,7 @@
             for(int c=1;c<len;c++) {
                 // argument is in the same arg
                 if(option) {
-                    ucx_map_cstr_put(a->options, option, &arg[c]);
+                    cxMapPut(a->options, cx_hash_key_str(option), &arg[c]);
                     option = NULL;
                     break;
                 }
@@ -83,7 +90,7 @@
                         return NULL;
                     }
                     case 'v': {
-                        ucx_map_cstr_put(a->options, "verbose", NOARG);
+                        cmd_map_put(a->options, "verbose", NOARG);
                         break;
                     }
                     case 'k': {
@@ -92,51 +99,51 @@
                         break;
                     }
                     case 'p': {
-                        ucx_map_cstr_put(a->options, "plain", NOARG);
+                        cmd_map_put(a->options, "plain", NOARG);
                         break;
                     }
                     case 'c': {
-                        ucx_map_cstr_put(a->options, "crypt", NOARG);
+                        cmd_map_put(a->options, "crypt", NOARG);
                         break;
                     }
                     case 'a': {
-                        ucx_map_cstr_put(a->options, "all", NOARG);
+                        cmd_map_put(a->options, "all", NOARG);
                         break;
                     }
                     case 'l': {
-                        ucx_map_cstr_put(a->options, "list", NOARG);
+                        cmd_map_put(a->options, "list", NOARG);
                         break;
                     }
                     case 'd': {
-                        ucx_map_cstr_put(a->options, "date", NOARG);
+                        cmd_map_put(a->options, "date", NOARG);
                         break;
                     }
                     case 't': {
-                        ucx_map_cstr_put(a->options, "type", NOARG);
+                        cmd_map_put(a->options, "type", NOARG);
                         break;
                     }
                     case 'R': {
-                        ucx_map_cstr_put(a->options, "recursive", NOARG);
+                        cmd_map_put(a->options, "recursive", NOARG);
                         break;
                     }
                     case 'O': {
-                        ucx_map_cstr_put(a->options, "override", NOARG);
+                        cmd_map_put(a->options, "override", NOARG);
                         break;
                     }
                     case 'i': {
-                        ucx_map_cstr_put(a->options, "insecure", NOARG);
+                        cmd_map_put(a->options, "insecure", NOARG);
                         break;
                     }
                     case 'N': {
-                        ucx_map_cstr_put(a->options, "noinput", NOARG);
+                        cmd_map_put(a->options, "noinput", NOARG);
                         break;
                     }
                     case 'e': {
-                        ucx_map_cstr_put(a->options, "extended", NOARG);
+                        cmd_map_put(a->options, "extended", NOARG);
                         break;
                     }
                     case 'x': {
-                        ucx_map_cstr_put(a->options, "xml", NOARG);
+                        cmd_map_put(a->options, "xml", NOARG);
                         break;
                     }
                     case 'F': {
@@ -146,11 +153,11 @@
                     }
                     case 'S': {
                         // undocumented hidden feature
-                        ucx_map_cstr_put(a->options, "structure", NOARG);
+                        cmd_map_put(a->options, "structure", NOARG);
                         break;
                     }
                     case 'K': {
-                        ucx_map_cstr_put(a->options, "keep", NOARG);
+                        cmd_map_put(a->options, "keep", NOARG);
                         break;
                     }
                     case 'o': {
@@ -193,7 +200,7 @@
                 }
             }
         } else if(option) {
-            ucx_map_cstr_put(a->options, option, arg);
+            cmd_map_put(a->options, option, arg);
             option = NULL;
         } else {
             a->argv[a->argc++] = arg;
@@ -209,6 +216,6 @@
     return a;
 }
 
-char* cmd_getoption(CmdArgs *arg, char *name) {
-    return ucx_map_cstr_get(arg->options, name);
+char* cmd_getoption(CmdArgs *arg, const char *name) {
+    return cxMapGet(arg->options, cx_hash_key_str(name));
 }
--- a/dav/optparser.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/optparser.h	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #ifndef OPTPARSER_H
 #define	OPTPARSER_H
 
-#include <ucx/map.h>
+#include <cx/map.h>
 #include "opt.h"
 
 #ifdef	__cplusplus
@@ -37,7 +37,7 @@
 #endif
 
 CmdArgs* cmd_parse_args(int argc, char **argv);
-char* cmd_getoption(CmdArgs *arg, char *name);
+char* cmd_getoption(CmdArgs *arg, const char *name);
 
 void cmd_args_free(CmdArgs *args);
 
--- a/dav/pwd.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/pwd.c	Fri Apr 21 21:25:32 2023 +0200
@@ -28,11 +28,13 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #include "pwd.h"
 
-#include <ucx/buffer.h>
-#include <ucx/utils.h>
+#include <cx/buffer.h>
+#include <cx/utils.h>
+#include <cx/hash_map.h>
 
 #ifdef _WIN32
 #include <winsock.h>
@@ -46,20 +48,20 @@
         return NULL;
     }
     
-    UcxBuffer *buf = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
-    ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write);
+    CxBuffer *buf = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cx_stream_copy(in, buf, (cx_read_func)fread, (cx_write_func)cxBufferWrite);
     fclose(in);
     
     if(buf->size < PWDS_HEADER_SIZE || buf->space[0] != PWDS_MAGIC_CHAR) {
-        ucx_buffer_free(buf);
+        cxBufferFree(buf);
         return NULL;
     }
     
     PwdStore *p = malloc(sizeof(PwdStore));
-    p->ids = ucx_map_new(16);
-    p->locations = NULL;
-    p->noloc = NULL;
-    p->index = ucx_map_new(16);
+    p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+    p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     p->content = buf;
     p->key = NULL;
     p->unlock_cmd = NULL;
@@ -77,11 +79,11 @@
 
 PwdStore* pwdstore_new(void) {
     PwdStore *p = calloc(1, sizeof(PwdStore));
-    p->ids = ucx_map_new(16);
-    p->locations = NULL;
-    p->noloc = NULL;
-    p->index = ucx_map_new(16);
-    p->content = ucx_buffer_new(NULL, PWDS_HEADER_SIZE, UCX_BUFFER_AUTOEXTEND);
+    p->ids = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+    p->locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    p->noloc = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    p->index = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
+    p->content = cxBufferCreate(NULL, PWDS_HEADER_SIZE, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     PWDS_MAGIC(p) = PWDS_MAGIC_CHAR;
     PWDS_VERSION(p) = 1;
     PWDS_ENC(p) = DAV_KEY_AES256;
@@ -92,7 +94,7 @@
     return p;
 }
 
-static int readval(UcxBuffer *in, char **val, int allowzero) {
+static int readval(CxBuffer *in, char **val, int allowzero) {
     // value  = length string
     // length = uint32
     // string = bytes
@@ -101,7 +103,7 @@
     
     // get length
     uint32_t length = 0;
-    if(ucx_buffer_read(&length, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) {
+    if(cxBufferRead(&length, 1, sizeof(uint32_t), in) != sizeof(uint32_t)) {
         return 0;
     }
     length = ntohl(length); // convert from BE to host byte order
@@ -119,7 +121,7 @@
     // get value
     char *value = malloc(length + 1);
     value[length] = 0;
-    if(ucx_buffer_read(value, 1, length, in) != length) {
+    if(cxBufferRead(value, 1, length, in) != length) {
         free(value);
         return 0;
     }
@@ -128,16 +130,17 @@
     return 1;
 }
 
-static int read_indexentry(PwdStore *p, UcxBuffer *in) {
+static int read_indexentry(PwdStore *p, CxBuffer *in) {
     // read type of index element
-    int type = ucx_buffer_getc(in);
+    int type = cxBufferGet(in);
     if(type == EOF || type != 0) {
         // only type 0 supported yet
         return 0;
     }
       
     char *id = NULL;
-    UcxList *locations = NULL;
+    CxList *locations = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    locations->simple_destructor = free;
     
     // get id (required)
     int ret = 0;
@@ -149,7 +152,7 @@
             if(!location) {
                 break;
             }
-            locations = ucx_list_append(locations, location);
+            cxListAdd(locations, location);
         }
     }
     
@@ -157,14 +160,14 @@
         pwdstore_put_index(p, id, locations);
     } else {
         if(id) free(id);
-        ucx_list_free_content(locations, free);
+        cxListDestroy(locations);
     }
     
     return ret;
 }
 
-static int read_pwdentry(PwdStore *p, UcxBuffer *in) {
-    int type = ucx_buffer_getc(in);
+static int read_pwdentry(PwdStore *p, CxBuffer *in) {
+    int type = cxBufferGet(in);
     if(type == EOF || type != 0) {
         // only type 0 supported yet
         return 0;
@@ -196,44 +199,36 @@
 static int remove_list_entries(PwdStore *s, const char *id) {
     int ret = 0;
     
-    UcxList *loc_entry = NULL;
-    UcxList *noloc_entry = NULL;
-    UCX_FOREACH(elm, s->locations) {
-        PwdIndexEntry *ie = elm->data;
+    CxList *loc_entry = NULL;
+    CxList *noloc_entry = NULL;
+    
+    CxMutIterator i = cxListMutIterator(s->locations);
+    cx_foreach(PwdIndexEntry*, ie, i) {
         if(!strcmp(ie->id, id)) {
-            loc_entry = elm;
-            ret = 1;
-            break;
+            cxIteratorFlagRemoval(i);
+            // TODO: break loop
         }
     }
-    UCX_FOREACH(elm, s->noloc) {
-        PwdIndexEntry *ie = elm->data;
+    i = cxListMutIterator(s->noloc);
+    cx_foreach(PwdIndexEntry*, ie, i) {
         if(!strcmp(ie->id, id)) {
-            noloc_entry = elm;
-            ret = 1;
-            break;
+            cxIteratorFlagRemoval(i);
+            // TODO: break loop
         }
     }
     
-    if(loc_entry) {
-        s->locations = ucx_list_remove(s->locations, loc_entry);
-    }
-    if(noloc_entry) {
-        s->noloc = ucx_list_remove(s->noloc, noloc_entry);
-    }
-    
     return ret;
 }
 
 void pwdstore_remove_entry(PwdStore *s, const char *id) {
     while(remove_list_entries(s, id)) {}
     
-    PwdIndexEntry *i = ucx_map_cstr_remove(s->index, id);
-    PwdEntry *e = ucx_map_cstr_remove(s->ids, id);
+    CxHashKey key = cx_hash_key_str(id);
+    PwdIndexEntry *i = cxMapRemoveAndGet(s->index, key);
+    PwdEntry *e = cxMapRemoveAndGet(s->ids, key);
     
     if(i) {
-        ucx_list_free_content(i->locations, free);
-        ucx_list_free(i->locations);
+        cxListDestroy(i->locations);
         free(i->id);
         free(i);
     }
@@ -253,7 +248,7 @@
     s->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t);
     
     // read indexlen and convert to host byte order
-    if(ucx_buffer_read(&netindexlen, 1, sizeof(uint32_t), s->content) != sizeof(uint32_t)) {
+    if(cxBufferRead(&netindexlen, 1, sizeof(uint32_t), s->content) != sizeof(uint32_t)) {
         return 1;
     }
     uint32_t indexlen = ntohl(netindexlen);
@@ -269,14 +264,14 @@
     s->encoffset = PWDS_HEADER_SIZE + indexlen;
     
     // the index starts after the header
-    UcxBuffer *index = ucx_buffer_new(s->content->space+PWDS_HEADER_SIZE, indexlen, 0);
+    CxBuffer *index = cxBufferCreate(s->content->space+PWDS_HEADER_SIZE, indexlen, cxDefaultAllocator, 0);
     index->size = indexlen;
     
     // read index
     while(read_indexentry(s, index)) {}
     
     // free index buffer structure (not the content)
-    ucx_buffer_free(index);
+    cxBufferFree(index);
     
     return 0;
 }
@@ -291,18 +286,18 @@
     
     // decrypt contet
     size_t encsz = p->content->size - p->encoffset;
-    UcxBuffer *enc = ucx_buffer_new(p->content->space + p->encoffset, encsz, 0);
+    CxBuffer *enc = cxBufferCreate(p->content->space + p->encoffset, encsz, cxDefaultAllocator, 0);
     enc->size = encsz;
     enc->size = p->content->size - p->encoffset;
-    UcxBuffer *content = aes_decrypt_buffer(enc, p->key);
-    ucx_buffer_free(enc);
+    CxBuffer *content = aes_decrypt_buffer(enc, p->key);
+    cxBufferFree(enc);
     if(!content) {
         return 1;
     }
     
     while(read_pwdentry(p, content)) {}
     
-    ucx_buffer_free(content);
+    cxBufferFree(content);
     
     return 0;
 }
@@ -335,24 +330,24 @@
 }
 
 void pwdstore_free(PwdStore* p) {
-    ucx_map_free_content(p->ids, (ucx_destructor)pwdstore_free_entry);
-    ucx_map_free(p->ids);
+    p->ids->simple_destructor = (cx_destructor_func)pwdstore_free_entry;
+    cxMapDestroy(p->ids);
     
-    ucx_list_free(p->locations);
+    cxListDestroy(p->locations);
     
     if(p->content) {
-        ucx_buffer_free(p->content);
+        cxBufferFree(p->content);
     }
     
     free(p);
 }
 
 int pwdstore_has_id(PwdStore *s, const char *id) {
-    return ucx_map_cstr_get(s->index, id) ? 1 : 0;
+    return cxMapGet(s->index, cx_hash_key_str(id)) ? 1 : 0;
 }
 
 PwdEntry* pwdstore_get(PwdStore *p, const char *id) {
-    PwdEntry *e = ucx_map_cstr_get(p->ids, id);
+    PwdEntry *e = cxMapGet(p->ids, cx_hash_key_str(id));
     if(e && e->user && e->password) {
         return e;
     } else {
@@ -365,11 +360,11 @@
     entry->id = strdup(id);
     entry->user = strdup(username);
     entry->password = strdup(password);
-    ucx_map_cstr_put(p->ids, id, entry);
+    cxMapPut(p->ids, cx_hash_key_str(id), entry);
 }
 
-void pwdstore_put_index(PwdStore *p, char *id, UcxList *locations) {
-    PwdIndexEntry *e = ucx_map_cstr_get(p->index, id);
+void pwdstore_put_index(PwdStore *p, char *id, CxList *locations) {
+    PwdIndexEntry *e = cxMapGet(p->index, cx_hash_key_str(id));
     if(e) {
         return;
     }
@@ -377,34 +372,34 @@
     newentry->id = id;
     if(locations) {
         newentry->locations = locations;
-        p->locations = ucx_list_append(p->locations, newentry);
+        cxListAdd(p->locations, newentry);
     } else {
         newentry->locations = NULL;
-        p->noloc = ucx_list_append(p->noloc, newentry);
+        cxListAdd(p->noloc, newentry);
     }
-    ucx_map_cstr_put(p->index, id, newentry);
+    cxMapPut(p->index, cx_hash_key_str(id), newentry);
 }
 
-void write_index_entry(UcxBuffer *out, PwdIndexEntry *e) {
+void write_index_entry(CxBuffer *out, PwdIndexEntry *e) {
     uint32_t idlen = strlen(e->id);
     uint32_t netidlen = htonl(idlen);
     
-    ucx_buffer_putc(out, 0); // type
+    cxBufferPut(out, 0); // type
 
-    ucx_buffer_write(&netidlen, 1, sizeof(uint32_t), out);
-    ucx_buffer_write(e->id, 1, idlen, out);
+    cxBufferWrite(&netidlen, 1, sizeof(uint32_t), out);
+    cxBufferWrite(e->id, 1, idlen, out);
     
-    UCX_FOREACH(elm, e->locations) {
-        char *location = elm->data;
+    CxIterator i = cxListIterator(e->locations);
+    cx_foreach(char *, location, i) {
         uint32_t locationlen = strlen(location);
         uint32_t netlocationlen = htonl(locationlen);
         
-        ucx_buffer_write(&netlocationlen, 1, sizeof(uint32_t), out);
-        ucx_buffer_write(location, 1, locationlen, out);
+        cxBufferWrite(&netlocationlen, 1, sizeof(uint32_t), out);
+        cxBufferWrite(location, 1, locationlen, out);
     }
     
     uint32_t terminate = 0;
-    ucx_buffer_write(&terminate, 1, sizeof(uint32_t), out);
+    cxBufferWrite(&terminate, 1, sizeof(uint32_t), out);
 }
 
 int pwdstore_store(PwdStore *p, const char *file) {
@@ -412,22 +407,21 @@
         return 1;
     }
     
-    UcxBuffer *index = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
-    UcxBuffer *content = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *index = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     // create index
-    UCX_FOREACH(elm, p->noloc) {
-        PwdIndexEntry *e = elm->data;
+    CxIterator i = cxListIterator(p->noloc);
+    cx_foreach(PwdIndexEntry*, e, i) {
         write_index_entry(index, e);
     }
-    UCX_FOREACH(elm, p->locations) {
-        PwdIndexEntry *e = elm->data;
+    i = cxListIterator(p->locations);
+    cx_foreach(PwdIndexEntry*, e, i) {
         write_index_entry(index, e);
     }
     
-    UcxMapIterator i = ucx_map_iterator(p->ids);
-    PwdEntry *value;
-    UCX_MAP_FOREACH(key, value, i) {
+    i = cxMapIteratorValues(p->ids);
+    cx_foreach(PwdEntry*, value, i) {
         if(!value->id || !value->user || !value->password) {
             continue;
         }
@@ -440,31 +434,31 @@
         uint32_t netplen = htonl(plen);
         
         // content buffer
-        ucx_buffer_putc(content, 0); // type
+        cxBufferPut(content, 0); // type
         
-        ucx_buffer_write(&netidlen, 1, sizeof(uint32_t), content);
-        ucx_buffer_write(value->id, 1, idlen, content);
-        ucx_buffer_write(&netulen, 1, sizeof(uint32_t), content);
-        ucx_buffer_write(value->user, 1, ulen, content);
-        ucx_buffer_write(&netplen, 1, sizeof(uint32_t), content);
-        ucx_buffer_write(value->password, 1, plen, content);
+        cxBufferWrite(&netidlen, 1, sizeof(uint32_t), content);
+        cxBufferWrite(value->id, 1, idlen, content);
+        cxBufferWrite(&netulen, 1, sizeof(uint32_t), content);
+        cxBufferWrite(value->user, 1, ulen, content);
+        cxBufferWrite(&netplen, 1, sizeof(uint32_t), content);
+        cxBufferWrite(value->password, 1, plen, content);
     }
       
     content->pos = 0;
-    UcxBuffer *enc = aes_encrypt_buffer(content, p->key);
+    CxBuffer *enc = aes_encrypt_buffer(content, p->key);
 
     p->content->pos = PWDS_HEADER_SIZE - sizeof(uint32_t);
     p->content->size = PWDS_HEADER_SIZE;
     
     // add index after header
     uint32_t netindexlen = htonl((uint32_t)index->size);
-    ucx_buffer_write(&netindexlen, 1, sizeof(uint32_t), p->content);
-    ucx_buffer_write(index->space, 1, index->size, p->content);
+    cxBufferWrite(&netindexlen, 1, sizeof(uint32_t), p->content);
+    cxBufferWrite(index->space, 1, index->size, p->content);
     
     // add encrypted buffer
-    ucx_buffer_write(enc->space, 1, enc->size, p->content);
+    cxBufferWrite(enc->space, 1, enc->size, p->content);
     
-    ucx_buffer_free(enc);
+    cxBufferFree(enc);
     
     FILE *out = fopen(file, "w");
     if(!out) {
--- a/dav/pwd.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/pwd.h	Fri Apr 21 21:25:32 2023 +0200
@@ -32,8 +32,9 @@
 #include <stdlib.h>
 #include <inttypes.h>
 
-#include <ucx/map.h>
-#include <ucx/buffer.h>
+#include <cx/map.h>
+#include <cx/buffer.h>
+#include <cx/linked_list.h>
 #include <libidav/crypto.h>
 
 #ifdef __cplusplus
@@ -84,30 +85,30 @@
      * key is the username
      * value is PwdEntry*
      */
-    UcxMap *ids;
+    CxMap *ids;
     
     /*
      * list of all credentials with location
      * value is PwdIndexEntry*
      */
-    UcxList *locations;
+    CxList *locations;
     
     /*
      * list of all credentials without location
      * value is PwdIndexEntry*
      */
-    UcxList *noloc;
+    CxList *noloc;
     
     /*
      * index map that contains all elements from the lists
      * 'locations' and 'noloc'
      */
-    UcxMap *index;
+    CxMap *index;
     
     /*
      * a buffer containing the complete file content
      */
-    UcxBuffer *content;
+    CxBuffer *content;
     
     /*
      * key used for encryption/decryption
@@ -150,7 +151,7 @@
 
 struct PwdIndexEntry {
     char *id;
-    UcxList *locations;
+    CxList *locations;
 };
 
 /*
@@ -179,7 +180,7 @@
 PwdEntry* pwdstore_get(PwdStore *p, const char *id);
 
 void pwdstore_put(PwdStore *p, const char *id, const char *username, const char *password);
-void pwdstore_put_index(PwdStore *p, char *id, UcxList *locations);
+void pwdstore_put_index(PwdStore *p, char *id, CxList *locations);
 
 void pwdstore_remove_entry(PwdStore *s, const char *id);
 
--- a/dav/scfg.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/scfg.c	Fri Apr 21 21:25:32 2023 +0200
@@ -31,8 +31,10 @@
 #include <string.h>
 #include <errno.h>
 #include <libidav/utils.h>
-#include <ucx/map.h>
-#include <ucx/utils.h>
+#include <cx/hash_map.h>
+#include <cx/utils.h>
+#include <cx/linked_list.h>
+#include <cx/printf.h>
 
 #include "scfg.h"
 #include "config.h"
@@ -58,10 +60,10 @@
 #define ENV_HOME getenv("HOME")
 #endif /* _WIN32 */
 
-static UcxMap *directories;
+static CxMap *directories;
 
-UcxMapIterator scfg_directory_iterator() {
-    return ucx_map_iterator(directories);
+CxIterator scfg_directory_iterator() {
+    return cxMapIteratorValues(directories);
 }
 
 static int create_default_sync_config(char *file) {
@@ -79,24 +81,22 @@
     return 0;
 }
 
-static UcxList* add_regex_pattern(UcxList *list, char *value,
+static void add_regex_pattern(CxList *list, char *value,
         unsigned short xmlline) {
-    regex_t *regex = malloc(sizeof(regex_t));
-    if (regcomp(regex, value, REG_EXTENDED|REG_NOSUB)) {
+    regex_t regex;
+    if (regcomp(&regex, value, REG_EXTENDED|REG_NOSUB)) {
         print_warning(xmlline,
                 "Invalid regular expression (%s) ... skipped\n", value);
-        free(regex);
-        return list;
     } else {
-        return ucx_list_append(list, regex);
+        cxListAdd(list, &regex);
     }
 }
 
 static int scfg_load_filter(
         xmlNode *node,
-        UcxList **include,
-        UcxList **exclude,
-        UcxList **tags)
+        CxList *include,
+        CxList *exclude,
+        CxList *tags)
 {
     node = node->children;
     
@@ -105,11 +105,11 @@
             char *value = util_xml_get_text(node);
             if(xstreq(node->name, "include")) {
                 if(value) {
-                    *include = add_regex_pattern(*include, value, node->line);
+                    add_regex_pattern(include, value, node->line);
                 }
             } else if(xstreq(node->name, "exclude")) {
                 if(value) {
-                    *exclude = add_regex_pattern(*exclude, value, node->line);
+                    add_regex_pattern(exclude, value, node->line);
                 }
             } else if(xstreq(node->name, "tags")) {
                 if(value) {
@@ -143,7 +143,7 @@
                         }
                         xmlFree(scope);
                         
-                        *tags = ucx_list_append(*tags, tagfilter);
+                        cxListAdd(tags, tagfilter);
                     }
                 }
             } else {
@@ -165,11 +165,15 @@
 }
 
 Filter* parse_filter(xmlNode *node) {
-    UcxList *include = NULL;
-    UcxList *exclude = NULL;
-    UcxList *tags = NULL;
+    CxList *include = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(regex_t));
+    CxList *exclude = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(regex_t));
+    CxList *tags = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS);
     
-    if(scfg_load_filter(node, &include, &exclude, &tags)) {
+    include->simple_destructor = (cx_destructor_func)regfree;
+    exclude->simple_destructor = (cx_destructor_func)regfree;
+    // TODO: set tags destructor
+    
+    if(scfg_load_filter(node, include, exclude, tags)) {
         return NULL;
     }
     
@@ -181,10 +185,10 @@
 }
 
 void init_default_filter(Filter *filter) {
-    if(!filter->include) {
-        regex_t *matchall = malloc(sizeof(regex_t));
-        regcomp(matchall, ".*", REG_NOSUB);
-        filter->include = ucx_list_append(NULL, matchall);
+    if(filter->include->size == 0) {
+        regex_t matchall;
+        regcomp(&matchall, ".*", REG_NOSUB);
+        cxListAdd(filter->include, &matchall);
     }
     /*
     if(!filter->exclude) {
@@ -343,15 +347,15 @@
     return sc;    
 }
 
-static UcxList* parse_splitconfig(xmlNode *node, int *error) {
-    UcxList *splitconfig = NULL;
+static CxList* parse_splitconfig(xmlNode *node, int *error) {
+    CxList *splitconfig = cxLinkedListCreateSimple(CX_STORE_POINTERS);
     int err = 0;
     xmlNode *c = node->children;
     while(c) {
         if(c->type == XML_ELEMENT_NODE && xstreq(c->name, "split")) {
             SplitConfig *sc = parse_split(c);
             if(sc) {
-                splitconfig = ucx_list_append(splitconfig, sc);
+                cxListAdd(splitconfig, sc);
             } else {
                 err = 1;
                 break;
@@ -427,10 +431,10 @@
     char *database = NULL;
     TagConfig *tagconfig = NULL;
     Versioning *versioning = NULL;
-    UcxList *include = NULL;
-    UcxList *exclude = NULL;
-    UcxList *tagfilter = NULL;
-    UcxList *splitconfig = NULL;
+    CxList *include = cxLinkedListCreateSimple(sizeof(regex_t));
+    CxList *exclude = cxLinkedListCreateSimple(sizeof(regex_t));
+    CxList *tagfilter = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *splitconfig = NULL;
     int max_retry = 0;
     int allow_cmd = SYNC_CMD_PULL | SYNC_CMD_PUSH
                   | SYNC_CMD_ARCHIVE | SYNC_CMD_RESTORE;
@@ -470,7 +474,7 @@
             } else if(xstreq(node->name, "repository")) {
                 repository = value;
             } else if(xstreq(node->name, "filter")) {
-                if(scfg_load_filter(node, &include, &exclude, &tagfilter)) {
+                if(scfg_load_filter(node, include, exclude, tagfilter)) {
                     return 1;
                 }
             } else if(xstreq(node->name, "database")) {
@@ -647,7 +651,7 @@
     dir->filter.tags = tagfilter;
     init_default_filter(&dir->filter);
     
-    if (trash && sstrtrim(sstr(trash)).length > 0) {
+    if (trash && cx_strtrim(cx_str(trash)).length > 0) {
         if (trash[0] == '/' || trash[0] == '$') {
             dir->trash = scfg_create_path(trash);
         } else {
@@ -665,13 +669,13 @@
         dir->trash = NULL;
     }
     
-    ucx_map_cstr_put(directories, name, dir);
+    cxMapPut(directories, cx_hash_key_str(name), dir);
     
     return 0;
 }
 
 int load_sync_config() {
-    directories = ucx_map_new(8);
+    directories = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8);
     
     if(check_config_dir()) {
         fprintf(stderr, "Cannot create .dav directory\n");
@@ -724,8 +728,8 @@
     return ret;
 }
 
-SyncDirectory* scfg_get_dir(char *name) {
-    return ucx_map_cstr_get(directories, name);
+SyncDirectory* scfg_get_dir(const char *name) {
+    return cxMapGet(directories, cx_hash_key_str(name));
 }
 
 int scfg_check_dir(SyncDirectory *dir) {
@@ -756,7 +760,7 @@
     return 0;
 }
 
-char* scfg_create_path(char *cfg) {
+char* scfg_create_path(const char *cfg) {
     if(!cfg) {
         return NULL;
     }
@@ -764,15 +768,15 @@
         return strdup(cfg);
     }
     
-    sstr_t s = sstr(cfg);
-    sstr_t path = sstrchr(sstr(cfg), '/');
+    cxstring s = cx_str(cfg);
+    cxstring path = cx_strchr(cx_str(cfg), '/');
     char *localpath = NULL;
     if(path.length > 0) {
         // path = $var/path/
         
-        sstr_t var = sstrsubsl(s, 1, path.ptr - s.ptr - 1);
+        cxstring var = cx_strsubsl(s, 1, path.ptr - s.ptr - 1);
         if(var.length > 0) {
-            char *env = sstrdup(var).ptr;
+            char *env = cx_strdup(var).ptr;
             char *envval = getenv(env);
             free(env);
             if(envval) {
@@ -786,7 +790,7 @@
                 exit(-1);
             }
         } else {
-            localpath = sstrdup(path).ptr;
+            localpath = cx_strdup(path).ptr;
         }
     } else {
         // path = $var
@@ -852,20 +856,19 @@
     return ret;
 }
 
-char* generate_db_name(char *basename) {
+char* generate_db_name(const char *basename) {
     char *dbname = NULL;
     int count = -1;
     while(!dbname) {
-        sstr_t name = count < 0 ?
-                ucx_sprintf("%s-db.xml", basename) : 
-                ucx_sprintf("%s%d-db.xml", basename, count);
+        cxmutstr name = count < 0 ?
+                cx_asprintf("%s-db.xml", basename) : 
+                cx_asprintf("%s%d-db.xml", basename, count);
         count++;
         
-        UcxMapIterator i = ucx_map_iterator(directories);
-        SyncDirectory *dir;
+        CxIterator i = cxMapIteratorValues(directories);
         bool unique = true;
-        UCX_MAP_FOREACH(key, dir, i) {
-            if(!sstrcmp(name, sstr(dir->database))) {
+        cx_foreach(SyncDirectory *, dir, i) {
+            if(!cx_strcmp(cx_strcast(name), cx_str(dir->database))) {
                 unique = false;
                 break;
             }
@@ -879,23 +882,15 @@
 }
 
 void free_filter(Filter filter) {
-    UCX_FOREACH(elm, filter.include) {
-        regfree(elm->data);
-        free(elm->data);
-    }
-    ucx_list_free(filter.include);
-    UCX_FOREACH(elm, filter.exclude) {
-        regfree(elm->data);
-        free(elm->data);
-    }
-    ucx_list_free(filter.exclude);
+    cxListDestroy(filter.include);
+    cxListDestroy(filter.exclude);
+    cxListDestroy(filter.tags);
 }
 
 void free_sync_config() {
     if(directories) {
-        UcxMapIterator i = ucx_map_iterator(directories);
-        SyncDirectory *dir;
-        UCX_MAP_FOREACH(elm, dir, i) {
+        CxIterator i = cxMapIteratorValues(directories);
+        cx_foreach(SyncDirectory *, dir, i) {
             free(dir->name);
             free(dir->path);
             free(dir->repository);
@@ -913,6 +908,6 @@
             free(dir);
         }
 
-        ucx_map_free(directories);
+        cxMapDestroy(directories);
     }
 }
--- a/dav/scfg.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/scfg.h	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #ifndef SCFG_H
 #define	SCFG_H
 
-#include <ucx/string.h>
+#include <cx/string.h>
 #include <stdbool.h>
 #include <libidav/webdav.h>
 #include <regex.h>
@@ -73,9 +73,9 @@
 typedef enum PushStrategy PushStrategy;
 
 struct Filter {
-    UcxList *include;
-    UcxList *exclude;
-    UcxList *tags;
+    CxList *include;
+    CxList *exclude;
+    CxList *tags;
 };
 
 typedef struct SyncDirectory {
@@ -88,7 +88,7 @@
     TagConfig *tagconfig;
     Versioning *versioning;
     Filter filter;
-    UcxList *splitconfig;
+    CxList *splitconfig;
     uint32_t metadata;
     int max_retry;
     int allow_cmd;
@@ -167,16 +167,16 @@
 
 int load_sync_config();
 
-UcxMapIterator scfg_directory_iterator();
-SyncDirectory* scfg_get_dir(char *name);
+CxIterator scfg_directory_iterator();
+SyncDirectory* scfg_get_dir(const char *name);
 
 int scfg_check_dir(SyncDirectory *dir);
 
-char* scfg_create_path(char *cfg);
+char* scfg_create_path(const char *cfg);
 
 int add_directory(SyncDirectory *dir);
 
-char* generate_db_name(char *basename);
+char* generate_db_name(const char *basename);
 
 void free_filter(Filter filter);
 
--- a/dav/sopt.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/sopt.c	Fri Apr 21 21:25:32 2023 +0200
@@ -32,17 +32,23 @@
 
 #include "sopt.h"
 
+#include <cx/hash_map.h>
+
 void cmd_args_free(CmdArgs *args) {
     if(args) {
-        ucx_map_free(args->options);
+        cxMapDestroy(args->options);
         free(args->argv);
         free(args);
     }
 }
 
+static void cmd_map_put(CxMap *map, const char *key, const void *value) {
+    cxMapPut(map, key, (void*)value);
+}
+
 CmdArgs* cmd_parse_args(int argc, char **argv) {
     CmdArgs *a = malloc(sizeof(CmdArgs));
-    a->options = ucx_map_new(16);
+    a->options = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     a->argv = calloc(argc, sizeof(char*));
     a->argc = 0;
     
@@ -62,23 +68,23 @@
                         return NULL;
                     }
                     case 'c': {
-                        ucx_map_cstr_put(a->options, "conflict", NOARG);
+                        cmd_map_put(a->options, "conflict", NOARG);
                         break;
                     }
                     case 'l': {
-                        ucx_map_cstr_put(a->options, "lock", NOARG);
+                        cmd_map_put(a->options, "lock", NOARG);
                         break;
                     }
                     case 'd': {
-                        ucx_map_cstr_put(a->options, "nolock", NOARG);
+                        cmd_map_put(a->options, "nolock", NOARG);
                         break;
                     }
                     case 'r': {
-                        ucx_map_cstr_put(a->options, "remove", NOARG);
+                        cmd_map_put(a->options, "remove", NOARG);
                         break;
                     }
                     case 'v': {
-                        ucx_map_cstr_put(a->options, "verbose", NOARG);
+                        cmd_map_put(a->options, "verbose", NOARG);
                         break;
                     }
                     case 's': {
@@ -97,20 +103,20 @@
                         break;
                     }
                     case 'R': {
-                        ucx_map_cstr_put(a->options, "restore-removed", NOARG);
+                        cmd_map_put(a->options, "restore-removed", NOARG);
                         break;
                     }
                     case 'M': {
-                        ucx_map_cstr_put(a->options, "restore-modified", NOARG);
+                        cmd_map_put(a->options, "restore-modified", NOARG);
                         break;
                     }
                     case 'S': {
-                        ucx_map_cstr_put(a->options, "snapshot", NOARG);
+                        cmd_map_put(a->options, "snapshot", NOARG);
                     }
                 }
             }
         } else if(option) {
-            ucx_map_cstr_put(a->options, option, arg);
+            cmd_map_put(a->options, option, arg);
             option = NULL;
         } else {
             a->argv[a->argc++] = arg;
@@ -120,6 +126,6 @@
     return a;
 }
 
-char* cmd_getoption(CmdArgs *arg, char *name) {
-    return ucx_map_cstr_get(arg->options, name);
+char* cmd_getoption(CmdArgs *arg, const char *name) {
+    return cxMapGet(arg->options, cx_hash_key_str(name));
 }
--- a/dav/sopt.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/sopt.h	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #ifndef OPTPARSER_H
 #define	OPTPARSER_H
 
-#include <ucx/map.h>
+#include <cx/map.h>
 #include "opt.h"
 
 #ifdef	__cplusplus
@@ -40,7 +40,7 @@
     
 void cmd_args_free(CmdArgs *args);
 CmdArgs* cmd_parse_args(int argc, char **argv);
-char* cmd_getoption(CmdArgs *arg, char *name);
+char* cmd_getoption(CmdArgs *arg, const char *name);
 
 #ifdef	__cplusplus
 }
--- a/dav/sync.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/sync.c	Fri Apr 21 21:25:32 2023 +0200
@@ -36,9 +36,10 @@
 #include <utime.h>
 #include <libxml/xmlerror.h>
 #include <sys/types.h>
-#include <ucx/string.h>
-#include <ucx/utils.h>
-#include <ucx/properties.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/hash_map.h>
+#include <cx/printf.h>
 #include <dirent.h>
 
 #include <math.h>
@@ -355,12 +356,12 @@
 
 static int res_matches_filter(Filter *filter, char *res_path) {
     // include/exclude filter
-    UCX_FOREACH(inc, filter->include) {
-        regex_t* pattern = (regex_t*) inc->data;
+    CxIterator i = cxListIterator(filter->include);
+    cx_foreach(regex_t*, pattern, i) {
         if (regexec(pattern, res_path, 0, NULL, 0) == 0) {
-            UCX_FOREACH(exc, filter->exclude) {
-                regex_t* pattern = (regex_t*) exc->data;
-                if (regexec(pattern, res_path, 0, NULL, 0) == 0) {
+            CxIterator e = cxListIterator(filter->exclude);
+            cx_foreach(regex_t*, expat, e) {
+                if (regexec(expat, res_path, 0, NULL, 0) == 0) {
                     return 1;
                 }
             }
@@ -373,7 +374,7 @@
 static int res_matches_dir_filter(SyncDirectory *dir, char *res_path) {
     // trash filter
     if (dir->trash) {
-        sstr_t rpath = sstr(util_concat_path(dir->path, res_path));
+        cxmutstr rpath = cx_mutstr(util_concat_path(dir->path, res_path));
         if (util_path_isrelated(dir->trash, rpath.ptr)) {
             free(rpath.ptr);
             return 1;
@@ -407,12 +408,11 @@
     }
     
     DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags");
-    UcxList *res_tags = parse_dav_xml_taglist(tagsprop);
+    CxList *res_tags = parse_dav_xml_taglist(tagsprop);
     
     int ret = matches_tagfilter(res_tags, tagfilter);
     
-    ucx_list_free_content(res_tags, (ucx_destructor) free_dav_tag);
-    ucx_list_free(res_tags);
+    cxListDestroy(res_tags);
     
     return ret;
 }
@@ -436,14 +436,11 @@
     }
 
     DavBool changed = 0;
-    UcxList *res_tags = sync_get_file_tags(dir, res, &changed, NULL);
+    CxList *res_tags = sync_get_file_tags(dir, res, &changed, NULL);
     
     int ret = matches_tagfilter(res_tags, tagfilter);
-    UCX_FOREACH(elm, res_tags) {
-        DavTag *t = elm->data;
-        free_dav_tag(t);
-    }
-    ucx_list_free(res_tags);
+    CxIterator i = cxListIterator(res_tags);
+    cxListDestroy(res_tags);
     return ret;
 }
 
@@ -510,7 +507,7 @@
 }
 
 static void localres_keep(SyncDatabase *db, const char *path) {
-    LocalResource *local = ucx_map_cstr_remove(db->resources, path);
+    LocalResource *local = cxMapRemoveAndGet(db->resources, cx_hash_key_str(path));
     if(local) {
         local->keep = TRUE;
     }
@@ -528,21 +525,23 @@
     return 1;
 }
 
-void res2map(DavResource *root, UcxMap *map) {
-    UcxList *stack = ucx_list_prepend(NULL, root->children);
-    while(stack) {
-        DavResource *res = stack->data;
-        stack = ucx_list_remove(stack, stack);
+void res2map(DavResource *root, CxMap *map) {
+    CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    cxListInsert(stack, 0, root->children);
+    while(stack->size > 0) {
+        DavResource *res = cxListAt(stack, 0);
+        cxListRemove(stack, 0);
         
         while(res) {
-            ucx_map_cstr_put(map, res->path, res);
+            cxMapPut(map, cx_hash_key_str(res->path), res);
             
             if(res->children) {
-                stack = ucx_list_prepend(stack, res->children);
+                cxListInsert(stack, 0, res->children);
             }
             res = res->next;
         }
     }
+    cxListDestroy(stack);
 }
 
 int cmd_pull(CmdArgs *a, DavBool incoming) {
@@ -575,7 +574,7 @@
         return -1;
     }
     
-    Repository *repo = get_repository(sstr(dir->repository));
+    Repository *repo = get_repository(cx_str(dir->repository));
     if(!repo) {
         fprintf(stderr, "Unknown repository %s\n", dir->repository);
         return -1;
@@ -588,13 +587,13 @@
     }
     remove_deleted_conflicts(dir, db);
     
-    UcxMap *hashes = NULL;
+    CxMap *hashes = NULL;
     if(SYNC_HASHING(dir)) {
         hashes = create_hash_index(db);
     }
     
     DavSession *sn = create_session(a, ctx, repo, dir->collection);
-    ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db);
+    util_regdestr(sn->mp, db, (cx_destructor_func)destroy_db);
     if (cmd_getoption(a, "verbose")) {
         curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
         curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
@@ -664,31 +663,32 @@
     int sync_error = 0;
     int sync_conflict = 0;
     
-    UcxList *res_modified = NULL;
-    UcxList *res_new = NULL;
-    UcxList *res_moved = NULL; // type: MovedFile
-    UcxList *res_link = NULL;
-    UcxList *res_conflict = NULL;
-    UcxList *res_mkdir = NULL;
-    UcxList *res_metadata = NULL;
-    UcxList *res_broken = NULL;
-    UcxMap  *lres_removed = ucx_map_new(16); // type: LocalResource*
+    CxList *res_modified = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *res_new = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *res_moved = cxLinkedListCreateSimple(CX_STORE_POINTERS); // type: MovedFile
+    CxList *res_link = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *res_conflict = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *res_mkdir = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *res_metadata = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *res_broken = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxMap  *lres_removed = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16); // type: LocalResource*
     
     //UcxMap *svrres = ucx_map_new(db->resources->count);
-    UcxMap *dbres = ucx_map_clone(db->resources, NULL, NULL);
-    
-    UcxList *stack = ucx_list_prepend(NULL, ls->children);
-    while(stack) {
-        DavResource *res = stack->data;
-        stack = ucx_list_remove(stack, stack);
+    CxMap *dbres = NULL; // TODO: ucx_map_clone(db->resources, NULL, NULL);
+    
+    CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    cxListInsert(stack, 0, ls->children);
+    while(stack->size > 0) {
+        DavResource *res = cxListAt(stack, 0);
+        cxListRemove(stack, 0);
          
         while(res) {
             DavBool res_filtered = FALSE;
             if (res_matches_dir_filter(dir, res->path)) {
                 res_filtered = TRUE;
             } else {
-                UCX_FOREACH(elm, dir->filter.tags) {
-                    SyncTagFilter *tf = elm->data;
+                CxIterator iter = cxListIterator(dir->filter.tags);
+                cx_foreach(SyncTagFilter *, tf, iter) {
                     if(!res_matches_tags(res, tf)) {
                         res_filtered = TRUE;
                         break;
@@ -714,7 +714,7 @@
             if(status && !strcmp(status, "broken")) {
                 res = res->next;
                 localres_keep(db, res->path);
-                res_broken = ucx_list_append(res_broken, res);
+                cxListAdd(res_broken, res);
                 continue;
             }
             
@@ -723,28 +723,28 @@
             switch(change) {
                 case REMOTE_NO_CHANGE: break;
                 case REMOTE_CHANGE_MODIFIED: {
-                    res_modified = ucx_list_append(res_modified, res);
+                    cxListAdd(res_modified, res);
                     break;
                 }
                 case REMOTE_CHANGE_NEW: {
-                    res_new = ucx_list_append(res_new, res);
+                    cxListAdd(res_new, res);
                     break;
                 }
                 case REMOTE_CHANGE_DELETED: break; // never happens
                 case REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED: {
-                    res_conflict = ucx_list_append(res_conflict, res);
+                    cxListAdd(res_conflict, res);
                     break;
                 }
                 case REMOTE_CHANGE_METADATA: {
-                    res_metadata = ucx_list_append(res_metadata, res);
+                    cxListAdd(res_metadata, res);
                     break;
                 }
                 case REMOTE_CHANGE_MKDIR: {
-                    res_mkdir = ucx_list_append(res_mkdir, res);
+                    cxListAdd(res_mkdir, res);
                     break;
                 }
                 case REMOTE_CHANGE_LINK: {
-                    res_link = ucx_list_append(res_link, res);
+                    cxListAdd(res_link, res);
                     break;
                 }
             }
@@ -752,10 +752,10 @@
             // remove every server resource from dbres
             // all remaining elements are the resources that are removed
             // on the server
-            ucx_map_cstr_remove(dbres, res->path);
+            cxMapRemove(dbres, cx_hash_key_str(res->path));
             
             if(!dav_get_property_ns(res, DAV_NS, "split") && res->children) {
-                stack = ucx_list_prepend(stack, res->children);
+                cxListInsert(stack, 0, res->children);
             }
             res = res->next;
         }
@@ -764,16 +764,15 @@
     // find deleted resources
     // svrres currently contains all resources from the server
     // and will replace the current db->resources map later
-    UcxMapIterator i = ucx_map_iterator(dbres);
-    LocalResource *local;
-    UCX_MAP_FOREACH(key, local, i) {
+    CxIterator i = cxMapIteratorValues(dbres);
+    cx_foreach(LocalResource *, local, i) {
         if (res_matches_dir_filter(dir, local->path)) {
             continue;
         }
         if(!local->keep) {
-            ucx_map_cstr_put(lres_removed, local->path, local);
-            if(lres_removed->count > lres_removed->size * 2) {
-                ucx_map_rehash(lres_removed);
+            cxMapPut(lres_removed, cx_hash_key_str(local->path), local);
+            if(lres_removed->size > lres_removed->size * 2) {
+                cxMapRehash(lres_removed);
             }
         }
     }
@@ -783,31 +782,25 @@
     //
     
     // the first thing we need are all directories to put the files in
-    UCX_FOREACH(elm, res_mkdir) {
-        DavResource *res = elm->data;
+    i = cxListIterator(res_mkdir);
+    cx_foreach(DavResource *, res, i) {
         if(sync_get_collection(a, dir, res, db)) {
             sync_error++;
         }
     }
     
     // we need a map for all conflicts for fast lookups
-    UcxMap *conflicts = ucx_map_new(ucx_list_size(res_conflict)+16);
-    UCX_FOREACH(elm, res_conflict) {
-        DavResource *res = elm->data;
-        ucx_map_cstr_put(conflicts, res->path, res);
+    CxMap *conflicts = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, res_conflict->size+16);
+    i = cxListIterator(res_conflict);
+    cx_foreach(DavResource *, res, i) {
+        cxMapPut(conflicts, cx_hash_key_str(res->path), res);
     }
     
     if(SYNC_HASHING(dir)) {
         // check for moved/copied files
-        UcxList *elm = res_new;
-        UcxList *prev = NULL;
-        UcxList *next = NULL;
         SYS_STAT s;
-        for(;elm;elm=next) {
-            DavResource *res = elm->data;
-            prev = elm->prev;
-            next = elm->next;
-            
+        CxMutIterator mut_iter = cxListMutIterator(res_new);
+        cx_foreach(DavResource *, res, mut_iter) {
             if(dav_get_property_ns(res, DAV_PROPS_NS, "link")) {
                 continue;
             }
@@ -817,7 +810,7 @@
                 continue;
             }
             
-            LocalResource *local = ucx_map_cstr_get(hashes, hash);
+            LocalResource *local = cxMapGet(hashes, cx_hash_key_str(hash));
             if(!local) {
                 continue;
             }
@@ -833,35 +826,28 @@
             MovedFile *mf = malloc(sizeof(MovedFile));
             mf->content = local;
             mf->resource = res;
-            if(ucx_map_cstr_remove(lres_removed, local->path)) {
+            if(cxMapRemoveAndGet(lres_removed, cx_hash_key_str(local->path))) {
                 mf->copy = FALSE;
             } else {
                 mf->copy = TRUE;
             }
             
-            res_moved = ucx_list_append(res_moved, mf);
+            cxListAdd(res_moved, mf);
             
             // remove item from res_new
-            if(prev) {
-                prev->next = next;
-            } else {
-                res_new = next;
-            }
-            if(next) {
-                next->prev = prev;
-            }
+            cxIteratorFlagRemoval(mut_iter);
         }
     }
     
     // do copy/move operations
-    UCX_FOREACH(elm, res_moved) {
-        MovedFile *mf = elm->data;
+    i = cxListIterator(res_moved);
+    cx_foreach(MovedFile *, mf, i) {
         if(sync_shutdown) {
             break;
         }
         
         DavBool issplit = dav_get_property_ns(mf->resource, DAV_NS, "split") ? 1 : 0;  
-        if(ucx_map_cstr_get(conflicts, mf->resource->path)) {
+        if(cxMapGet(conflicts, cx_hash_key_str(mf->resource->path))) {
             rename_conflict_file(dir, db, mf->resource->path, issplit);
             sync_conflict++;
         }
@@ -874,36 +860,45 @@
     }
     
     // download all new, modified and conflict files
-    UcxList *download = ucx_list_concat(res_modified, res_conflict);
-    download = ucx_list_concat(res_new, download);
-    download = ucx_list_concat(download, res_link);
-    UCX_FOREACH(elm, download) {
-        DavResource *res = elm->data;
+    for(int n=0;n<4;n++) {
+        CxList *ls;
+        if(n == 0) {
+            ls = res_new;
+        } else if(n == 1) {
+            ls = res_modified;
+        } else if(n == 2) {
+            ls = res_conflict;
+        } else {
+            ls = res_link;
+        }
+        CxIterator iter = cxListIterator(ls);
+        cx_foreach(DavResource *, res, iter) {
+            if(sync_shutdown) {
+                break;
+            }
+            
+            DavBool issplit = dav_get_property_ns(res, DAV_NS, "split") ? 1 : 0;  
+            if(cxMapGet(conflicts, cx_hash_key_str(res->path))) {
+                rename_conflict_file(dir, db, res->path, issplit);
+                sync_conflict++;
+            }
+
+            // download the resource
+            if(sync_get_resource(a, dir, res->path, res, db, TRUE, &sync_success)) {
+                fprintf(stderr, "resource download failed: %s\n", res->path);
+                sync_error++;
+            }
+        }
+    }
+    
+    // update metadata
+    i = cxListIterator(res_metadata);
+    cx_foreach(DavResource *, res, i) {
         if(sync_shutdown) {
             break;
         }
         
-        DavBool issplit = dav_get_property_ns(res, DAV_NS, "split") ? 1 : 0;  
-        if(ucx_map_cstr_get(conflicts, res->path)) {
-            rename_conflict_file(dir, db, res->path, issplit);
-            sync_conflict++;
-        }
-        
-        // download the resource
-        if(sync_get_resource(a, dir, res->path, res, db, TRUE, &sync_success)) {
-            fprintf(stderr, "resource download failed: %s\n", res->path);
-            sync_error++;
-        }
-    }
-    
-    // update metadata
-    UCX_FOREACH(elm, res_metadata) {
-        DavResource *res = elm->data;
-        if(sync_shutdown) {
-            break;
-        }
-        
-        LocalResource *local = ucx_map_cstr_get(db->resources, res->path);
+        LocalResource *local = cxMapGet(db->resources, cx_hash_key_str(res->path));
         if(local) {
             printf("update: %s\n", res->path);
             char *res_path = resource_local_path(res);
@@ -929,36 +924,35 @@
         }
     }
     
-    UcxList *rmdirs = NULL;
-    UcxMapIterator mi = ucx_map_iterator(lres_removed);
+    CxList *rmdirs = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_pathlen_cmp, CX_STORE_POINTERS);
+    i = cxMapIteratorValues(lres_removed);
     LocalResource *removed_res;
-    UcxKey key;
-    UCX_MAP_FOREACH(key, removed_res, mi) {
+    cx_foreach(LocalResource *, removed_res, i) {
         if(sync_shutdown) {
             break;
         }
         
         int ret = sync_remove_local_resource(dir, removed_res);
         if(ret == -1) {
-            rmdirs = ucx_list_append(rmdirs, removed_res);
+            cxListAdd(rmdirs, removed_res);
         } else if(ret == 0) {
-            LocalResource *local = ucx_map_cstr_remove(db->resources, removed_res->path);
+            LocalResource *local = cxMapRemoveAndGet(db->resources, cx_hash_key_str(removed_res->path));
             if(local) {
                 local_resource_free(local);
             }
             sync_delete++;
         }     
     }
-    ucx_map_free(lres_removed);
+    cxMapDestroy(lres_removed);
     
     // sort dir list, we need to delete dirs with higher depth first
-    rmdirs = ucx_list_sort(rmdirs, (cmp_func)resource_pathlen_cmp, NULL);
+    cxListSort(rmdirs);
     // delete dirs
-    UCX_FOREACH(elm, rmdirs) {
-        LocalResource *local_dir = elm->data;
+    i = cxListIterator(rmdirs);
+    cx_foreach(LocalResource *, local_dir, i) {
         if(!sync_remove_local_directory(dir, local_dir)) {
             // dir successfully removed, now remove the related db entry
-            LocalResource *local = ucx_map_cstr_remove(db->resources, local_dir->path);
+            LocalResource *local = cxMapRemoveAndGet(db->resources, cx_hash_key_str(local_dir->path));
             if(local) {
                 local_resource_free(local);
             }
@@ -1030,7 +1024,7 @@
     RemoteChangeType type = cmd_getoption(a, "conflict") ?
             REMOTE_CHANGE_MODIFIED : REMOTE_CHANGE_CONFLICT_LOCAL_MODIFIED;
     
-    LocalResource *local = ucx_map_cstr_get(db->resources, res->path);
+    LocalResource *local = cxMapGet(db->resources, cx_hash_key_str(res->path));
     char *local_path = create_local_path(dir, res->path);
     
     char *link = SYNC_SYMLINK(dir) ? 
@@ -1081,9 +1075,9 @@
                 nochange = TRUE;
             }
         } else if(local->etag) {
-            sstr_t e = sstr(etag);
-            if(sstrprefix(e, S("W/"))) {
-                e = sstrsubs(e, 2);
+            cxstring e = cx_str(etag);
+            if(cx_strprefix(e, CX_STR("W/"))) {
+                e = cx_strsubs(e, 2);
             }
             if(!strcmp(e.ptr, local->etag)) {
                 // resource is already up-to-date on the client
@@ -1164,7 +1158,7 @@
         // check if tags have changed
         if(dir->tagconfig) {
             DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags");
-            UcxList *remote_tags = NULL;
+            CxList *remote_tags = NULL;
             if(tagsprop) {
                 remote_tags = parse_dav_xml_taglist(tagsprop);
             }
@@ -1213,7 +1207,7 @@
             local = calloc(1, sizeof(LocalResource));
             local->path = strdup(res->path);
             
-            ucx_map_cstr_put(db->resources, local->path, local);
+            cxMapPut(db->resources, cx_hash_key_str(local->path), local);
         }
         
         // update local res
@@ -1245,7 +1239,7 @@
     local->size = s->st_size;
 }
 
-static UcxList* sync_download_changed_parts(
+static CxList* sync_download_changed_parts(
         DavResource *res,
         LocalResource *local,
         FILE *out,
@@ -1254,7 +1248,8 @@
         int64_t *truncate_file,
         int *err)
 {
-    UcxList *updates = NULL;
+    CxList *updates = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    updates->simple_destructor = (cx_destructor_func)filepart_free;
     
     size_t local_numparts = local ? local->numparts : 0;
     fseeko(out, 0, SEEK_END);
@@ -1262,7 +1257,7 @@
     
     int error = 0;
     
-    UcxBuffer *buf = ucx_buffer_new(NULL, blocksize, 0);
+    CxBuffer *buf = cxBufferCreate(NULL, blocksize, cxDefaultAllocator, 0);
     
     int64_t maxsize = -1;
     
@@ -1306,7 +1301,7 @@
                 }
                 buf->pos = 0;
                 buf->size = 0;
-                if(dav_get_content(part, buf,(dav_write_func)ucx_buffer_write)) {
+                if(dav_get_content(part, buf,(dav_write_func)cxBufferWrite)) {
                     fprintf(stderr, "Error: cannot download part: %s\n", part->name);
                     error = 1;
                     break;
@@ -1321,7 +1316,7 @@
                 update->block = partnum;
                 update->etag = etag ? strdup(etag) : NULL;
                 update->hash = dav_create_hash(buf->space, buf->size);
-                updates = ucx_list_append(updates, update);
+                cxListAdd(updates, update);
                 
                 block_end = offset+buf->size;
             } else {
@@ -1343,11 +1338,11 @@
         part = part->next;
     }
     
-    ucx_buffer_free(buf);
+    cxBufferFree(buf);
     
     if(error) {
         *err = 1;
-        ucx_list_free_content(updates, (ucx_destructor)filepart_free);
+        cxListDestroy(updates);
         return NULL;
     }
     
@@ -1373,7 +1368,7 @@
         return 1;
     }
     
-    ucx_stream_copy(in, out, (read_func)fread, (write_func)fwrite);
+    cx_stream_copy(in, out, (cx_read_func)fread, (cx_write_func)fwrite);
     fclose(in);
     fclose(out);
     
@@ -1415,7 +1410,7 @@
     } else {
         // reuse previous LocalResource (content)
         // remove it from db->resources, change path and put it back
-        local = ucx_map_cstr_remove(db->resources, content->path);
+        local = cxMapRemoveAndGet(db->resources, cx_hash_key_str(content->path));
         if(!local) {
             // can't happen, but handle it nevertheless
             local = content;
@@ -1423,7 +1418,7 @@
         free(content->path);
         local->path = strdup(res->path);
     }
-    ucx_map_cstr_put(db->resources, local->path, local);
+    cxMapPut(db->resources, cx_hash_key_str(local->path), local);
     
     if(sync_store_metadata(dir, new_path, local, res)) {
         fprintf(stderr, "Cannot store metadata: %s\n", res->path);
@@ -1463,7 +1458,7 @@
     char *link = SYNC_SYMLINK(dir) ?
             dav_get_string_property_ns(res, DAV_PROPS_NS, "link") : NULL;
     
-    LocalResource *local = ucx_map_cstr_get(db->resources, path);
+    LocalResource *local = cxMapGet(db->resources, cx_hash_key_str(path));
     
     char *local_path;
     if(link) {
@@ -1488,7 +1483,7 @@
         }
         issplit = TRUE;
     }
-    UcxList *part_updates = NULL;
+    CxList *part_updates = NULL;
     uint64_t blockcount = 0;
     char *content_hash = NULL;
        
@@ -1590,7 +1585,7 @@
             // new local resource
             local = calloc(1, sizeof(LocalResource));
             local->path = strdup(path);
-            ucx_map_cstr_put(db->resources, local->path, local);
+            cxMapPut(db->resources, cx_hash_key_str(local->path), local);
         }
         
         if(sync_store_metadata(dir, local_path, local, res)) {
@@ -1673,11 +1668,11 @@
     }
       
     // if it doesn't exist in the db, create an entry for the dir
-    LocalResource *local = ucx_map_cstr_get(db->resources, res->path);
+    LocalResource *local = cxMapGet(db->resources, cx_hash_key_str(res->path));
     if(!local) {
         local = calloc(1, sizeof(LocalResource));
         local->path = strdup(res->path);
-        ucx_map_cstr_put(db->resources, local->path, local);
+        cxMapPut(db->resources, cx_hash_key_str(local->path), local);
     }
     local->isdirectory = 1;
     
@@ -1770,14 +1765,14 @@
     int loop = 1;
     do {
         char *res_parent = util_parent_path(path);
-        char *res_name = util_resource_name(path);
+        const char *res_name = util_resource_name(path);
         
-        sstr_t new_path = ucx_sprintf(
+        cxmutstr new_path = cx_asprintf(
             "%sorig.%d.%s",
             parent,
             rev,
             res_name);
-        sstr_t new_res_path = ucx_sprintf(
+        cxmutstr new_res_path = cx_asprintf(
             "%sorig.%d.%s",
             res_parent,
             rev,
@@ -1799,7 +1794,7 @@
                     LocalResource *conflict = calloc(1, sizeof(LocalResource));
                     conflict->path = strdup(new_res_path.ptr);
                     conflict->conflict_source = strdup(path);
-                    ucx_map_cstr_put(db->conflict, new_res_path.ptr, conflict);
+                    cxMapPut(db->conflict, cx_hash_key_str(new_res_path.ptr), conflict);
                 }
             }
         }
@@ -1817,8 +1812,7 @@
     char *new_path = NULL;
     char *parent = util_parent_path(path);
     for (int i=0;;i++) {
-        sstr_t np = ucx_asprintf(
-        ucx_default_allocator(),
+        cxmutstr np = cx_asprintf(
             "%sdownload%d-%s",
             parent,
             i,
@@ -1841,8 +1835,7 @@
 void move_to_trash(SyncDirectory *dir, char *path) {
     char *new_path = NULL;
     for (int i=0;;i++) {
-        sstr_t np = ucx_asprintf(
-        ucx_default_allocator(),
+        cxmutstr np = cx_asprintf(
             "%s%d-%s",
             dir->trash,
             i,
@@ -1876,7 +1869,7 @@
 }
 
 static int res_isconflict(SyncDatabase *db, LocalResource *res) {
-    return ucx_map_cstr_get(db->conflict, res->path) ? 1 : 0;
+    return cxMapGet(db->conflict, cx_hash_key_str(res->path)) ? 1 : 0;
 }
 
 int cmd_push(CmdArgs *a, DavBool outgoing, DavBool archive) {
@@ -1917,7 +1910,7 @@
         return -1;
     }
     
-    Repository *repo = get_repository(sstr(dir->repository));
+    Repository *repo = get_repository(cx_str(dir->repository));
     if(!repo) {
         fprintf(stderr, "Unkown repository %s\n", dir->name);
         return -1;
@@ -1931,7 +1924,7 @@
     remove_deleted_conflicts(dir, db);
     
     DavSession *sn = create_session(a, ctx, repo, dir->collection);
-    ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db);
+    util_regdestr(sn->mp, db, (cx_destructor_func)destroy_db);
     if (cmd_getoption(a, "verbose")) {
         curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
         curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
@@ -1953,9 +1946,9 @@
         return -1;
     }
     
-    UcxMap *svrres = NULL;
+    CxMap *svrres = NULL;
     if(restore) {
-        svrres = ucx_map_new(1024);
+        svrres = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 1024);
         res2map(root, svrres);
     }
     
@@ -1981,7 +1974,7 @@
     
     DavBool remove_file = cmd_getoption(a, "remove") ? 1 : 0;
     
-    UcxMap *db_hashes = NULL;
+    CxMap *db_hashes = NULL;
     if(SYNC_HASHING(dir)) {
         db_hashes = create_hash_index(db);
     }
@@ -1991,23 +1984,24 @@
     int sync_conflict = 0;
     int sync_error = 0;
     
-    UcxList *ls_new = NULL;
-    UcxList *ls_modified = NULL;
-    UcxList *ls_conflict = NULL;
-    UcxList *ls_update = NULL;
-    UcxList *ls_delete = NULL;
-    UcxList *ls_move = NULL;
-    UcxList *ls_copy = NULL;
-    UcxList *ls_mkcol = NULL;
+    CxList *ls_new = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS);
+    CxList *ls_modified = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS);
+    CxList *ls_conflict = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS);
+    CxList *ls_update = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS);
+    CxList *ls_delete = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS);
+    CxList *ls_move = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS);
+    CxList *ls_copy = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS);
+    CxList *ls_mkcol = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)resource_path_cmp, CX_STORE_POINTERS);
+    
       
     // upload all changed files
     //UcxList *resources = cmd_getoption(a, "read") ?
     //        read_changes(dir, db) : local_scan(dir, db);
-    UcxList *resources = local_scan(dir, db);
-    UcxMap *resources_map = ucx_map_new(ucx_list_size(resources)+16);
-    
-    UCX_FOREACH(elm, resources) {
-        LocalResource *local_res = elm->data;
+    CxList *resources = local_scan(dir, db);
+    CxMap *resources_map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, resources->size+16);
+    
+    CxIterator iter = cxListIterator(resources);
+    cx_foreach(LocalResource *, local_res, iter) {
         // ignore all files, that are excluded by a static filter (sync.xml) 
         
         // static include/exclude filter
@@ -2015,22 +2009,25 @@
             continue;
         }
         // static tag filter
-        UCX_FOREACH(elm, dir->filter.tags) {
-            SyncTagFilter *tf = elm->data;
-            if(!localres_matches_tags(dir, local_res, tf)) {
-                continue;
+        if(dir->filter.tags) {
+            CxIterator tag_iter = cxListIterator(dir->filter.tags);
+            cx_foreach(SyncTagFilter *, tf, tag_iter) {
+                if(!localres_matches_tags(dir, local_res, tf)) {
+                    continue;
+                }
             }
         }
         
+        
         // we need a fast file lookup map later to detect deleted files
-        ucx_map_cstr_put(resources_map, local_res->path, local_res);
+        cxMapPut(resources_map, cx_hash_key_str(local_res->path), local_res);
         
         // dynamic tag filter
         if(!localres_matches_tags(dir, local_res, tagfilter)) {
             if(!remove_file) {
-                LocalResource *dbres = ucx_map_cstr_get(
+                LocalResource *dbres = cxMapGet(
                         db->resources,
-                        local_res->path);
+                        cx_hash_key_str(local_res->path));
                 if(dbres) {
                     // this makes sure the file will not be deleted later
                     dbres->keep = TRUE;
@@ -2041,7 +2038,7 @@
         
         // skip conflict backups silently
         if(res_isconflict(db, local_res)) {
-            ls_conflict = ucx_list_append(ls_conflict, local_res);
+            cxListAdd(ls_conflict, local_res);
             continue;
         }
         
@@ -2054,14 +2051,14 @@
                     restore_modified);
         if(is_changed) {
             if(local_res->isdirectory) {
-                ls_mkcol = ucx_list_append(ls_mkcol, local_res);
+                cxListAdd(ls_mkcol, local_res);
             } else if(local_res->isnew) {
-                ls_new = ucx_list_append(ls_new, local_res);
+                cxListAdd(ls_new, local_res);
             } else {
-                ls_modified = ucx_list_append(ls_modified, local_res);
+                cxListAdd(ls_modified, local_res);
             }
         } else if(local_res->metadata_updated) {
-            ls_update = ucx_list_append(ls_update, local_res);
+            cxListAdd(ls_update, local_res);
         }
         
         if(local_res->isnew) {
@@ -2077,13 +2074,9 @@
     if(SYNC_STORE_HASH(dir)) {
         // calculate hashes of all new files and check if a file
         // was moved or is a copy
-        UcxList *elm = ls_new;
-        while(elm) {
-            LocalResource *local = elm->data;
-            UcxList *prev = elm->prev;
-            UcxList *next = elm->next;
+        CxMutIterator mut_iter = cxListMutIterator(ls_new);
+        cx_foreach(LocalResource *, local, mut_iter) {
             if(local->isdirectory || local->link_target) {
-                elm = elm->next;
                 continue;
             }
             
@@ -2091,78 +2084,70 @@
             char *hash = util_file_hash(local_path);
             local->hash = hash;
             // check if a file with this hash already exists
-            LocalResource *origin = ucx_map_cstr_get(db_hashes, hash);
+            LocalResource *origin = cxMapGet(db_hashes, cx_hash_key_str(hash));
             if(origin) {
                 local->origin = local_resource_copy(origin, origin->path);
                 // the file is a copied/moved file
                 // check if the file is in the resources_map, because then
                 // it still exists
-                if(ucx_map_cstr_get(resources_map, origin->path)) {
-                    ls_copy = ucx_list_append(ls_copy, local);
+                if(cxMapGet(resources_map, cx_hash_key_str(origin->path))) {
+                    cxListAdd(ls_copy, local);
                 } else {
-                    ls_move = ucx_list_append(ls_move, local);
+                    cxListAdd(ls_move, local);
                     // put file in resources_map to prevent deletion
-                    ucx_map_cstr_put(resources_map, origin->path, local);
+                    cxMapPut(resources_map, cx_hash_key_str(origin->path), local);
                 }
                 // remove list elemend from ls_new
-                if(prev) {
-                    prev->next = next;
-                } else {
-                    ls_new = next;
-                }
-                if(next) {
-                    next->prev = prev;
-                }
+                cxIteratorFlagRemoval(mut_iter);
             }
             
             free(local_path);
-            
-            elm = next;
         }
     }
     
     // find all deleted files and cleanup the database
-    UcxMapIterator i = ucx_map_iterator(db->resources);
+    iter = cxMapIterator(db->resources);
     LocalResource *local;
-    UcxList *removed_res = NULL;
-    UCX_MAP_FOREACH(key, local, i) { 
+    CxList *removed_res = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    cx_foreach(CxMapEntry *, entry, iter) { 
+        LocalResource *local = entry->value;
         // all filtered files should be removed from the database
         if(res_matches_dir_filter(dir, local->path+1)) {
-            ucx_map_cstr_remove(db->resources, local->path);
+            cxMapRemove(db->resources, local->path);
             continue;
         }
-        UCX_FOREACH(elm, dir->filter.tags) {
-            SyncTagFilter *tf = elm->data;
+        CxIterator tag_iter = cxListIterator(dir->filter.tags);
+        cx_foreach(SyncTagFilter *, tf, tag_iter) {
             if(!localres_matches_tags(dir, local, tf)) {
-                ucx_map_cstr_remove(db->resources, local->path);
+                cxMapRemove(db->resources, local->path);
                 continue;
             }
         }
         
-        if(!ucx_map_get(resources_map, key)) {
+        if(!cxMapGet(resources_map, *entry->key)) {
             // The current LocalResource is in the database but doesn't exist
             // in the filesystem anymore. This means the file was deleted
             // and should be deleted on the server
             if(!archive) {
-                ls_delete = ucx_list_append(ls_delete, local);
+                cxListAdd(ls_delete, local);
             } else {
-                removed_res = ucx_list_prepend(removed_res, local);
+                cxListInsert(removed_res, 0, local);
             }
         }
     }
-    UCX_FOREACH(elm, removed_res) {
-        LocalResource *local = elm->data;
-        ucx_map_cstr_remove(db->resources, local->path);
-    }
-    
-    ls_new = ucx_list_sort(ls_new, (cmp_func)resource_path_cmp, NULL);
-    ls_modified = ucx_list_sort(ls_modified, (cmp_func)resource_path_cmp, NULL);
-    ls_conflict = ucx_list_sort(ls_conflict, (cmp_func)resource_path_cmp, NULL);
-    ls_update = ucx_list_sort(ls_update, (cmp_func)resource_path_cmp, NULL);
-    ls_delete = ucx_list_sort(ls_delete, (cmp_func)resource_path_cmp, NULL);
-    ls_move = ucx_list_sort(ls_move, (cmp_func)resource_path_cmp, NULL);
-    ls_copy = ucx_list_sort(ls_copy, (cmp_func)resource_path_cmp, NULL);
-    ls_mkcol = ucx_list_sort(ls_mkcol, (cmp_func)resource_path_cmp, NULL);
+    iter = cxListIterator(removed_res);
+    cx_foreach(LocalResource *, local, iter) {
+        cxMapRemove(db->resources, local->path);
+    }
+    
+    cxListSort(ls_new);
+    cxListSort(ls_modified);
+    cxListSort(ls_conflict);
+    cxListSort(ls_update);
+    cxListSort(ls_delete);
+    cxListSort(ls_move);
+    cxListSort(ls_copy);
+    cxListSort(ls_mkcol);
     
     if(outgoing) {
         print_outgoing(
@@ -2186,8 +2171,11 @@
     int error = 0;
     
     // create collections
-    for(UcxList *elm=ls_mkcol;elm && !sync_shutdown;elm=elm->next) {
-        LocalResource *local_res = elm->data;
+    iter = cxListIterator(ls_mkcol);
+    cx_foreach(LocalResource *, local_res, iter) {
+        if(sync_shutdown) {
+            break;
+        }
         
         DavResource *res = dav_resource_new(sn, local_res->path);
         if(!res) {
@@ -2227,8 +2215,9 @@
 
             // remove old db entry (if it exists)
             // and add add new entry
-            LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path);
-            ucx_map_cstr_put(db->resources, local_res->path, local_res);
+            // TODO: free??
+            LocalResource *dbres = cxMapGet(db->resources, cx_hash_key_str(local_res->path));
+            cxMapPut(db->resources, cx_hash_key_str(local_res->path), local_res);
         }
         
         dav_resource_free(res);
@@ -2240,163 +2229,176 @@
         copy = FALSE;
         ls_copy = ls_move;
     }
-    for(UcxList *elm=ls_copy;elm && !sync_shutdown;elm=elm->next) {
-        LocalResource *local = elm->data;
-        
-        int err = 0;
-        DavResource *res = dav_resource_new(sn, local->path);
-        if(dav_exists(res)) {
-            printf("conflict: %s\n", local->path);
-            local->last_modified = 0;
-            nullfree(local->etag);
-            local->etag = NULL;
-            nullfree(local->hash);
-            local->hash = NULL;
-            local->skipped = TRUE;
-            sync_conflict++;
-        } else {
-            DavResource *origin_res = dav_resource_new(sn, local->origin->path);
-            int origin_changed = remote_resource_is_changed(
-                    sn,
-                    dir,
-                    db,
-                    origin_res,
-                    local->origin,
-                    NULL);
-            if(origin_changed) {
-                // upload with put
-                ls_modified = ucx_list_prepend(ls_modified, local);               
+    iter = cxListIterator(ls_copy);
+    for(int i=0;i<2;i++) {
+        cx_foreach(LocalResource*, local, iter) {
+            if(sync_shutdown) {
+                break;
+            }
+
+            int err = 0;
+            DavResource *res = dav_resource_new(sn, local->path);
+            if(dav_exists(res)) {
+                printf("conflict: %s\n", local->path);
+                local->last_modified = 0;
+                nullfree(local->etag);
+                local->etag = NULL;
+                nullfree(local->hash);
+                local->hash = NULL;
+                local->skipped = TRUE;
+                sync_conflict++;
             } else {
-                printf("%s: %s -> %s\n", copy ? "copy":"move", local->origin->path, local->path);
-                err = sync_move_remote_resource(
+                DavResource *origin_res = dav_resource_new(sn, local->origin->path);
+                int origin_changed = remote_resource_is_changed(
+                        sn,
                         dir,
                         db,
                         origin_res,
-                        local,
-                        copy,
-                        &sync_success);
+                        local->origin,
+                        NULL);
+                if(origin_changed) {
+                    // upload with put
+                    cxListInsert(ls_modified, 0, local);               
+                } else {
+                    printf("%s: %s -> %s\n", copy ? "copy":"move", local->origin->path, local->path);
+                    err = sync_move_remote_resource(
+                            dir,
+                            db,
+                            origin_res,
+                            local,
+                            copy,
+                            &sync_success);
+                }
             }
-        }
-        
-        if(err) {
-            sync_error++;
-            print_resource_error(sn, res->path);
-            ret = -1;
-            error = 1;
-        } else {
-            LocalResource *dbres = ucx_map_cstr_remove(db->resources, local->path);
-            ucx_map_cstr_put(db->resources, local->path, local);
-        }
-        
-        if(copy && !elm->next) {
-            // finished copy, begin move
-            elm->next = ls_move;
-            copy = FALSE;
-        }
+
+            if(err) {
+                sync_error++;
+                print_resource_error(sn, res->path);
+                ret = -1;
+                error = 1;
+            } else {
+                LocalResource *dbres = cxMapGet(db->resources, cx_hash_key_str(local->path));
+                cxMapPut(db->resources, cx_hash_key_str(local->path), local);
+            }
+        }
+        copy = FALSE;
+        iter = cxListIterator(ls_move);
     }
     
     // upload changed files 
-    ls_modified = ucx_list_concat(ls_new, ls_modified);
-    
-    for(UcxList *elm=ls_modified;elm && !sync_shutdown;elm=elm->next) {
-        LocalResource *local_res = elm->data;
-        int err = 0;
-        
-        DavResource *res = dav_resource_new(sn, local_res->path);
-        if(!res) {
-            print_resource_error(sn, local_res->path);
-            ret = -1;
-            sync_error++;
-        } else {
-            DavBool equal = FALSE;
-            DavBool res_conflict = FALSE;
-            int changed = remote_resource_is_changed(sn, dir, db, res, local_res, &equal);
-            if(equal) {
-                char *etag = dav_get_string_property(res, "D:getetag");
-                if(local_res->metadata_updated) {
-                    ls_update = ucx_list_prepend(ls_update, local_res);
-                } else if(etag) {
-                    // update etag in db
-                    if(local_res->etag) {
-                        free(local_res->etag);
+    //ls_modified = ucx_list_concat(ls_new, ls_modified);
+    iter = cxListIterator(ls_new);
+    for(int i=0;i<2;i++) {
+        cx_foreach(LocalResource*, local_res, iter) {
+            if(sync_shutdown) {
+                break;
+            }
+            
+            int err = 0;
+
+            DavResource *res = dav_resource_new(sn, local_res->path);
+            if(!res) {
+                print_resource_error(sn, local_res->path);
+                ret = -1;
+                sync_error++;
+            } else {
+                DavBool equal = FALSE;
+                DavBool res_conflict = FALSE;
+                int changed = remote_resource_is_changed(sn, dir, db, res, local_res, &equal);
+                if(equal) {
+                    char *etag = dav_get_string_property(res, "D:getetag");
+                    if(local_res->metadata_updated) {
+                        cxListInsert(ls_update, 0, local_res);
+                    } else if(etag) {
+                        // update etag in db
+                        if(local_res->etag) {
+                            free(local_res->etag);
+                        }
+                        local_res->etag = strdup(etag);
                     }
-                    local_res->etag = strdup(etag);
+                } else if(cdt && changed) {
+                    printf("conflict: %s\n", local_res->path);
+                    local_res->last_modified = 0;
+                    nullfree(local_res->etag);
+                    local_res->etag = NULL;
+                    nullfree(local_res->hash);
+                    local_res->hash = NULL;
+                    local_res->skipped = TRUE;
+                    sync_conflict++;
+
+                    if(local_res->link_target) {
+                        free(local_res->link_target);
+                        local_res->link_target = local_res->link_target_db;
+                        local_res->link_target_db = NULL;
+                    }
+
+                    res_conflict = TRUE;
+                } else {
+                    if(local_res->link_target) {
+                        printf(
+                                "link: %s -> %s\n",
+                                local_res->path,
+                                local_res->link_target);
+                    } else {
+                        printf("put: %s\n", local_res->path);
+                    }
+                    if(sync_put_resource(dir, res, local_res, &sync_success)) {
+                        sync_error++;
+                        print_resource_error(sn, res->path);
+                        ret = -1;
+                        error = 1;
+
+                        err = 1;
+                    }
                 }
-            } else if(cdt && changed) {
-                printf("conflict: %s\n", local_res->path);
-                local_res->last_modified = 0;
-                nullfree(local_res->etag);
-                local_res->etag = NULL;
-                nullfree(local_res->hash);
-                local_res->hash = NULL;
-                local_res->skipped = TRUE;
-                sync_conflict++;
-                
-                if(local_res->link_target) {
-                    free(local_res->link_target);
-                    local_res->link_target = local_res->link_target_db;
-                    local_res->link_target_db = NULL;
-                }
-                
-                res_conflict = TRUE;
-            } else {
-                if(local_res->link_target) {
-                    printf(
-                            "link: %s -> %s\n",
-                            local_res->path,
-                            local_res->link_target);
-                } else {
-                    printf("put: %s\n", local_res->path);
-                }
-                if(sync_put_resource(dir, res, local_res, &sync_success)) {
-                    sync_error++;
-                    print_resource_error(sn, res->path);
-                    ret = -1;
-                    error = 1;
-                    
-                    err = 1;
+
+                if(!err) {
+                    LocalResource *dbres = cxMapRemoveAndGet(db->resources, cx_hash_key_str(local_res->path));
+                    // in case of a conflict, don't store the resource
+                    // in the db, if it is new
+                    if(!res_conflict || dbres) {
+                        cxMapPut(db->resources, cx_hash_key_str(local_res->path), local_res);
+                    }
                 }
             }
-            
-            if(!err) {
-                LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path);
-                // in case of a conflict, don't store the resource
-                // in the db, if it is new
-                if(!res_conflict || dbres) {
-                    ucx_map_cstr_put(db->resources, local_res->path, local_res);
-                }
-            }
-        }
-        
-        dav_resource_free(res);
+
+            dav_resource_free(res);
+        }
+        iter = cxListIterator(ls_modified);
     }
     
     // metadata updates
-    for(UcxList *elm=ls_update;elm && !sync_shutdown;elm=elm->next) {
-        LocalResource *local_res = elm->data;
+    iter = cxListIterator(ls_update);
+    cx_foreach(LocalResource *, local_res, iter) {
+        if(sync_shutdown) {
+            break;
+        }
         
         DavResource *res = dav_resource_new(sn, local_res->path);       
         if(local_res->metadata_updated) {
             printf("update: %s\n", local_res->path);
             if(!sync_update_metadata(dir, sn, res, local_res)) {
-                LocalResource *dbres = ucx_map_cstr_remove(db->resources, local_res->path);
-                ucx_map_cstr_put(db->resources, local_res->path, local_res);
+                LocalResource *dbres = cxMapGet(db->resources, cx_hash_key_str(local_res->path));
+                cxMapPut(db->resources, cx_hash_key_str(local_res->path), local_res);
             }
         }
     }  
 
     // delete all removed files
-    ls_delete = ucx_list_sort(ls_delete, (cmp_func)resource_pathlen_cmp, NULL);
-    
-    UcxList *cols = NULL;
-    UcxList **col_list = &cols;
-    UcxList *deletelist = ls_delete;
+    cxListSort(ls_delete);
+    
+    CxList *cols = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    CxList *col_list = cols;
+    CxList *deletelist = ls_delete;
     for(int i=0;i<2;i++) {
         // the first iteration deletes everything from ls_delete except
         // all collections, which are stored in cols
         // in the second run all collections will be deleted
-        for(UcxList *elm=deletelist;elm && !sync_shutdown;elm=elm->next) {
-            LocalResource *local = elm->data;
+        iter = cxListIterator(deletelist);
+        cx_foreach(LocalResource *, local, iter) {
+            if(sync_shutdown) {
+                break;
+            }
             if(local->keep) {
                 continue;
             }
@@ -2407,7 +2409,7 @@
                     break;
                 }
             } else {
-                LocalResource *dbres = ucx_map_cstr_remove(db->resources, local->path);
+                LocalResource *dbres = cxMapRemoveAndGet(db->resources, cx_hash_key_str(local->path));
                 //local_resource_free(dbres);
             }
         }
@@ -2477,14 +2479,14 @@
     }
     
     SyncDirectory *dir = NULL;
-    UcxMap *files = NULL;
+    CxMap *files = NULL;
     if(syncdir) {
         dir = scfg_get_dir(syncdir);
     }
     
     LocalResource nres;
     if(a->argc > 0) {
-        files = ucx_map_new(a->argc+8);
+        files = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, a->argc+8);
         // get all specified files and check the syncdir
         SyncDirectory *sd = NULL;
         for(int i=0;i<a->argc;i++) {
@@ -2503,7 +2505,7 @@
                 }
             }
             
-            ucx_map_cstr_put(files, f.path, &nres);
+            cxMapPut(files, cx_hash_key_str(f.path), &nres);
         }
         dir = sd;
     }
@@ -2536,16 +2538,16 @@
     }
     remove_deleted_conflicts(dir, db);
     
-    UcxList *modified = NULL;
-    UcxList *deleted = NULL;
+    CxList *modified = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)localres_cmp_path, CX_STORE_POINTERS);
+    CxList *deleted = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)localres_cmp_path, CX_STORE_POINTERS);
     
     // iterate over all db resources and check if any resource is
     // modified or deleted
-    UcxMapIterator i = ucx_map_iterator(files ? files : db->resources);
-    LocalResource *resource;
-    UCX_MAP_FOREACH(key, resource, i) {
+    CxIterator i = cxMapIterator(files ? files : db->resources);
+    cx_foreach(CxMapEntry *, entry, i) {
+        LocalResource *resource = entry->value;
         if(resource == &nres) {
-            resource = ucx_map_get(db->resources, key);
+            resource = cxMapGet(db->resources, *entry->key);
             if(!resource) {
                 continue;
             }
@@ -2556,7 +2558,7 @@
         if(sys_stat(file_path, &s)) {
             if(errno == ENOENT) {
                 if(restore_removed) {
-                    deleted = ucx_list_prepend(deleted, resource);
+                    cxListAdd(deleted, resource);
                 }
             } else {
                 fprintf(stderr, "Cannot stat file: %s\n", file_path);
@@ -2564,11 +2566,11 @@
             }
         } else {
             if(files) {
-                modified = ucx_list_prepend(modified, resource);
+                cxListAdd(modified, resource);
             } else if(!resource->isdirectory && !S_ISDIR(s.st_mode)) {
                 if(resource->last_modified != s.st_mtime || resource->size != s.st_size) {
                     if(restore_modified) {
-                        modified = ucx_list_prepend(modified, resource);
+                        cxListAdd(modified, resource);
                     }
                 }
             }
@@ -2578,19 +2580,19 @@
     }  
     
     if(files) {
-        ucx_map_free(files);
+        cxMapDestroy(files);
     }
     
     int ret = 0;
     
     // create DavSession
-    Repository *repo = get_repository(sstr(dir->repository));
+    Repository *repo = get_repository(cx_str(dir->repository));
     if(!repo) {
         fprintf(stderr, "Unkown repository %s\n", dir->name);
         return -1;
     }
     DavSession *sn = create_session(a, ctx, repo, dir->collection);
-    ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db);
+    util_regdestr(sn->mp, db, (cx_destructor_func)destroy_db);
     if (cmd_getoption(a, "verbose")) {
         curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
         curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
@@ -2619,70 +2621,74 @@
     int sync_success = 0;
     int sync_error = 0;
     
-    UcxList *resources = ucx_list_concat(modified, deleted);
-    resources = ucx_list_sort(resources, (cmp_func)localres_cmp_path, NULL);
-    
-    UCX_FOREACH(elm, resources) {
-        LocalResource *resource = elm->data;
-        
-        DavResource *res = dav_get(sn, resource->path, "D:getetag,idav:status,idav:version-collection,idav:split,`idav:content-hash`,idavprops:tags,idavprops:finfo,idavprops:xattributes,idavprops:link");
-        if(!res) {
-            printf("skip: %s\n", resource->path);
-            continue;
-        }
-        char *status = dav_get_string_property(res, "idav:status");
-        if(status && !strcmp(status, "broken")) {
-            fprintf(stderr, "Resource %s broken\n", res->path);
-            continue;
-        }
-        
-        DavResource *vres = NULL;
-        DavBool update_local_entry = TRUE;
-        if(version) {
-            if(dir->versioning->type == VERSIONING_SIMPLE) {
-                vres = versioning_simple_find(res, version);
-            } else if(dir->versioning->type == VERSIONING_DELTAV) {
-                vres = versioning_deltav_find(res, version);
+    // TODO: old code sorted both modified and deleted, is this necessary?
+    //UcxList *resources = ucx_list_concat(modified, deleted);
+    //resources = ucx_list_sort(resources, (cmp_func)localres_cmp_path, NULL);
+    cxListSort(deleted);
+    
+    CxIterator iter = cxListIterator(modified);
+    for(int i=0;i<2;i++) {
+        cx_foreach(LocalResource *, resource, iter) {
+            DavResource *res = dav_get(sn, resource->path, "D:getetag,idav:status,idav:version-collection,idav:split,`idav:content-hash`,idavprops:tags,idavprops:finfo,idavprops:xattributes,idavprops:link");
+            if(!res) {
+                printf("skip: %s\n", resource->path);
+                continue;
             }
-            if(!vres) {
-                fprintf(stderr, "Cannot find  specified version for resource %s\n", res->path);
-                ret = 1;
-                break;
+            char *status = dav_get_string_property(res, "idav:status");
+            if(status && !strcmp(status, "broken")) {
+                fprintf(stderr, "Resource %s broken\n", res->path);
+                continue;
             }
-            
-            // By restoring an old version of a file, the local dir is not
-            // in sync with the server anymore. Mark this file to change
-            // the metadata later, to make sure, the file will be detected
-            // as locally modified, on the next push/pull
-            update_local_entry = FALSE;
-        } else {
-            vres = res;
-        }
-        
-        // download the resource
-        if(!sync_shutdown) {
-            if(resource->isdirectory) {
-                char *local_path = create_local_path(dir, res->path);
-                if(sys_mkdir(local_path) && errno != EEXIST) {
-                    fprintf(stderr,
-                            "Cannot create directory %s: %s",
-                            local_path, strerror(errno));
+
+            DavResource *vres = NULL;
+            DavBool update_local_entry = TRUE;
+            if(version) {
+                if(dir->versioning->type == VERSIONING_SIMPLE) {
+                    vres = versioning_simple_find(res, version);
+                } else if(dir->versioning->type == VERSIONING_DELTAV) {
+                    vres = versioning_deltav_find(res, version);
+                }
+                if(!vres) {
+                    fprintf(stderr, "Cannot find  specified version for resource %s\n", res->path);
+                    ret = 1;
+                    break;
                 }
-                free(local_path);
+
+                // By restoring an old version of a file, the local dir is not
+                // in sync with the server anymore. Mark this file to change
+                // the metadata later, to make sure, the file will be detected
+                // as locally modified, on the next push/pull
+                update_local_entry = FALSE;
             } else {
-                if(sync_get_resource(a, dir, res->path, vres, db, update_local_entry, &sync_success)) {
-                    fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path);
-                    sync_error++;
-                } else if(!update_local_entry) {
-                    LocalResource *lr = ucx_map_cstr_get(db->resources, res->path);
-                    if(lr) {
-                        lr->last_modified = 0;
-                        nullfree(lr->hash);
-                        lr->hash = NULL;
-                    } // else should not happen
+                vres = res;
+            }
+
+            // download the resource
+            if(!sync_shutdown) {
+                if(resource->isdirectory) {
+                    char *local_path = create_local_path(dir, res->path);
+                    if(sys_mkdir(local_path) && errno != EEXIST) {
+                        fprintf(stderr,
+                                "Cannot create directory %s: %s",
+                                local_path, strerror(errno));
+                    }
+                    free(local_path);
+                } else {
+                    if(sync_get_resource(a, dir, res->path, vres, db, update_local_entry, &sync_success)) {
+                        fprintf(stderr, "sync_get_resource failed for resource: %s\n", res->path);
+                        sync_error++;
+                    } else if(!update_local_entry) {
+                        LocalResource *lr = cxMapGet(db->resources, cx_hash_key_str(res->path));
+                        if(lr) {
+                            lr->last_modified = 0;
+                            nullfree(lr->hash);
+                            lr->hash = NULL;
+                        } // else should not happen
+                    }
                 }
             }
         }
+        iter = cxListIterator(deleted);
     }
     
     // unlock repository
@@ -2730,25 +2736,25 @@
 
 void print_outgoing(
         CmdArgs *args,
-        UcxList *ls_new,
-        UcxList *ls_modified,
-        UcxList *ls_conflict,
-        UcxList *ls_update,
-        UcxList *ls_delete,
-        UcxList *ls_move,
-        UcxList *ls_copy,
-        UcxList *ls_mkcol)
+        CxList *ls_new,
+        CxList *ls_modified,
+        CxList *ls_conflict,
+        CxList *ls_update,
+        CxList *ls_delete,
+        CxList *ls_move,
+        CxList *ls_copy,
+        CxList *ls_mkcol)
 {
     int64_t total_size = 0;
     
-    size_t len_new = ucx_list_size(ls_new);
-    size_t len_mod = ucx_list_size(ls_modified);
-    size_t len_cnf = ucx_list_size(ls_conflict);
-    size_t len_upd = ucx_list_size(ls_update);
-    size_t len_del = ucx_list_size(ls_delete);
-    size_t len_mov = ucx_list_size(ls_move);
-    size_t len_cpy = ucx_list_size(ls_copy);
-    size_t len_mkc = ucx_list_size(ls_mkcol);
+    size_t len_new = ls_new->size;
+    size_t len_mod = ls_modified->size;
+    size_t len_cnf = ls_conflict->size;
+    size_t len_upd = ls_update->size;
+    size_t len_del = ls_delete->size;
+    size_t len_mov = ls_move->size;
+    size_t len_cpy = ls_copy->size;
+    size_t len_mkc = ls_mkcol->size;
     
     size_t total = len_new + len_mod + len_cnf + len_upd + len_del + len_mov + len_cpy + len_mkc;
     if(total == 0) {
@@ -2761,32 +2767,32 @@
        
     if(ls_mkcol) {
         printf("Directories:\n");
-        UCX_FOREACH(elm, ls_mkcol) {
-            LocalResource *res = elm->data;
+        CxIterator i = cxListIterator(ls_mkcol);
+        cx_foreach(LocalResource *, res, i) {
             printf(" %-49s\n", res->path+1);
             total_size += res->size;
         }
     }
     if(ls_new) {
         printf("New:\n");
-        UCX_FOREACH(elm, ls_new) {
-            LocalResource *res = elm->data;
-            print_outgoging_file(elm->data);
+        CxIterator i = cxListIterator(ls_new);
+        cx_foreach(LocalResource *, res, i) {
+            print_outgoging_file(res);
             total_size += res->size;
         }
     }
     if(ls_modified) {
         printf("Modified:\n");
-        UCX_FOREACH(elm, ls_modified) {
-            LocalResource *res = elm->data;
-            print_outgoging_file(elm->data);
+        CxIterator i = cxListIterator(ls_modified);
+        cx_foreach(LocalResource *, res, i) {
+            print_outgoging_file(res);
             total_size += res->size;
         }
     }
     if(ls_update) {
         printf("Update:\n");
-        UCX_FOREACH(elm, ls_update) {
-            LocalResource *res = elm->data;
+        CxIterator i = cxListIterator(ls_update);
+        cx_foreach(LocalResource *, res, i) {
             char *lastmodified = util_date_str(res->last_modified);
             printf(" %-49s  %12s\n", res->path+1, lastmodified);
             free(lastmodified);
@@ -2794,29 +2800,29 @@
     }
     if(ls_delete) {
         printf("Delete:\n");
-        UCX_FOREACH(elm, ls_delete) {
-            LocalResource *res = elm->data;
+        CxIterator i = cxListIterator(ls_delete);
+        cx_foreach(LocalResource *, res, i) {
             printf(" %s\n", res->path+1);
         }
     }
     if(ls_copy) {
         printf("Copy:\n");
-        UCX_FOREACH(elm, ls_copy) {
-            LocalResource *res = elm->data;
+        CxIterator i = cxListIterator(ls_copy);
+        cx_foreach(LocalResource *, res, i) {
             printf("%s -> %s\n", res->origin->path+1, res->path);
         }
     }
     if(ls_move) {
         printf("Move:\n");
-        UCX_FOREACH(elm, ls_move) {
-            LocalResource *res = elm->data;
+        CxIterator i = cxListIterator(ls_move);
+        cx_foreach(LocalResource *, res, i) {
             printf("%s -> %s\n", res->origin->path+1, res->path);
         }
     }
     if(ls_conflict) {
         printf("Conflict\n");
-        UCX_FOREACH(elm, ls_conflict) {
-            LocalResource *res = elm->data;
+        CxIterator i = cxListIterator(ls_conflict);
+        cx_foreach(LocalResource *, res, i) {
             printf(" %s\n", res->path+1);
         }
     }
@@ -2836,17 +2842,18 @@
     free(total_size_str);
 }
 
-UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db) {
-    UcxList *resources = NULL;
+CxList* local_scan(SyncDirectory *dir, SyncDatabase *db) {
+    CxList *resources = cxLinkedListCreateSimple(CX_STORE_POINTERS);
     
     char *path = strdup("/");
-    UcxList *stack = ucx_list_prepend(NULL, path);
-    while(stack) {
+    CxList *stack = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    cxListInsert(stack, 0, path);
+    while(stack->size > 0) {
         // get a directory path from the stack and read all entries
         // if an entry is a directory, put it on the stack
         
-        char *p = stack->data;
-        stack = ucx_list_remove(stack, stack);
+        char *p = cxListAt(stack, 0);
+        cxListRemove(stack, 0);
         char *local_path = create_local_path(dir, p);
         SYS_DIR local_dir = sys_opendir(local_path);
         
@@ -2864,11 +2871,11 @@
                 LocalResource *res = local_resource_new(dir, db, new_path);
                 if(res) {
                     if(res->isdirectory) {
-                        resources = ucx_list_append(resources, res);
-                        stack = ucx_list_prepend(stack, new_path);
+                        cxListAdd(resources, res);
+                        cxListInsert(stack, 0, new_path);
                         free_new_path = FALSE;
                     } else {
-                        resources = ucx_list_append(resources, res);
+                        cxListAdd(resources, res);
                     }
                 }
                 if(free_new_path) {
@@ -2885,50 +2892,6 @@
     return resources;
 }
 
-UcxList* read_changes(SyncDirectory *dir, SyncDatabase *db) {
-    UcxProperties *parser = ucx_properties_new();
-    parser->delimiter = ':';
-    
-    UcxList *resources = NULL;
-    sstr_t name;
-    sstr_t value;
-    
-    char buf[STDIN_BUF_SIZE];
-    size_t r;
-    while(!feof(stdin)) {
-        r = fread(buf, 1, STDIN_BUF_SIZE, stdin);
-        ucx_properties_fill(parser, buf, r);
-        while(ucx_properties_next(parser, &name, &value)) {
-            if(value.length == 0) {
-                fprintf(stderr, "Wrong input\n");
-                continue;
-            }
-            if(value.ptr[0] == '"'
-                    && value.length > 2
-                    && value.ptr[value.length - 1] == '"')
-            {
-                value.ptr[value.length - 1] = '\0';
-                value.ptr++;
-                value.length -= 2;
-            }
-            value = sstrdup(value);
-            
-            if(!sstrcmp(name, S("put"))) {
-                LocalResource *res = local_resource_new(dir, db, value.ptr);
-                if(res) {
-                    resources = ucx_list_append(resources, res);
-                }
-            } else if(!sstrcmp(name, S("remove"))) {
-                ucx_map_sstr_remove(db->resources, value);
-            }
-            
-            free(value.ptr);
-        }
-    }
-    ucx_properties_free(parser);    
-    
-    return resources;
-}
 
 LocalResource* local_resource_new(SyncDirectory *dir, SyncDatabase *db, char *path) {
     char *file_path = create_local_path(dir, path);
@@ -3051,11 +3014,11 @@
         SyncDirectory *dir,
         SyncDatabase *db,
         LocalResource *res,
-        UcxMap *svrres,
+        CxMap *svrres,
         DavBool restore_removed,
         DavBool restore_modified)
 {
-    LocalResource *db_res = ucx_map_cstr_get(db->resources, res->path);
+    LocalResource *db_res = cxMapGet(db->resources, cx_hash_key_str(res->path));
     res->tags_updated = 0;
     if(db_res) { 
         // copy some metadata from db_res, that localscan does not deliver
@@ -3088,7 +3051,7 @@
         
         // check if the file must be restored on the server
         if(svrres) {
-            DavResource *remote = ucx_map_cstr_get(svrres, res->path);
+            DavResource *remote = cxMapGet(svrres, cx_hash_key_str(res->path));
             if(restore_removed && !remote) {
                 return 1;
             }
@@ -3110,7 +3073,7 @@
             res->tags_updated = 1;
             res->metadata_updated = 1;
         } else if(dir->tagconfig && dir->tagconfig->detect_changes ) {
-            UcxBuffer *tags = sync_get_file_tag_data(dir, res);
+            CxBuffer *tags = sync_get_file_tag_data(dir, res);
             if(tags) {
                 if(db_res->tags_hash) {
                     char *hash = dav_create_hash(tags->space, tags->size);
@@ -3268,9 +3231,9 @@
             // the resource is on the server and the client has no etag
             ret = 1;
         } else if(etag) {
-            sstr_t e = sstr(etag);
-            if(sstrprefix(e, S("W/"))) {
-                e = sstrsubs(e, 2);
+            cxstring e = cx_str(etag);
+            if(cx_strprefix(e, CX_STR("W/"))) {
+                e = cx_strsubs(e, 2);
             }
             if(strcmp(e.ptr, res->etag)) {
                 ret = 1;
@@ -3306,11 +3269,11 @@
         return;
     }
     
-    scstr_t e = scstr(etag);
-    if(sstrprefix(e, S("W/"))) {
-        e = scstrsubs(e, 2);
-    }
-    local->etag = sstrdup(e).ptr;
+    cxstring e = cx_str(etag);
+    if(cx_strprefix(e, CX_STR("W/"))) {
+        e = cx_strsubs(e, 2);
+    }
+    local->etag = cx_strdup(e).ptr;
 }
 
 char* resource_local_path(DavResource *res) {
@@ -3334,9 +3297,9 @@
         return 0;
     } else if(local->blocksize > 0) {
         local_blocksize = (size_t)local->blocksize;
-    } else {
-        UCX_FOREACH(elm, dir->splitconfig) {
-            SplitConfig *sc = elm->data;
+    } else if(dir->splitconfig) {
+        CxIterator i = cxListIterator(dir->splitconfig);
+        cx_foreach(SplitConfig *, sc, i) {
             if(sc->filter) {
                 if(res_matches_filter(sc->filter, local->path)) {
                     continue;
@@ -3462,38 +3425,6 @@
     return ret;
 }
 
-int sync_tags_equal(UcxList *tags1, UcxList *tags2) {
-    if(!tags1) {
-        return tags2 ? 0 : 1;
-    }
-    if(!tags2) {
-        return tags1 ? 0 : 1;
-    }
-    
-    UcxMap *map1 = ucx_map_new(32);
-    UCX_FOREACH(elm, tags1) {
-        DavTag *t = elm->data;
-        ucx_map_cstr_put(map1, t->name, t);
-    }
-    
-    int equal = 1;
-    int i = 0;
-    UCX_FOREACH(elm, tags2) {
-        DavTag *t = elm->data;
-        if(!ucx_map_cstr_get(map1, t->name)) {
-            equal = 0;
-            break;
-        }
-        i++;
-    }
-    
-    if(i != map1->count) {
-        equal = 0;
-    }
-    ucx_map_free(map1);
-    return equal;
-}
-
 int sync_store_metadata(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res) {
     int ret = 0;
     
@@ -3545,12 +3476,12 @@
     // create a map of all currently available local attributes
     ssize_t nelm = 0;
     char **list = xattr_list(path, &nelm);
-    UcxMap *current_xattr = NULL;
+    CxMap *current_xattr = NULL;
     if(nelm > 0) {
-        current_xattr = ucx_map_new(nelm + 8);
+        current_xattr = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, nelm + 8);
         for(int i=0;i<nelm;i++) {
             // use the xattr name as key and store any value
-            ucx_map_cstr_put(current_xattr, list[i], list[i]);
+            cxMapPut(current_xattr, cx_hash_key_str(list[i]), list[i]);
         }
     }
     if(list) {
@@ -3560,7 +3491,7 @@
     // store extended attributes
     size_t nattr = xattr ? xattr->nattr : 0;
     for(int i=0;i<nattr;i++) {
-        sstr_t value = xattr->values[i];
+        cxmutstr value = xattr->values[i];
         if(xattr_set(path, xattr->names[i], value.ptr, value.length)) {
             fprintf(stderr,
                     "Cannot store xattr '%s' for file: %s\n",
@@ -3572,7 +3503,7 @@
             // to detect which xattributes are removed, we remove all new
             // attributes from the map and all remaining attributes must
             // be removed with xattr_remove
-            char *value = ucx_map_cstr_remove(current_xattr, xattr->names[i]);
+            char *value = cxMapRemoveAndGet(current_xattr, cx_hash_key_str(xattr->names[i]));
             if(value) {
                 free(value);
             }
@@ -3580,13 +3511,13 @@
     }
     
     if(current_xattr) {
-        UcxMapIterator i = ucx_map_iterator(current_xattr);
+        CxIterator i = cxMapIteratorValues(current_xattr);
         char *value = NULL;
-        UCX_MAP_FOREACH(key, value, i) {
+        cx_foreach(char *, value, i) {
             (void)xattr_remove(path, value); // don't print error
             free(value);
         }
-        ucx_map_free(current_xattr);
+        cxMapDestroy(current_xattr);
     }
     
     return 0;
@@ -3598,7 +3529,7 @@
     }
     
     char *remote_hash = NULL;
-    UcxList *tags = NULL;
+    CxList *tags = NULL;
     if(dir->tagconfig) {
         DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags");
         if(tagsprop) {
@@ -3609,7 +3540,7 @@
     
     DavBool store_tags = FALSE;
     DavBool tags_changed = FALSE;
-    UcxList *local_tags = sync_get_file_tags(dir, local, &tags_changed, NULL);
+    CxList *local_tags = sync_get_file_tags(dir, local, &tags_changed, NULL);
     if(tags_changed) {
         switch(dir->tagconfig->conflict) {
             case TAG_NO_CONFLICT: {
@@ -3626,7 +3557,7 @@
                 break;
             }
             case TAG_MERGE: {
-                UcxList *new_tags = merge_tags(local_tags, tags);
+                CxList *new_tags = merge_tags(local_tags, tags);
                 // TODO: free tags and local_tags
                 tags = new_tags;
                 store_tags = TRUE;   
@@ -3636,7 +3567,7 @@
             }
         }
     } else {
-        if(!sync_tags_equal(tags, local_tags)) {
+        if(!compare_taglists(tags, local_tags)) {
             store_tags = TRUE;
         }
         // TODO: free local_tags
@@ -3661,10 +3592,10 @@
     return ret;
 }
 
-int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, UcxList *tags) {
+int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, CxList *tags) {
     int ret = 0;
     if(dir->tagconfig->store == TAG_STORE_XATTR) {
-        UcxBuffer *data = NULL;
+        CxBuffer *data = NULL;
         if(tags) {
             switch(dir->tagconfig->local_format) {
                 default: break;
@@ -3704,7 +3635,7 @@
                 } else {
                     free(data_hash);
                 }
-                ucx_buffer_free(data);
+                cxBufferFree(data);
             } else {
                 ret = -1;
             }
@@ -3724,14 +3655,14 @@
     return ret;
 }
 
-UcxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res) {
+CxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res) {
     if(!dir->tagconfig) {
         return NULL;
     }
     if(res->cached_tags) {
         return res->cached_tags;
     }
-    UcxBuffer *buf = NULL;
+    CxBuffer *buf = NULL;
     if(dir->tagconfig->store == TAG_STORE_XATTR) {
         ssize_t tag_length = 0;
         char *local_path = create_local_path(dir, local_resource_path(res));
@@ -3742,7 +3673,7 @@
         free(local_path);
         
         if(tag_length > 0) {
-            buf = ucx_buffer_new(tag_data, (size_t)tag_length, UCX_BUFFER_AUTOFREE);
+            buf = cxBufferCreate(tag_data, (size_t)tag_length, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS);
             buf->size = (size_t)tag_length;
         }
     }
@@ -3750,10 +3681,10 @@
     return buf;
 }
 
-UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash) {
+CxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash) {
     if(changed) *changed = FALSE;
     
-    UcxList *tags = NULL;
+    CxList *tags = NULL;
     
     if(!res) {
         return NULL;
@@ -3766,7 +3697,7 @@
         *changed = TRUE;
     }
     if(dir->tagconfig->store == TAG_STORE_XATTR) {
-        UcxBuffer *tag_buf = res->cached_tags ?
+        CxBuffer *tag_buf = res->cached_tags ?
                 res->cached_tags :
                 sync_get_file_tag_data(dir, res);
         
@@ -4011,7 +3942,7 @@
 // this macro is only a workaround for a netbeans bug    
 #define LOG10 log10
 
-static UcxList* upload_parts(
+static CxList* upload_parts(
         LocalResource *local,
         DavResource *res,
         FILE *in,
@@ -4067,7 +3998,8 @@
         return NULL;
     }
     
-    UcxMap *updated_parts_map = ucx_map_new((nblocks/2)+64);
+    CxMap *updated_parts_map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, (nblocks/2)+64);
+    updated_parts_map->simple_destructor = (cx_destructor_func)filepart_free;
     
     int blockindex = 0;
     int uploaded_parts = 0;
@@ -4121,7 +4053,7 @@
                 FilePart *f = calloc(1, sizeof(FilePart));
                 f->block = blockindex;
                 f->hash = block_hash;
-                ucx_map_cstr_put(updated_parts_map, name, f);
+                cxMapPut(updated_parts_map, cx_hash_key_str(name), f);
             }
             dav_resource_free(part);
             uploaded_parts++;
@@ -4138,8 +4070,7 @@
     
     free(buffer);
     if(*err) {
-        ucx_map_free_content(updated_parts_map, (ucx_destructor)filepart_free);
-        ucx_map_free(updated_parts_map);
+        cxMapDestroy(updated_parts_map);
         return NULL;
     }
     
@@ -4151,18 +4082,17 @@
     
     // get etags from uploaded resources
     // also delete everything, that is not part of the file
-    UcxList *updated_parts = NULL;
+    CxList *updated_parts = cxLinkedListCreateSimple(CX_STORE_POINTERS);
     DavResource *parts = dav_query(res->session, "select D:getetag from %s order by name", res->path);
     if(!parts) {
         print_resource_error(res->session, parts->path);
         *err = 1;
-        ucx_map_free_content(updated_parts_map, (ucx_destructor)filepart_free);
-        ucx_map_free(updated_parts_map);
+        cxMapDestroy(updated_parts_map);
         return NULL;
     }
     DavResource *part = parts->children;
     while(part) {
-        FilePart *fp = ucx_map_cstr_remove(updated_parts_map, part->name);
+        FilePart *fp = cxMapRemoveAndGet(updated_parts_map, cx_hash_key_str(part->name));
         // every part we uploaded is in the map
         // if we get parts that are not in the map, someone else uploaded it
         if(fp) {
@@ -4173,7 +4103,7 @@
                 }
 
                 fp->etag = strdup(etag);
-                updated_parts = ucx_list_append(updated_parts, fp);
+                cxListAdd(updated_parts, fp);
             } // else { wtf is wrong with this resource }
         } else {
             uint64_t name_partnum = 0;
@@ -4200,14 +4130,13 @@
     }
     dav_resource_free_all(parts);
         
-    ucx_map_free_content(updated_parts_map, (ucx_destructor)filepart_free);
-    ucx_map_free(updated_parts_map);
+    cxMapDestroy(updated_parts_map);
     
     *err = 0;
     return updated_parts;
 }
 
-void update_parts(LocalResource *local, UcxList *updates, uint64_t numparts) {
+void update_parts(LocalResource *local, CxList *updates, uint64_t numparts) {
     size_t old_num = local->numparts;
     if(old_num > numparts) {
         // free old parts
@@ -4226,8 +4155,12 @@
         local->numparts = numparts;
     }
     
-    UCX_FOREACH(elm, updates) {
-        FilePart *p = elm->data;
+    if(!updates) {
+        return;
+    }
+    
+    CxIterator i = cxListIterator(updates);
+    cx_foreach(FilePart *, p, i) {
         if(p->block > numparts) {
             // just make sure things don't explode in case some weird stuff
             // is going on
@@ -4285,7 +4218,7 @@
     
     DavBool issplit = split_blocksize == 0 ? FALSE : TRUE;
     int split_err = 0;
-    UcxList *parts = NULL;
+    CxList *parts = NULL;
     uint64_t blockcount = 0;
     
     if(islink) {
@@ -4455,7 +4388,7 @@
     
     LocalResource *local_origin = local->origin;
     if(!copy) {
-        ucx_map_cstr_remove(db->resources, local_origin->path);
+        cxMapRemove(db->resources, cx_hash_key_str(local_origin->path));
     }
     
     // set resource metadata
@@ -4496,7 +4429,7 @@
         DavSession *sn,
         LocalResource *local_res,
         int *counter,
-        UcxList **cols)
+        CxList *cols)
 {
     DavResource *res = dav_get(sn, local_res->path, "D:getetag,idav:split");
     if(!res) {
@@ -4508,7 +4441,7 @@
     if(res->iscollection) {
         DavXmlNode *split = dav_get_property_ns(res, DAV_NS, "split");
         if(cols) {
-            *cols = ucx_list_append(*cols, local_res);
+            cxListAdd(cols, local_res);
         } else if(split || !res->children) {
             printf("delete: %s\n", res->path);
             if(dav_delete(res)) {
@@ -4582,7 +4515,7 @@
         // get local tags
         DavBool changed = 0;
         char *tags_hash = NULL;
-        UcxList *tags = sync_get_file_tags(dir, local, &changed, &tags_hash);
+        CxList *tags = sync_get_file_tags(dir, local, &changed, &tags_hash);
         char *new_remote_hash = nullstrdup(tags_hash);
         if(changed || local->tags_updated) { 
             DavBool store_tags = TRUE;
@@ -4594,7 +4527,7 @@
             if(dav_load_prop(res, &p, 1) && sn->error != DAV_NOT_FOUND) {
                 print_resource_error(sn, res->path);
             }
-            UcxList *remote_tags = NULL;
+            CxList *remote_tags = NULL;
             DavXmlNode *tagsprop = dav_get_property_ns(res, DAV_PROPS_NS, "tags");
             if(tagsprop) {
                 remote_tags = parse_dav_xml_taglist(tagsprop);
@@ -4613,7 +4546,7 @@
                         break;
                     }
                     case TAG_MERGE: {
-                        UcxList *new_tags = merge_tags(tags, remote_tags);
+                        CxList *new_tags = merge_tags(tags, remote_tags);
                         free_taglist(tags);
                         tags = new_tags;
                         
@@ -4706,12 +4639,11 @@
 }
 
 void remove_deleted_conflicts(SyncDirectory *dir, SyncDatabase *db) {
-    char **dc = calloc(sizeof(void*), db->conflict->count);
+    char **dc = calloc(sizeof(void*), db->conflict->size);
     int numdc = 0;
     
-    UcxMapIterator i = ucx_map_iterator(db->conflict);
-    LocalResource *res;
-    UCX_MAP_FOREACH(key, res, i) {
+    CxIterator i = cxMapIteratorValues(db->conflict);
+    cx_foreach(LocalResource *, res, i) {
         char *path = create_local_path(dir, res->path);
         SYS_STAT s;
         if(sys_stat(path, &s)) {
@@ -4727,18 +4659,16 @@
     }
     
     for(int i=0;i<numdc;i++) {
-        ucx_map_cstr_remove(db->conflict, dc[i]);
+        cxMapRemove(db->conflict, cx_hash_key_str(dc[i]));
     }
     
     free(dc);
 }
 
 static void resolve_skipped(SyncDatabase *db) {
-    UcxKey k;
-    LocalResource *res;
-    UcxMapIterator i = ucx_map_iterator(db->resources);
+    CxIterator i = cxMapIteratorValues(db->resources);
     int skipped = 0;
-    UCX_MAP_FOREACH(k, res, i) {
+    cx_foreach(LocalResource *, res, i) {
         if(res->skipped) {
             skipped++;
             fprintf(stderr, "skipped from push: %s\n", res->path);
@@ -4777,9 +4707,9 @@
     int ret = 0;
     
     // remove conflicts
-    int num_conflict = db->conflict->count;
-    ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free);
-    ucx_map_clear(db->conflict);
+    int num_conflict = db->conflict->size;
+    //ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free);
+    cxMapClear(db->conflict);
     
     // store db
     if(store_db(db, dir->database, dir->db_settings)) {
@@ -4829,9 +4759,8 @@
     int ret = 0;
     
     // delete all conflict files
-    UcxMapIterator i = ucx_map_iterator(db->conflict);
-    LocalResource *res;
-    UCX_MAP_FOREACH(key, res, i) {
+    CxIterator i = cxMapIterator(db->conflict);
+    cx_foreach(LocalResource*, res, i) {
         printf("delete: %s\n", res->path);
         char *path = create_local_path(dir, res->path);
         if(sys_unlink(path)) {
@@ -4844,8 +4773,8 @@
         }
         free(path);
     }
-    ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free);
-    ucx_map_clear(db->conflict);
+    //ucx_map_free_content(db->conflict, (ucx_destructor)local_resource_free);
+    cxMapClear(db->conflict);
     
     // store db
     if(store_db(db, dir->database, dir->db_settings)) {
@@ -4893,30 +4822,28 @@
     remove_deleted_conflicts(dir, db);
     
     // get all conflict sources
-    UcxMapIterator i = ucx_map_iterator(db->conflict);
-    LocalResource *res;
-    UcxList* conflict_sources = NULL;
-    UCX_MAP_FOREACH(key, res, i) {
-        conflict_sources = ucx_list_append(conflict_sources, res->conflict_source);
+    CxIterator i = cxMapIteratorValues(db->conflict);
+    CxList* conflict_sources = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    cx_foreach(LocalResource *, res, i) {
+        cxListAdd(conflict_sources, res->conflict_source);
     }
     
     // print unique conflict sources
-    conflict_sources = ucx_list_sort(conflict_sources, ucx_cmp_str, NULL);
-    UCX_FOREACH(elem, conflict_sources) {
-        char* path = elem->data;
-        if(cmd_getoption(a, "verbose")) {
-            int confl_count = 1;
-            while(elem->next && !strcmp(elem->next->data, path)) {
-                elem = elem->next;
-                ++confl_count;
-            }
-            printf("%s (%d)\n", path, confl_count);
-        } else {
-            printf("%s\n", path);
-            while(elem->next && !strcmp(elem->next->data, path)) {
-                elem = elem->next;
-            }
-        }
+    // TODO: set cmpfunc at map creation
+    conflict_sources->cmpfunc = (cx_compare_func)strcmp;
+    cxListSort(conflict_sources);
+    i = cxListIterator(conflict_sources);
+    char *prev = "";
+    cx_foreach(char *, path, i) {
+        // TODO: implement verbose print   if(cmd_getoption(a, "verbose"))
+        //       printf("%s (%d)\n", path, confl_count);
+        if(!strcmp(path, prev)) {
+            continue;
+        }
+        
+        printf("%s\n", path);
+        
+        prev = path;
     }
     
     // cleanup
@@ -5029,7 +4956,7 @@
         fprintf(stderr, "No versioning configured for syncdir %s\n", dir->name);
     }
     
-    Repository *repo = get_repository(sstr(dir->repository));
+    Repository *repo = get_repository(cx_str(dir->repository));
     if(!repo) {
         fprintf(stderr, "Unknown repository %s\n", dir->repository);
         return -1;
@@ -5043,7 +4970,7 @@
     remove_deleted_conflicts(dir, db);
     
     DavSession *sn = create_session(a, ctx, repo, dir->collection);
-    ucx_mempool_reg_destr(sn->mp, db, (ucx_destructor)destroy_db);
+    util_regdestr(sn->mp, db, (cx_destructor_func)destroy_db);
     if (cmd_getoption(a, "verbose")) {
         curl_easy_setopt(sn->handle, CURLOPT_VERBOSE, 1L);
         curl_easy_setopt(sn->handle, CURLOPT_STDERR, stderr);
@@ -5079,23 +5006,23 @@
             }
             
             DavResource *child = vcol->children;
-            UcxList *children = NULL;
+            CxList *children = cxLinkedListCreate(cxDefaultAllocator, (cx_compare_func)strcmp, CX_STORE_POINTERS);
             while(child) {
-                children = ucx_list_append(children, child);
+                cxListAdd(children, child);
                 child = child->next;
             }
-            children = ucx_list_sort(children, ucx_cmp_str, NULL);
+            cxListSort(children);
             
             DavBool first = 1;
-            UCX_FOREACH(elm, children) {
-                DavResource *c = elm->data;
+            CxIterator i = cxListIterator(children);
+            cx_foreach(DavResource *, c, i) {
                 if(!first) {
                     putchar('\n');
                 }
                 print_resource_version(c, c->name);
                 first = 0;
             }
-            ucx_list_free(children);
+            cxListDestroy(children);
         } while(0);
     } else if(dir->versioning->type == VERSIONING_DELTAV) {
         DavResource *versions = dav_versiontree(res, NULL);
@@ -5271,6 +5198,10 @@
 }
 
 int cmd_tagop(CmdArgs *args, int cmd) {
+    // TODO: port to ucx 3
+    return 1;
+#if 0
+    
     SyncFile file;
     int ret = 0;
     char *path = args->argv[0];
@@ -5293,13 +5224,13 @@
     }
     
     LocalResource *newres = NULL;
-    LocalResource *localres = ucx_map_cstr_get(db->resources, file.path);
+    LocalResource *localres = cxMapGet(db->resources, cx_hash_key_str(file.path));
     if(!localres) {
         newres = calloc(1, sizeof(LocalResource));
         newres->path = strdup(file.path);
         localres = newres;
     }
-    UcxList *tags = NULL;
+    CxList *tags = NULL;
     DavBool store_tags = FALSE;
     
     if(cmd != CMD_TAG_SET) {
@@ -5307,7 +5238,7 @@
         char *tagcolor = NULL; // TODO: get color
 
         tags = sync_get_file_tags(file.dir, localres, NULL, NULL);
-        UcxList *x = NULL;
+        CxList *x = NULL;
         UCX_FOREACH(elm, tags) {
             DavTag *t = elm->data;
             if(cmd == CMD_TAG_LIST) {
@@ -5374,6 +5305,7 @@
     
     free(file.path);
     return ret;
+#endif
 }
 
 int isfileindir(SyncDirectory *dir, const char *path, SyncFile *f) {
@@ -5406,16 +5338,16 @@
     // TODO: normalize path
     DavBool not_in_dir = 0;
     
-    scstr_t fp = scstr(fullpath);
-    scstr_t dp = scstr(dir->path);
+    cxstring fp = cx_str(fullpath);
+    cxstring dp = cx_str(dir->path);
     if(fp.length == dp.length) {
-        if(sstrcmp(fp, dp)) {
+        if(cx_strcmp(fp, dp)) {
             not_in_dir = 1;
         }
     } else if(fp.length < dp.length) {
         not_in_dir = 1;
     } else {
-        if(!sstrprefix(fp, dp)) {
+        if(!cx_strprefix(fp, dp)) {
             not_in_dir = 1;
         } else {
             if(dp.ptr[dp.length-1] == '/') {
@@ -5466,10 +5398,8 @@
     } else {
         SyncDirectory *target = NULL;
         
-        UcxMapIterator i = scfg_directory_iterator();
-        UcxKey k;
-        SyncDirectory *dir;
-        UCX_MAP_FOREACH(key, dir, i) {
+        CxIterator i = scfg_directory_iterator();
+        cx_foreach(SyncDirectory *, dir, i) {
             if(isfileindir(dir, path, f)) {
                 if(target) {
                     return 5;
@@ -5500,11 +5430,17 @@
 
 
 int cmd_add_directory(CmdArgs *args) {
+    /*
     if(!get_repositories()) {
         fprintf(stderr, "No repositories available. Run 'dav add-repository' first.\n");
         fprintf(stderr, "Abort\n");
         return -1;
     }
+    */
+    
+    // TODO: port to ucx 3
+    return 1;
+#if 0
     
     printf("Each sync directory must have an unique name.\n");
     char *name = assistant_getcfg("name");
@@ -5525,10 +5461,9 @@
     }
     
     printf("Specify webdav repository.\n");
-    UcxList *repos = get_repositories();
+    CxIterator repos = get_repositories();
     int i = 0;
-    UCX_FOREACH(elm, repos) {
-        Repository *r = elm->data;
+    cx_foreach(Repository *, r, repos) {
         printf("%d) %s\n", i, r->name);
         i++;
     }
@@ -5590,12 +5525,12 @@
     free(db);
     
     return ret;
+#endif
 }
 
 int cmd_list_dirs() {
-    UcxMapIterator iter = scfg_directory_iterator();
-    SyncDirectory *dir;
-    UCX_MAP_FOREACH(key, dir, iter) {
+    CxIterator iter = scfg_directory_iterator();
+    cx_foreach(SyncDirectory *, dir, iter) {
         printf("%s\n", dir->name);
     }
     return 0;
@@ -5604,19 +5539,18 @@
 int cmd_check_repositories(CmdArgs *a) {
     int ret = EXIT_SUCCESS;
 
-    UcxList *reponames = NULL;
+    CxList *reponames = cxLinkedListCreateSimple(CX_STORE_POINTERS);
     {
-        UcxMapIterator iter = scfg_directory_iterator();
-        SyncDirectory *dir;
-        UCX_MAP_FOREACH(key, dir, iter) {
-            reponames = ucx_list_append(reponames, dir->repository);
-        }
-    }
-
-    UCX_FOREACH(listelem, reponames) {
-        char *reponame = listelem->data;
+        CxIterator iter = scfg_directory_iterator();
+        cx_foreach(SyncDirectory *, dir, iter) {
+            cxListAdd(reponames, dir->repository);
+        }
+    }
+    
+    CxIterator iter = cxListIterator(reponames);
+    cx_foreach(char *, reponame, iter) {
         printf("Checking %s... ", reponame);
-        Repository* repo = get_repository(sstr(reponame));
+        Repository* repo = get_repository(cx_str(reponame));
         if (!repo) {
             printf(" not found in config.xml!\n");
             ret = EXIT_FAILURE;
@@ -5640,13 +5574,13 @@
         }
     }
     
-    ucx_list_free(reponames);
+    cxListDestroy(reponames);
     
     return ret;
 }
 
 char* create_locktoken_file(const char *syncdirname, const char *locktoken) {
-    sstr_t fname = ucx_sprintf("locktoken-%s.txt", syncdirname);
+    cxmutstr fname = cx_asprintf("locktoken-%s.txt", syncdirname);
     char *path = config_file_path(fname.ptr);
     free(fname.ptr);
     
--- a/dav/sync.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/sync.h	Fri Apr 21 21:25:32 2023 +0200
@@ -31,7 +31,8 @@
 
 #include <curl/curl.h>
 #include <libidav/webdav.h>
-#include <ucx/list.h>
+#include <cx/list.h>
+#include <cx/hash_map.h>
 
 #include <pthread.h>
 
@@ -98,7 +99,7 @@
 pthread_t start_sighandler(pthread_mutex_t *mutex) ;
 void stop_sighandler(pthread_mutex_t *mutex, pthread_t tid);
 
-void res2map(DavResource *root, UcxMap *map);
+void res2map(DavResource *root, CxMap *map);
 
 int cmd_pull(CmdArgs *args, DavBool incoming);
 int cmd_push(CmdArgs *args, DavBool outgoing, DavBool archive);
@@ -106,14 +107,14 @@
 
 void print_outgoing(
         CmdArgs *args,
-        UcxList *ls_new,
-        UcxList *ls_modified,
-        UcxList *ls_conflict,
-        UcxList *ls_update,
-        UcxList *ls_delete,
-        UcxList *ls_move,
-        UcxList *ls_copy,
-        UcxList *ls_mkcol);
+        CxList *ls_new,
+        CxList *ls_modified,
+        CxList *ls_conflict,
+        CxList *ls_update,
+        CxList *ls_delete,
+        CxList *ls_move,
+        CxList *ls_copy,
+        CxList *ls_mkcol);
 
 RemoteChangeType resource_get_remote_change(
         CmdArgs *a,
@@ -149,15 +150,15 @@
 char* create_tmp_download_path(char *path);
 void move_to_trash(SyncDirectory *dir, char *path);
 
-UcxList* local_scan(SyncDirectory *dir, SyncDatabase *db);
-UcxList* read_changes(SyncDirectory *dir, SyncDatabase *db);
+CxList* local_scan(SyncDirectory *dir, SyncDatabase *db);
+CxList* read_changes(SyncDirectory *dir, SyncDatabase *db);
 LocalResource* local_resource_new(SyncDirectory *dir, SyncDatabase *db, char *path);
 char* local_resource_path(LocalResource *res);
 int local_resource_is_changed(
         SyncDirectory *dir,
         SyncDatabase *db,
         LocalResource *res,
-        UcxMap *svrres,
+        CxMap *svrres,
         DavBool restore_deleted,
         DavBool restore_modified);
 int remote_resource_is_changed(
@@ -183,13 +184,13 @@
 
 int sync_set_status(DavResource *res, char *status);
 int sync_remove_status(DavResource *res);
-UcxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res);
-UcxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash);
-int sync_tags_equal(UcxList *tags1, UcxList *tags2);
+CxBuffer* sync_get_file_tag_data(SyncDirectory *dir, LocalResource *res);
+CxList* sync_get_file_tags(SyncDirectory *dir, LocalResource *res, DavBool *changed, char **newhash);
+int sync_tags_equal(CxList *tags1, CxList *tags2);
 int sync_store_metadata(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res);
 int sync_store_xattr(SyncDirectory *dir, const char *path, XAttributes *xattr);
 int sync_store_tags(SyncDirectory *dir, const char *path, LocalResource *local, DavResource *res);
-int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, UcxList *tags);
+int sync_store_tags_local(SyncDirectory *dir, LocalResource *local, const char *path, CxList *tags);
 int sync_put_resource(
         SyncDirectory *dir,
         DavResource *res,
@@ -203,7 +204,7 @@
         LocalResource *local,
         DavBool copy,
         int *counter);
-int sync_delete_remote_resource(SyncDirectory *dir, DavSession *sn, LocalResource *res, int *counter, UcxList **cols);
+int sync_delete_remote_resource(SyncDirectory *dir, DavSession *sn, LocalResource *res, int *counter, CxList *cols);
 MetadataHashes sync_set_metadata_properties(
         SyncDirectory *dir,
         DavSession *sn,
@@ -216,7 +217,7 @@
         DavResource *res,
         LocalResource *local);
 
-void update_parts(LocalResource *local, UcxList *updates, uint64_t numparts);
+void update_parts(LocalResource *local, CxList *updates, uint64_t numparts);
 
 void remove_deleted_conflicts(SyncDirectory *dir, SyncDatabase *db);
 
--- a/dav/system.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/system.c	Fri Apr 21 21:25:32 2023 +0200
@@ -36,7 +36,7 @@
 #include <sys/types.h>
 #include <errno.h>
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #ifndef _WIN32
 #include <unistd.h>
@@ -335,8 +335,8 @@
     // however, we interpret .lnk files as symlinks
     int ret = 0;
     
-    scstr_t path_s = scstr(path);
-    if(scstrsuffix(path_s, SC(".lnk"))) {
+    cxstring path_s = cx_str(path);
+    if(cx_strsuffix(path_s, CX_STR(".lnk"))) {
         // looks like a .lnk file
         // check content
         IShellLink *sl;
--- a/dav/tags.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/tags.c	Fri Apr 21 21:25:32 2023 +0200
@@ -31,8 +31,10 @@
 #include <string.h>
 #include <ctype.h>
 
-#include <ucx/string.h>
-#include <ucx/utils.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
+#include <cx/hash_map.h>
 
 #include <libidav/crypto.h>
 
@@ -52,50 +54,53 @@
     free(tag);
 }
 
-void free_taglist(UcxList *list) {
-    ucx_list_free_content(list, (ucx_destructor)free_dav_tag);
-    ucx_list_free(list);
+void free_taglist(CxList *list) {
+    if(!list) {
+        return;
+    }
+    cxListDestroy(list);
 }
 
 int compare_tagname(DavTag* left, DavTag* right, void* ignorecase) {
-    sstr_t leftname = sstr(left->name);
-    sstr_t rightname = sstr(right->name);
+    cxstring leftname = cx_str(left->name);
+    cxstring rightname = cx_str(right->name);
     if (ignorecase && *((int*) ignorecase)) {
-        return sstrcasecmp(leftname, rightname);
+        return cx_strcasecmp(leftname, rightname);
     } else {
-        return sstrcmp(leftname, rightname);
+        return cx_strcmp(leftname, rightname);
     }
 }
 
-UcxList* parse_text_taglist(const char *buf, size_t length) {
-    UcxList *tags = NULL;
+CxList* parse_text_taglist(const char *buf, size_t length) {
+    CxList *tags = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    tags->simple_destructor = (cx_destructor_func)free_dav_tag;
     
     int line_start = 0;
     for(int i=0;i<length;i++) {
         if(buf[i] == '\n' || i == length-1) {
-            sstr_t line = sstrtrim(sstrn((char*)buf + line_start, i - line_start));
+            cxstring line = cx_strtrim(cx_strn((char*)buf + line_start, i - line_start));
             if(line.length > 0) {
                 DavTag *tag = calloc(1, sizeof(DavTag));
-                sstr_t color = sstrchr(line, '#');
+                cxstring color = cx_strchr(line, '#');
                 if(color.length>0) {
-                    sstr_t name = line;
+                    cxstring name = line;
                     name.length = (int)(color.ptr-line.ptr);
                     if(name.length != 0) {
-                        tag->name = sstrdup(name).ptr;
+                        tag->name = cx_strdup(name).ptr;
                         color.ptr++;
                         color.length--;
                         if(color.length > 0) {
-                            tag->color = sstrdup(color).ptr;
+                            tag->color = cx_strdup(color).ptr;
                         }
                     } else {
                         free(tag);
                     }
                 } else {
-                    tag->name = sstrdup(line).ptr;
+                    tag->name = cx_strdup(line).ptr;
                     tag->color = NULL;
                 }
                 
-                tags = ucx_list_append(tags, tag);
+                cxListAdd(tags, tag);
             }
             line_start = i+1;
         }
@@ -104,55 +109,61 @@
     return tags;
 }
 
-UcxBuffer* create_text_taglist(UcxList *tags) {
+CxMap* taglist2map(CxList *tags) {
+    CxMap *map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, tags->size + 8);
+    CxIterator iter = cxListIterator(tags);
+    cx_foreach(DavTag*, t, iter) {
+        cxMapPut(map, cx_hash_key_str(t->name), t);
+    }
+    return map;
+}
+
+CxBuffer* create_text_taglist(CxList *tags) {
     if(!tags) {
         return NULL;
     }
     
-    UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
-    UCX_FOREACH(elm, tags) {
-        DavTag *tag = elm->data;
+    CxBuffer *buf = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxIterator i = cxListIterator(tags);
+    cx_foreach(DavTag *, tag, i) {
         if(tag->color) {
-            ucx_bprintf(buf, "%s#%s\n", tag->name, tag->color);
+            cx_bprintf(buf, "%s#%s\n", tag->name, tag->color);
         } else {
-            ucx_bprintf(buf, "%s\n", tag->name);
+            cx_bprintf(buf, "%s\n", tag->name);
         }
     }
     return buf;
 }
 
 
-UcxList* parse_csv_taglist(const char *buf, size_t length) {
-    UcxList *taglist = NULL;
+CxList* parse_csv_taglist(const char *buf, size_t length) {
+    CxList *taglist = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    taglist->simple_destructor = (cx_destructor_func)free_dav_tag;
     
-    sstr_t str = sstrn((char*)buf, length);
-    ssize_t count = 0;
-    sstr_t *tags = sstrsplit(str, S(","), &count);
-    for(int i=0;i<count;i++) {
-        sstr_t trimmed_tag = sstrtrim(tags[i]);
+    cxstring str = cx_strn(buf, length);
+    CxStrtokCtx tags = cx_strtok(str, CX_STR(","), INT_MAX);
+    cxstring tagstr;
+    while(cx_strtok_next(&tags, &tagstr)) {
+        cxstring trimmed_tag = cx_strtrim(tagstr);
         if (trimmed_tag.length > 0) {
             DavTag *tag = malloc(sizeof(DavTag));
-            tag->name = sstrdup(trimmed_tag).ptr;
+            tag->name = cx_strdup(trimmed_tag).ptr;
             tag->color = NULL;
-            taglist = ucx_list_append(taglist, tag);
+            cxListAdd(taglist, tag);
         }
-        free(tags[i].ptr);
-    }
-    if(tags) {
-        free(tags);
     }
     return taglist;
 }
 
-UcxBuffer* create_csv_taglist(UcxList *tags) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
+CxBuffer* create_csv_taglist(CxList *tags) {
+    CxBuffer *buf = cxBufferCreate(NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     int insertsep = 0;
-    UCX_FOREACH(elm, tags) {
-        DavTag *tag = elm->data;
+    CxIterator i = cxListIterator(tags);
+    cx_foreach(DavTag*, tag, i) {
         if(insertsep) {
-            ucx_buffer_putc(buf, ',');
+            cxBufferPut(buf, ',');
         }
-        ucx_buffer_puts(buf, tag->name);
+        cxBufferPutString(buf, tag->name);
         insertsep = 1;
     }
     return buf;
@@ -196,8 +207,9 @@
     return tag;
 } 
 
-UcxList* parse_dav_xml_taglist(DavXmlNode *taglistnode) {
-    UcxList *tags = NULL;
+CxList* parse_dav_xml_taglist(DavXmlNode *taglistnode) {
+    CxList *tags = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    tags->simple_destructor = (cx_destructor_func)free_dav_tag;
     
     DavXmlNode *node = taglistnode;
     while(node) {
@@ -205,7 +217,7 @@
             if(!strcmp(node->namespace, DAV_PROPS_NS) && !strcmp(node->name, "tag")) {
                 DavTag *tag = parse_xml_dav_tag(node);
                 if(tag) {
-                    tags = ucx_list_append(tags, tag);
+                    cxListAdd(tags, tag);
                 }
             }
         } 
@@ -215,12 +227,11 @@
     return tags;
 }
 
-DavXmlNode* create_xml_taglist(UcxList *tags) {
+DavXmlNode* create_xml_taglist(CxList *tags) {
     DavXmlNode *tag1 = NULL;
     DavXmlNode *lasttag = NULL;
-    UCX_FOREACH(elm, tags) {
-        DavTag *tag = elm->data;
-        
+    CxIterator i = cxListIterator(tags);
+    cx_foreach(DavTag*, tag, i) {
         DavXmlNode *tagelm = dav_xml_createnode(DAV_PROPS_NS, "tag");
         DavXmlNode *tagname = dav_xml_createnode_with_text(DAV_PROPS_NS, "name", tag->name);
         tagelm->children = tagname;
@@ -278,8 +289,9 @@
     return tag;
 }
 
-UcxList* parse_macos_taglist(const char *buf, size_t length) {
-    UcxList *taglist = NULL;
+CxList* parse_macos_taglist(const char *buf, size_t length) {
+    CxList *taglist = cxLinkedListCreate(CX_STORE_POINTERS);
+    taglist->simple_destructor = (cx_destructor_func)free_dav_tag;
     
     CFDataRef data = CFDataCreateWithBytesNoCopy(
             kCFAllocatorDefault,
@@ -297,7 +309,7 @@
         if(CFStringGetCString(str, cstr, cstrbuflen, kCFStringEncodingUTF8)) {
             DavTag *tag = tagstr2davtag(cstr);
             if(tag) {
-                taglist = ucx_list_append(taglist, tag);
+                cxListAdd(taglist, tag);
             }
         }
         free(cstr);
@@ -309,7 +321,7 @@
     return taglist;
 }
 
-UcxBuffer* create_macos_taglist(UcxList *tags) {
+CxBuffer* create_macos_taglist(CxList *tags) {
     size_t count = ucx_list_size(tags);
     if(count == 0) {
         return NULL;
@@ -317,8 +329,8 @@
     
     CFStringRef *strings = calloc(sizeof(CFStringRef), count);
     int i = 0;
-    UCX_FOREACH(elm, tags) {
-        DavTag *tag = elm->data;
+    CxIterator i = cxListIterator(tags);
+    cx_foreach(DavTag*, tag, i) {
         CFStringRef str = NULL;
         if(tag->color) {
             sstr_t s = sstrcat(3, sstr(tag->name), S("\n"), sstr(tag->color));
@@ -334,13 +346,13 @@
     CFPropertyListRef array = CFArrayCreate(kCFAllocatorDefault, (const void**)strings, count, &kCFTypeArrayCallBacks);
     CFDataRef data = CFPropertyListCreateData(kCFAllocatorDefault, array, kCFPropertyListBinaryFormat_v1_0, 0, NULL);
     
-    UcxBuffer *buf = NULL;
+    CxBuffer *buf = NULL;
     if(data) {
         int datalen = CFDataGetLength(data);
         CFRange range;
         range.location = 0;
         range.length = datalen;
-        buf = ucx_buffer_new(NULL, datalen, 0);
+        buf = cxBufferCreate(NULL, datalen, cxDefaultAllocator, 0);
         CFDataGetBytes(data, range, (UInt8*)buf->space);
         buf->size = datalen;
         CFRelease(data);
@@ -355,18 +367,18 @@
 }
 
 #else
-UcxList* parse_macos_taglist(const char *buf, size_t length) {
+CxList* parse_macos_taglist(const char *buf, size_t length) {
     fprintf(stderr, "Error: macos tags not supported on this platform.\n");
     return NULL;
 }
-UcxBuffer* create_macos_taglist(UcxList *tags) {
+CxBuffer* create_macos_taglist(CxList *tags) {
     fprintf(stderr, "Error: macos tags not supported on this platform.\n");
     return NULL;
 }
 #endif
 
 
-int compare_taglists(UcxList *tags1, UcxList *tags2) {
+int compare_taglists(CxList *tags1, CxList *tags2) {
     if(!tags1) {
         return tags2 ? 0 : 1;
     }
@@ -374,100 +386,93 @@
         return tags1 ? 0 : 1;
     }
     
-    UcxMap *map1 = ucx_map_new(32);
-    UCX_FOREACH(elm, tags1) {
-        DavTag *t = elm->data;
-        ucx_map_cstr_put(map1, t->name, t);
-    }
+    CxMap *map1 = taglist2map(tags1);
     
     int equal = 1;
     int i = 0;
-    UCX_FOREACH(elm, tags2) {
-        DavTag *t = elm->data;
-        if(!ucx_map_cstr_get(map1, t->name)) {
+    CxIterator iter = cxListIterator(tags2);
+    cx_foreach(DavTag*, t, iter) {
+        if(!cxMapGet(map1, cx_hash_key_str(t->name))) {
             equal = 0;
             break;
         }
         i++;
     }
     
-    if(i != map1->count) {
+    if(i != map1->size) {
         equal = 0;
     }
-    ucx_map_free(map1);
+    cxMapDestroy(map1);
     return equal;
 }
 
-char* create_tags_hash(UcxList *tags) {
+char* create_tags_hash(CxList *tags) {
     if(!tags) {
         return NULL;
     }
-    UcxBuffer *buf = create_text_taglist(tags);
+    CxBuffer *buf = create_text_taglist(tags);
     char *hash = dav_create_hash(buf->space, buf->size);
-    ucx_buffer_free(buf);
+    cxBufferDestroy(buf);
     return hash;
 }
 
-UcxList* merge_tags(UcxList *tags1, UcxList *tags2) {
+CxList* merge_tags(CxList *tags1, CxList *tags2) {
     // this map is used to check the existence of tags
-    UcxMap *tag_map = ucx_map_new(32);
+    CxMap *tag_map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
     // merged taglist
-    UcxList *new_tags = NULL;
+    CxList *new_tags = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    new_tags->simple_destructor = (cx_destructor_func)free_dav_tag;
 
     // add all local tags
-    UCX_FOREACH(elm, tags1) {
-        DavTag *t = elm->data;
-        ucx_map_cstr_put(tag_map, t->name, t);
+    CxIterator iter = cxListIterator(tags1);
+    cx_foreach(DavTag*, t, iter) {
+        cxMapPut(tag_map, cx_hash_key_str(t->name), t);
         DavTag *newt = calloc(1, sizeof(DavTag));
         newt->color = t->color ? strdup(t->color) : NULL;
         newt->name = strdup(t->name);
-        new_tags = ucx_list_append(new_tags, newt);
+        cxListAdd(new_tags, newt);
     }
     // check if a remote tag is already in the map
     // and if not add it to the new taglist
-    UCX_FOREACH(elm, tags2) {
-        DavTag *t = elm->data;
-        if(!ucx_map_cstr_get(tag_map, t->name)) {
+    iter = cxListIterator(tags2);
+    cx_foreach(DavTag*, t, iter) {
+        if(!cxMapGet(tag_map, cx_hash_key_str(t->name))) {
             DavTag *newt = calloc(1, sizeof(DavTag));
             newt->color = t->color ? strdup(t->color) : NULL;
             newt->name = strdup(t->name);
-            new_tags = ucx_list_append(new_tags, newt);
+            cxListAdd(new_tags, newt);
         }
     }
 
-    ucx_map_free(tag_map);
+    cxMapDestroy(tag_map);
 
     return new_tags;
 }
 
-void add_tag_colors(UcxList *taglist, UcxList *colored) {
-    UcxMap *tagmap = ucx_map_new(32);
-    UCX_FOREACH(elm, taglist) {
-        DavTag *tag = elm->data;
-        ucx_map_cstr_put(tagmap, tag->name, tag);
-    }
+void add_tag_colors(CxList *taglist, CxList *colored) {
+    CxMap *tagmap = taglist2map(taglist);
     
-    UCX_FOREACH(elm, colored) {
-        DavTag *colored_tag = elm->data;
+    CxIterator i = cxListIterator(colored);
+    cx_foreach(DavTag*, colored_tag, i) {
         if(colored_tag->color) {
-            DavTag *tag = ucx_map_cstr_get(tagmap, colored_tag->name);
+            DavTag *tag = cxMapGet(tagmap, cx_hash_key_str(colored_tag->name));
             if(tag && !tag->color) {
                 tag->color = strdup(colored_tag->color);
             }
         }
     }
     
-    ucx_map_free(tagmap);
+    cxMapDestroy(tagmap);
 }
 
 /* ----------- ----------- tag filter  ---------------------- */
 
-static size_t rtrimskip(scstr_t str, size_t skip) {
+static size_t rtrimskip(cxstring str, size_t skip) {
     while (skip < str.length && isspace(str.ptr[skip])) skip++;
     return skip;
 }
 
-static size_t parse_tagfilter_taglist(scstr_t fs, SyncTagFilter* tagfilter) {
+static size_t parse_tagfilter_taglist(cxstring fs, SyncTagFilter* tagfilter) {
     size_t csvlen;
     for (csvlen = 0 ; csvlen < fs.length ; ++csvlen) {
         if (fs.ptr[csvlen] == ')') break;
@@ -479,12 +484,12 @@
     return csvlen;
 }
 
-static size_t parse_tagfilter_subfilters(scstr_t fs, SyncTagFilter* tagfilter);
+static size_t parse_tagfilter_subfilters(cxstring fs, SyncTagFilter* tagfilter);
 
-static size_t parse_tagfilter_filter(scstr_t fs, SyncTagFilter* tagfilter) {
+static size_t parse_tagfilter_filter(cxstring fs, SyncTagFilter* tagfilter) {
     
     size_t consumed = rtrimskip(fs, 0);
-    fs = scstrsubs(fs, consumed);
+    fs = cx_strsubs(fs, consumed);
     
     if (fs.length == 0) {
         return consumed;
@@ -512,7 +517,7 @@
         if (hasop) {
             size_t skip = rtrimskip(fs, 1);
             consumed += skip;
-            fs = scstrsubs(fs, skip);
+            fs = cx_strsubs(fs, skip);
         }
 
         if (fs.length > 0 && fs.ptr[0] == '(') {
@@ -533,7 +538,7 @@
 /*
  * Parses:  ( "(" , filter , ")" )+
  */
-static size_t parse_tagfilter_subfilters(scstr_t fs, SyncTagFilter* f) {
+static size_t parse_tagfilter_subfilters(cxstring fs, SyncTagFilter* f) {
     
     // strategy: allocate much and give back later (instead of reallocs in loop)
     size_t subfilter_cap = 8;
@@ -545,7 +550,7 @@
     do {
         // skip leading parenthesis (and white spaces)
         c = rtrimskip(fs, 1);
-        fs = scstrsubs(fs, c);
+        fs = cx_strsubs(fs, c);
         total_consumed += c;
     
         // increase array capacity, if necessary
@@ -572,7 +577,7 @@
             
             // consume ')' and find the next parenthesis or the end-of-string
             c = rtrimskip(fs, 1+c);
-            fs = scstrsubs(fs, c);
+            fs = cx_strsubs(fs, c);
             total_consumed += c;
 
             if (fs.length == 0 || fs.ptr[0] == ')') {
@@ -611,7 +616,7 @@
         return tagfilter;
     }
     
-    scstr_t fs = scstr(filterstring);
+    cxstring fs = cx_str(filterstring);
     size_t consumed = parse_tagfilter_filter(fs, tagfilter);
     if (!consumed) {
         free_tagfilter(tagfilter);
@@ -639,42 +644,55 @@
 }
 
 
-static int matches_tags_and(UcxList *dav_tags, UcxList *tags, int ignorecase) {
-    UCX_FOREACH(e, tags) {
-        if (!ucx_list_contains(dav_tags, e->data,
-                (cmp_func) compare_tagname, &ignorecase)) {
-            return 0;
+static int matches_tags_and(CxList *dav_tags, CxList *tags, int ignorecase) {
+    // ignorecase not supported yet
+    int ret = 1;
+    CxMap *tagmap = taglist2map(dav_tags);
+    CxIterator i = cxListIterator(tags);
+    cx_foreach(DavTag *, tag, i) {
+        if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) {
+            ret = 0;
+            break;
         }
     }
-    return 1;
+    cxMapDestroy(tagmap);
+    return ret;
 }
 
-static int matches_tags_or(UcxList *dav_tags, UcxList *tags, int ignorecase) {
-    UCX_FOREACH(e, tags) {
-        if (ucx_list_contains(dav_tags, e->data,
-                (cmp_func) compare_tagname, &ignorecase)) {
-            return 1;
+static int matches_tags_or(CxList *dav_tags, CxList *tags, int ignorecase) {
+    // ignorecase not supported yet
+    int ret = 0;
+    CxMap *tagmap = taglist2map(dav_tags);
+    CxIterator i = cxListIterator(tags);
+    cx_foreach(DavTag *, tag, i) {
+        if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) {
+            ret = 1;
+            break;
         }
     }
-    return 0;
+    cxMapDestroy(tagmap);
+    return ret;
 }
 
-static int matches_tags_one(UcxList *dav_tags, UcxList *tags, int ignorecase) {
+static int matches_tags_one(CxList *dav_tags, CxList *tags, int ignorecase) {
     int matches_exactly_one = 0;
-    UCX_FOREACH(e, tags) {
-        if (ucx_list_contains(dav_tags, e->data,
-                (cmp_func) compare_tagname, &ignorecase)) {
+    CxMap *tagmap = taglist2map(dav_tags);
+    CxIterator i = cxListIterator(tags);
+    cx_foreach(DavTag *, tag, i) {
+        if (cxMapGet(tagmap, cx_hash_key_str(tag->name))) {
             if (matches_exactly_one) {
+                cxMapDestroy(tagmap);
                 return 0;
             } else {
                 matches_exactly_one = 1;
             }
         }
     }
+    cxMapDestroy(tagmap);
     return matches_exactly_one;
 }
 
-static int matches_subfilters_and(UcxList *dav_tags, SyncTagFilter *filter) {
+static int matches_subfilters_and(CxList *dav_tags, SyncTagFilter *filter) {
     int ret = 1;
     for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
         ret &= matches_tagfilter(dav_tags, filter->subfilters[i]);
@@ -682,7 +700,7 @@
     return ret;
 }
 
-static int matches_subfilters_or(UcxList *dav_tags, SyncTagFilter *filter) {
+static int matches_subfilters_or(CxList *dav_tags, SyncTagFilter *filter) {
     int ret = 0;
     for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
         ret |= matches_tagfilter(dav_tags, filter->subfilters[i]);
@@ -690,7 +708,7 @@
     return ret;
 }
 
-static int matches_subfilters_one(UcxList *dav_tags, SyncTagFilter *filter) {
+static int matches_subfilters_one(CxList *dav_tags, SyncTagFilter *filter) {
     int one = 0;
     for (size_t i = 0 ; i < filter->subfilter_count ; i++) {
         if (matches_tagfilter(dav_tags, filter->subfilters[i])) {
@@ -704,7 +722,7 @@
     return one;
 }
 
-int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter) {
+int matches_tagfilter(CxList *dav_tags, SyncTagFilter *tagfilter) {
 
     if (tagfilter->subfilter_count > 0) {
         switch (tagfilter->mode) {
--- a/dav/tags.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/tags.h	Fri Apr 21 21:25:32 2023 +0200
@@ -29,9 +29,9 @@
 #ifndef TAGS_H
 #define TAGS_H
 
-#include <ucx/string.h>
-#include <ucx/buffer.h>
-#include <ucx/list.h>
+#include <cx/string.h>
+#include <cx/buffer.h>
+#include <cx/list.h>
 
 #include <libidav/webdav.h>
 
@@ -65,44 +65,48 @@
 struct SyncTagFilter {
     int scope;
     int mode;
-    UcxList* tags;
+    CxList* tags;
     size_t subfilter_count;
     SyncTagFilter** subfilters;
 };
 
 void free_dav_tag(DavTag* tag);
 
-void free_taglist(UcxList *list);
+void free_taglist(CxList *list);
 
 int compare_tagname(DavTag* left, DavTag* right, void* ignorecase);
 
-UcxList* parse_text_taglist(const char *buf, size_t length);
-UcxBuffer* create_text_taglist(UcxList *tags);
+CxMap* taglist2map(CxList *tags);
 
-UcxList* parse_csv_taglist(const char *buf, size_t length);
-UcxBuffer* create_csv_taglist(UcxList *tags);
+CxList* parse_text_taglist(const char *buf, size_t length);
+CxBuffer* create_text_taglist(CxList *tags);
+
+CxList* parse_csv_taglist(const char *buf, size_t length);
+CxBuffer* create_csv_taglist(CxList *tags);
 
-UcxList* parse_dav_xml_taglist(DavXmlNode *taglistnode);
-DavXmlNode* create_xml_taglist(UcxList *tags);
+CxList* parse_dav_xml_taglist(DavXmlNode *taglistnode);
+DavXmlNode* create_xml_taglist(CxList *tags);
+
+CxList* parse_macos_taglist(const char *buf, size_t length);
+CxBuffer* create_macos_taglist(CxList *tags);
 
-UcxList* parse_macos_taglist(const char *buf, size_t length);
-UcxBuffer* create_macos_taglist(UcxList *tags);
+int compare_taglists(CxList *tags1, CxList *tags2);
 
-char* create_tags_hash(UcxList *tags);
+char* create_tags_hash(CxList *tags);
 
-UcxList* merge_tags(UcxList *tags1, UcxList *tags2);
+CxList* merge_tags(CxList *tags1, CxList *tags2);
 
 /*
  * Adds tag colors from the colored list to taglist if tags have the same name
  */
-void add_tag_colors(UcxList *taglist, UcxList *colored);
+void add_tag_colors(CxList *taglist, CxList *colored);
 
 /* ----------- ----------- tag filter  ---------------------- */
 
 SyncTagFilter* parse_tagfilter_string(const char* filterstring, int scope);
 void free_tagfilter(SyncTagFilter* filter);
 
-int matches_tagfilter(UcxList *dav_tags, SyncTagFilter *tagfilter);
+int matches_tagfilter(CxList *dav_tags, SyncTagFilter *tagfilter);
 
 #ifdef __cplusplus
 }
--- a/dav/tar.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/dav/tar.c	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #include "tar.h"
 
 #include <string.h>
-#include <ucx/string.h>
+#include <cx/string.h>
 #include <libidav/utils.h>
 
 
@@ -59,13 +59,13 @@
 static int add_header(TarOutputStream *tar, char *path, uint32_t mode, uint64_t size, time_t mtime, int type) {
     // split path in prefix and name and check length
     char *p = util_parent_path(path);
-    char *n = util_resource_name(path);
+    const char *n = util_resource_name(path);
     if(!p || !n) {
         return -1;
     }
     
-    sstr_t prefix = sstr(p);
-    sstr_t name = sstr(n);
+    cxstring prefix = cx_str(p);
+    cxstring name = cx_str(n);
     
     if(prefix.ptr[prefix.length-1] == '/') {
         prefix.length--;
@@ -73,16 +73,19 @@
     
     if(prefix.length > 154) {
         tar->error = TAR_PATH_TOO_LONG;
+        free(p);
         return -1;
     }
     if(name.length > 99) {
         tar->error = TAR_PATH_TOO_LONG;
+        free(p);
         return -1;
     }
     
     // check file length
     if(size >= 077777777777 ) {
         tar->error = TAR_FILE_TOO_LARGE;
+        free(p);
         return -1;
     }
     
@@ -136,6 +139,8 @@
     
     fwrite(&h, 1, 512, tar->file);
     
+    free(p);
+    
     return 0;
 }
 
--- a/libidav/Makefile	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/Makefile	Fri Apr 21 21:25:32 2023 +0200
@@ -44,10 +44,10 @@
 
 OBJ = $(SRC:%.c=../build/libidav/%$(OBJ_EXT))
 
-all: ../build/ucx ../build/libidav$(LIB_EXT)
+all: ../build/ucx ../build/lib/libidav$(LIB_EXT)
 
-../build/libidav$(LIB_EXT): $(OBJ)
-	$(AR) $(ARFLAGS) $(AOFLAGS)../build/libidav$(LIB_EXT) $(OBJ)
+../build/lib/libidav$(LIB_EXT): $(OBJ)
+	$(AR) $(ARFLAGS) $(AOFLAGS)$@ $(OBJ)
 
 ../build/libidav/%$(OBJ_EXT): %.c
 	$(CC) $(CFLAGS) $(DAV_CFLAGS) -I.. -I../ucx -c -o $@ $<
--- a/libidav/crypto.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/crypto.c	Fri Apr 21 21:25:32 2023 +0200
@@ -1487,26 +1487,26 @@
 
 
 
-UcxBuffer* aes_encrypt_buffer(UcxBuffer *in, DavKey *key) {
-    UcxBuffer *encbuf = ucx_buffer_new(
-            NULL,
-            in->size+16,
-            UCX_BUFFER_AUTOEXTEND);
+CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key) {
+    CxBuffer *encbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    if(!encbuf) {
+        return NULL;
+    }
     
     AESEncrypter *enc = aes_encrypter_new(
             key,
             in,
-            (dav_read_func)ucx_buffer_read,
+            (dav_read_func)cxBufferRead,
             NULL);
     if(!enc) {
-        ucx_buffer_free(encbuf);
+        cxBufferFree(encbuf);
         return NULL;
     }
     
     char buf[1024];
     size_t r;
     while((r = aes_read(buf, 1, 1024, enc)) > 0) {
-        ucx_buffer_write(buf, 1, r, encbuf);
+        cxBufferWrite(buf, 1, r, encbuf);
     }
     aes_encrypter_close(enc);
     
@@ -1514,15 +1514,19 @@
     return encbuf;
 }
 
-UcxBuffer* aes_decrypt_buffer(UcxBuffer *in, DavKey *key) {
-    UcxBuffer *decbuf = ucx_buffer_new(
-            NULL,
-            in->size,
-            UCX_BUFFER_AUTOEXTEND);
+CxBuffer* aes_decrypt_buffer(CxBuffer *in, DavKey *key) {
+    CxBuffer *decbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    if(!decbuf) {
+        return NULL;
+    }
     AESDecrypter *dec = aes_decrypter_new(
             key,
             decbuf,
-            (dav_write_func)ucx_buffer_write);
+            (dav_write_func)cxBufferWrite);
+    if(!dec) {
+        cxBufferFree(decbuf);
+        return NULL;
+    }
     
     aes_write(in->space, 1, in->size, dec);
     aes_decrypter_shutdown(dec);
--- a/libidav/crypto.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/crypto.h	Fri Apr 21 21:25:32 2023 +0200
@@ -30,7 +30,7 @@
 #define	DAV_CRYPTO_H
 
 #include "webdav.h"
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #ifdef __APPLE__
 /* macos */
@@ -155,8 +155,8 @@
 
 DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc);
 
-UcxBuffer* aes_encrypt_buffer(UcxBuffer *buf, DavKey *key);
-UcxBuffer* aes_decrypt_buffer(UcxBuffer *buf, DavKey *key);
+CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key);
+CxBuffer* aes_decrypt_buffer(CxBuffer *in, DavKey *key);
 
 #ifdef	__cplusplus
 }
--- a/libidav/davqlexec.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/davqlexec.c	Fri Apr 21 21:25:32 2023 +0200
@@ -31,8 +31,12 @@
 #include <string.h>
 #include <inttypes.h>
 
-#include <ucx/utils.h>
-#include <ucx/map.h>
+#include <cx/utils.h>
+#include <cx/map.h>
+#include <cx/hash_map.h>
+#include <cx/printf.h>
+#include <cx/basic_mempool.h>
+
 #include "davqlexec.h"
 #include "utils.h"
 #include "methods.h"
@@ -46,9 +50,16 @@
     }
     args->first = NULL;
     
+    if(!st->args) {
+        args->first = NULL;
+        args->current = NULL;
+        return args;
+    }
+    
     DavQLArg *cur = NULL;
-    UCX_FOREACH(elm, st->args) {
-        intptr_t type = (intptr_t)elm->data;
+    CxIterator i = cxListIterator(st->args);
+    cx_foreach(void*, data, i) {
+        intptr_t type = (intptr_t)data;
         DavQLArg *arg = calloc(1, sizeof(DavQLArg));
         if(!arg) {
             dav_ql_free_arglist(args);
@@ -167,8 +178,9 @@
     return result;
 }
 
-sstr_t dav_format_string(UcxAllocator *a, sstr_t fstr, DavQLArgList *ap, davqlerror_t *error) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
+cxmutstr dav_format_string(const CxAllocator *a, cxstring fstr, DavQLArgList *ap, davqlerror_t *error) {
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 128, a, CX_BUFFER_AUTO_EXTEND);
     
     int placeholder = 0;
     for(int i=0;i<fstr.length;i++) {
@@ -176,24 +188,24 @@
         if(placeholder) {
             if(c == '%') {
                 // no placeholder, %% transposes to %
-                ucx_buffer_putc(buf, c);
+                cxBufferPut(&buf, c);
             } else {
                 // detect placeholder type and insert arg
                 int err = 0;
                 switch(c) {
                     case 's': {
                         char *arg = dav_ql_getarg_str(ap);
-                        ucx_buffer_puts(buf, arg);
+                        cxBufferPutString(&buf, arg);
                         break;
                     }
                     case 'd': {
                         int arg = dav_ql_getarg_int(ap);
-                        ucx_bprintf(buf, "%d", arg);
+                        cx_bprintf(&buf, "%d", arg);
                         break;
                     }
                     case 'u': {
                         unsigned int arg = dav_ql_getarg_uint(ap);
-                        ucx_bprintf(buf, "%u", arg);
+                        cx_bprintf(&buf, "%u", arg);
                         break;
                     }
                     case 't': {
@@ -207,11 +219,8 @@
                     }
                 }
                 if(err) {
-                    ucx_buffer_free(buf);
-                    sstr_t n;
-                    n.ptr = NULL;
-                    n.length = 0;
-                    return n;
+                    cxBufferDestroy(&buf);
+                    return (cxmutstr){NULL,0};
                 }
             }
             placeholder = 0;
@@ -219,29 +228,32 @@
             if(c == '%') {
                 placeholder = 1;
             } else {
-                ucx_buffer_putc(buf, c);
+                cxBufferPut(&buf, c);
             }
         }
     }
+    if(cxBufferPut(&buf, '\0')) {
+        *error = DAVQL_OOM;
+        cxBufferDestroy(&buf);
+        return (cxmutstr){NULL, 0};
+    }
     *error = DAVQL_OK;
     
-    sstr_t ret = sstrdup_a(a, sstrn(buf->space, buf->size));
-    ucx_buffer_free(buf);
-    return ret;
+    return cx_mutstrn(buf.space, buf.size-1);
 }
 
-static int fl_add_properties(DavSession *sn, UcxMempool *mp, UcxMap *map, DavQLExpression *expression) {
+static int fl_add_properties(DavSession *sn, const CxAllocator *a, CxMap *map, DavQLExpression *expression) {
     if(!expression) {
         return 0;
     }
     
     if(expression->type == DAVQL_IDENTIFIER) {
-        DavProperty *property = ucx_mempool_malloc(mp, sizeof(DavProperty));
+        DavProperty *property = cxMalloc(a, sizeof(DavProperty));
 
         char *name;
         DavNamespace *ns = dav_get_property_namespace(
                 sn->context,
-                sstrdup_a(mp->allocator, expression->srctext).ptr,
+                cx_strdup_a(a, expression->srctext).ptr,
                 &name);
         if(!ns) {
             return -1;
@@ -251,16 +263,16 @@
         property->name = name;
         property->value = NULL;
         
-        ucx_map_sstr_put(map, expression->srctext, property);
+        cxMapPut(map, cx_hash_key(expression->srctext.ptr, expression->srctext.length), property);
     }
     
     if(expression->left) {
-        if(fl_add_properties(sn, mp, map, expression->left)) {
+        if(fl_add_properties(sn, a, map, expression->left)) {
             return -1;
         }
     }
     if(expression->right) {
-        if(fl_add_properties(sn, mp, map, expression->right)) {
+        if(fl_add_properties(sn, a, map, expression->right)) {
             return -1;
         }
     }
@@ -268,170 +280,170 @@
     return 0;
 }
 
-static UcxBuffer* fieldlist2propfindrequest(DavSession *sn, UcxMempool *mp, UcxList *fields, int *isallprop) {
-    UcxMap *properties = ucx_map_new(32);
+static CxBuffer* fieldlist2propfindrequest(DavSession *sn, const CxAllocator *a, CxList *fields, int *isallprop) {
+    CxMap *properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
     *isallprop = 0;
     
-    UCX_FOREACH(elm, fields) {
-        DavQLField *field = elm->data;
-        if(!sstrcmp(field->name, S("*"))) {
-            ucx_map_free(properties);
+    CxIterator i = cxListIterator(fields);
+    cx_foreach(DavQLField*, field, i) {
+        if(!cx_strcmp(field->name, CX_STR("*"))) {
+            cxMapDestroy(properties);
             *isallprop = 1;
             return create_allprop_propfind_request();
-        } else if(!sstrcmp(field->name, S("-"))) {
-            ucx_map_free(properties);
+        } else if(!cx_strcmp(field->name, CX_STR("-"))) {
+            cxMapDestroy(properties);
             return create_propfind_request(sn, NULL, "propfind", 0);
         } else {
-            if(fl_add_properties(sn, mp, properties, field->expr)) {
+            if(fl_add_properties(sn, a, properties, field->expr)) {
                 // TODO: set error
-                ucx_map_free(properties);
+                cxMapDestroy(properties);
                 return NULL;
             }
         }
     }
     
-    UcxMapIterator i = ucx_map_iterator(properties);
-    UcxKey key;
-    DavProperty *value;
-    UcxList *list = NULL;
-    UCX_MAP_FOREACH(key, value, i) {
-        list = ucx_list_append(list, value);
+    i = cxMapIteratorValues(properties);
+    CxList *list = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    cx_foreach(DavProperty*, value, i) {
+        cxListAdd(list, value);
     }
     
-    UcxBuffer *reqbuf = create_propfind_request(sn, list, "propfind", 0);
-    ucx_list_free(list);
-    ucx_map_free(properties);
+    CxBuffer *reqbuf = create_propfind_request(sn, list, "propfind", 0);
+    cxListDestroy(list);
+    cxMapDestroy(properties);
     return reqbuf;
 }
 
-static int reset_properties(DavSession *sn, DavResult *result, DavResource *res, UcxList *fields) {
-    UcxMap *new_properties = ucx_map_new_a(sn->mp->allocator, 32);
+static int reset_properties(DavSession *sn, DavResult *result, DavResource *res, CxList *fields) {
+    CxMap *new_properties = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 32);
     DavResourceData *data = (DavResourceData*)res->data;
     
     // add basic properties
     void *value;
     
-    sstr_t cl_keystr = dav_property_key("DAV:", "getcontentlength");
-    UcxKey cl_key = ucx_key(cl_keystr.ptr, cl_keystr.length);
-    value = ucx_map_get(data->properties, cl_key);
+    cxmutstr cl_keystr = dav_property_key("DAV:", "getcontentlength");
+    CxHashKey cl_key = cx_hash_key(cl_keystr.ptr, cl_keystr.length);
+    value = cxMapGet(data->properties, cl_key);
     if(value) {
-        ucx_map_put(new_properties, cl_key, value);
+        cxMapPut(new_properties, cl_key, value);
     }
     
-    sstr_t cd_keystr = dav_property_key("DAV:", "creationdate");
-    UcxKey cd_key = ucx_key(cd_keystr.ptr, cd_keystr.length);
-    value = ucx_map_get(data->properties, cd_key);
+    cxmutstr cd_keystr = dav_property_key("DAV:", "creationdate");
+    CxHashKey cd_key = cx_hash_key(cd_keystr.ptr, cd_keystr.length);
+    value = cxMapGet(data->properties, cd_key);
     if(value) {
-        ucx_map_put(new_properties, cd_key, value);
+        cxMapPut(new_properties, cd_key, value);
     }
     
-    sstr_t lm_keystr = dav_property_key("DAV:", "getlastmodified");
-    UcxKey lm_key = ucx_key(lm_keystr.ptr, lm_keystr.length);
-    value = ucx_map_get(data->properties, lm_key);
+    cxmutstr lm_keystr = dav_property_key("DAV:", "getlastmodified");
+    CxHashKey lm_key = cx_hash_key(lm_keystr.ptr, lm_keystr.length);
+    value = cxMapGet(data->properties, lm_key);
     if(value) {
-        ucx_map_put(new_properties, lm_key, value);
+        cxMapPut(new_properties, lm_key, value);
     }
     
-    sstr_t ct_keystr = dav_property_key("DAV:", "getcontenttype");
-    UcxKey ct_key = ucx_key(ct_keystr.ptr, ct_keystr.length);
-    value = ucx_map_get(data->properties, ct_key);
+    cxmutstr ct_keystr = dav_property_key("DAV:", "getcontenttype");
+    CxHashKey ct_key = cx_hash_key(ct_keystr.ptr, ct_keystr.length);
+    value = cxMapGet(data->properties, ct_key);
     if(value) {
-        ucx_map_put(new_properties, ct_key, value);
+        cxMapPut(new_properties, ct_key, value);
     }
     
-    sstr_t rt_keystr = dav_property_key("DAV:", "resourcetype");
-    UcxKey rt_key = ucx_key(rt_keystr.ptr, rt_keystr.length);
-    value = ucx_map_get(data->properties, rt_key);
+    cxmutstr rt_keystr = dav_property_key("DAV:", "resourcetype");
+    CxHashKey rt_key = cx_hash_key(rt_keystr.ptr, rt_keystr.length);
+    value = cxMapGet(data->properties, rt_key);
     if(value) {
-        ucx_map_put(new_properties, rt_key, value);
+        cxMapPut(new_properties, rt_key, value);
     }
     
-    sstr_t cn_keystr = dav_property_key(DAV_NS, "crypto-name");
-    UcxKey cn_key = ucx_key(cn_keystr.ptr, cn_keystr.length);
-    value = ucx_map_get(data->properties, cn_key);
+    cxmutstr cn_keystr = dav_property_key(DAV_NS, "crypto-name");
+    CxHashKey cn_key = cx_hash_key(cn_keystr.ptr, cn_keystr.length);
+    value = cxMapGet(data->properties, cn_key);
     if(value) {
-        ucx_map_put(new_properties, cn_key, value);
+        cxMapPut(new_properties, cn_key, value);
     }
     
-    sstr_t ck_keystr = dav_property_key(DAV_NS, "crypto-key");
-    UcxKey ck_key = ucx_key(ck_keystr.ptr, ck_keystr.length);
-    value = ucx_map_get(data->properties, ck_key);
+    cxmutstr ck_keystr = dav_property_key(DAV_NS, "crypto-key");
+    CxHashKey ck_key = cx_hash_key(ck_keystr.ptr, ck_keystr.length);
+    value = cxMapGet(data->properties, ck_key);
     if(value) {
-        ucx_map_put(new_properties, ck_key, value);
+        cxMapPut(new_properties, ck_key, value);
     }
     
-    sstr_t ch_keystr = dav_property_key(DAV_NS, "crypto-hash");
-    UcxKey ch_key = ucx_key(ch_keystr.ptr, ch_keystr.length);
-    value = ucx_map_get(data->properties, ch_key);
+    cxmutstr ch_keystr = dav_property_key(DAV_NS, "crypto-hash");
+    CxHashKey ch_key = cx_hash_key(ch_keystr.ptr, ch_keystr.length);
+    value = cxMapGet(data->properties, ch_key);
     if(value) {
-        ucx_map_put(new_properties, ch_key, value);
+        cxMapPut(new_properties, ch_key, value);
     }
     
     // add properties from field list
-    UCX_FOREACH(elm, fields) {
-        DavCompiledField *field = elm->data;
-        DavQLStackObj field_result;
-        if(!dav_exec_expr(field->code, res, &field_result)) {
-            sstr_t str;
-            str.ptr = NULL;
-            str.length = 0;
-            DavXmlNode *node = NULL;
-            if(field_result.type == 0) {
-                str = ucx_asprintf(
-                        sn->mp->allocator,
-                        "%d",
-                        field_result.data.integer);
-            } else if(field_result.type == 1) {
-                if(field_result.data.string) {
-                    str = sstrdup_a(sn->mp->allocator, sstrn(
-                            field_result.data.string,
-                            field_result.length));
+    if(fields) {
+        CxIterator i = cxListIterator(fields);
+        cx_foreach(DavCompiledField*, field, i) {
+            DavQLStackObj field_result;
+            if(!dav_exec_expr(field->code, res, &field_result)) {
+                cxmutstr str;
+                str.ptr = NULL;
+                str.length = 0;
+                DavXmlNode *node = NULL;
+                if(field_result.type == 0) {
+                    str = cx_asprintf_a(
+                            sn->mp->allocator,
+                            "%" PRId64,
+                            field_result.data.integer);
+                } else if(field_result.type == 1) {
+                    if(field_result.data.string) {
+                        str = cx_strdup_a(sn->mp->allocator, cx_strn(
+                                field_result.data.string,
+                                field_result.length));
+                    }
+                } else if(field_result.type == 2) {
+                    node = dav_copy_node(field_result.data.node);
+                } else {
+                    // unknown type
+                    // TODO: error
+                    resource_free_properties(sn, new_properties);
+                    return -1;
                 }
-            } else if(field_result.type == 2) {
-                node = dav_copy_node(field_result.data.node);
+                if(str.ptr) {
+                    node = dav_session_malloc(sn, sizeof(DavXmlNode));
+                    memset(node, 0, sizeof(DavXmlNode));
+                    node->type = DAV_XML_TEXT;
+                    node->content = str.ptr;
+                    node->contentlength = str.length;
+                }
+                if(node) {
+                    cxmutstr key = dav_property_key(field->ns, field->name);
+
+                    DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace));
+                    namespace->prefix = NULL;
+                    namespace->name = dav_session_strdup(sn, field->ns);
+
+                    DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty));
+                    prop->name = dav_session_strdup(sn, field->name);
+                    prop->ns = namespace;
+                    prop->value = node;
+
+                    cxMapPut(new_properties, cx_hash_key(key.ptr, key.length), prop);
+                    free(key.ptr);
+                }
             } else {
-                // unknown type
                 // TODO: error
                 resource_free_properties(sn, new_properties);
                 return -1;
             }
-            if(str.ptr) {
-                node = dav_session_malloc(sn, sizeof(DavXmlNode));
-                memset(node, 0, sizeof(DavXmlNode));
-                node->type = DAV_XML_TEXT;
-                node->content = str.ptr;
-                node->contentlength = str.length;
-            }
-            if(node) {
-                sstr_t key = dav_property_key(field->ns, field->name);
-                
-                DavNamespace *namespace = dav_session_malloc(sn, sizeof(DavNamespace));
-                namespace->prefix = NULL;
-                namespace->name = dav_session_strdup(sn, field->ns);
-
-                DavProperty *prop = dav_session_malloc(sn, sizeof(DavProperty));
-                prop->name = dav_session_strdup(sn, field->name);
-                prop->ns = namespace;
-                prop->value = node;
-                
-                ucx_map_sstr_put(new_properties, key, prop);
-                free(key.ptr);
-            }
-        } else {
-            // TODO: error
-            resource_free_properties(sn, new_properties);
-            return -1;
         }
     }
     
-    ucx_map_remove(data->properties, cl_key);
-    ucx_map_remove(data->properties, cd_key);
-    ucx_map_remove(data->properties, lm_key);
-    ucx_map_remove(data->properties, ct_key);
-    ucx_map_remove(data->properties, rt_key);
-    ucx_map_remove(data->properties, cn_key);
-    ucx_map_remove(data->properties, ck_key);
-    ucx_map_remove(data->properties, ch_key);
+    cxMapRemove(data->properties, cl_key);
+    cxMapRemove(data->properties, cd_key);
+    cxMapRemove(data->properties, lm_key);
+    cxMapRemove(data->properties, ct_key);
+    cxMapRemove(data->properties, rt_key);
+    cxMapRemove(data->properties, cn_key);
+    cxMapRemove(data->properties, ck_key);
+    cxMapRemove(data->properties, ch_key);
     
     resource_free_properties(sn, data->properties);
     data->properties = new_properties;
@@ -452,7 +464,7 @@
  * execute a davql select statement
  */
 DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap) {
-    UcxMempool *mp = ucx_mempool_new(128);
+    CxMempool *mp = cxBasicMempoolCreate(128);
     DavResult result;
     result.result = NULL;
     result.status = 1;
@@ -461,157 +473,158 @@
     if(!args) {
         return result;
     }
-    ucx_mempool_reg_destr(mp, args, (ucx_destructor)dav_ql_free_arglist);
+    util_regdestr(mp, args, (cx_destructor_func)dav_ql_free_arglist);
     
     int isallprop;
-    UcxBuffer *rqbuf = fieldlist2propfindrequest(sn, mp, st->fields, &isallprop);
+    CxBuffer *rqbuf = fieldlist2propfindrequest(sn, mp->allocator, st->fields, &isallprop);
     if(!rqbuf) {
-        ucx_mempool_destroy(mp);
+        cxMempoolDestroy(mp);
         return result;
     }
-    ucx_mempool_reg_destr(mp, rqbuf, (ucx_destructor)ucx_buffer_free);
+    util_regdestr(mp, rqbuf, (cx_destructor_func)cxBufferFree);
     
     // compile field list
-    UcxList *cfieldlist = NULL;
-    UCX_FOREACH(elm, st->fields) {
-        DavQLField *field = elm->data;
-        if(sstrcmp(field->name, S("*")) && sstrcmp(field->name, S("-"))) {
-            // compile field expression
-            UcxBuffer *code = dav_compile_expr(
+    CxList *cfieldlist = cxLinkedListCreate(mp->allocator, NULL, CX_STORE_POINTERS);
+    if(st->fields) {
+        CxIterator i = cxListIterator(st->fields);
+        cx_foreach(DavQLField*, field, i) {
+            if(cx_strcmp(field->name, CX_STR("*")) && cx_strcmp(field->name, CX_STR("-"))) {
+                // compile field expression
+                CxBuffer *code = dav_compile_expr(
+                        sn->context,
+                        mp->allocator,
+                        field->expr,
+                        args);
+                if(!code) {
+                    // TODO: set error string
+                    return result;
+                }
+                DavCompiledField *cfield = cxMalloc(
+                        mp->allocator,
+                        sizeof(DavCompiledField));
+
+                char *ns;
+                char *name;
+                dav_get_property_namespace_str(
                     sn->context,
-                    mp->allocator,
-                    field->expr,
-                    args);
-            if(!code) {
-                // TODO: set error string
-                return result;
-            }
-            ucx_mempool_reg_destr(mp, code, (ucx_destructor)ucx_buffer_free);
-            DavCompiledField *cfield = ucx_mempool_malloc(
-                    mp,
-                    sizeof(DavCompiledField));
-            
-            char *ns;
-            char *name;
-            dav_get_property_namespace_str(
-                sn->context,
-                sstrdup_a(mp->allocator, field->name).ptr,
-                &ns,
-                &name);
-            if(!ns || !name) {
-                // TODO: set error string
-                return result;
-            }
-            cfield->ns = ns;
-            cfield->name = name;
-            cfield->code = code;
-            cfieldlist = ucx_list_append_a(mp->allocator, cfieldlist, cfield);
-        } 
+                    cx_strdup_a(mp->allocator, field->name).ptr,
+                    &ns,
+                    &name);
+                if(!ns || !name) {
+                    // TODO: set error string
+                    return result;
+                }
+                cfield->ns = ns;
+                cfield->name = name;
+                cfield->code = code;
+                cxListAdd(cfieldlist, cfield);
+            } 
+        }
     }
     
     // get path string
     davqlerror_t error;
-    sstr_t path = dav_format_string(mp->allocator, st->path, args, &error);
+    cxmutstr path = dav_format_string(mp->allocator, st->path, args, &error);
     if(error) {
         // TODO: cleanup
-        ucx_mempool_destroy(mp);
+        cxMempoolDestroy(mp);
         return result;
     }
     
     int depth = st->depth == DAV_DEPTH_PLACEHOLDER ?
             dav_ql_getarg_int(args) : st->depth;
     
-    UcxBuffer *where = dav_compile_expr(sn->context, mp->allocator, st->where, args);
+    CxBuffer *where = dav_compile_expr(sn->context, mp->allocator, st->where, args);
     if(st->where && !where) {
         // TODO: cleanup
-        ucx_mempool_destroy(mp);
+        cxMempoolDestroy(mp);
         return result;
     }
-    if(where) {
-        ucx_mempool_reg_destr(mp, where, (ucx_destructor)ucx_buffer_free);
-    }
     
     // compile order criterion
-    UcxList *ordercr = NULL;
-    UCX_FOREACH(elm, st->orderby) {
-        DavQLOrderCriterion *oc = elm->data;
-        DavQLExpression *column = oc->column;
-        //printf("%.*s %s\n", column->srctext.length, column->srctext.ptr, oc->descending ? "desc" : "asc");
-        if(column->type == DAVQL_IDENTIFIER) {
-            // TODO: remove code duplication (add_cmd)
-            davqlresprop_t resprop;
-            sstr_t propertyname = sstrchr(column->srctext, ':');
-            if(propertyname.length > 0) {
-                char *ns;
-                char *name;
-                dav_get_property_namespace_str(
-                        sn->context,
-                        sstrdup_a(mp->allocator, column->srctext).ptr,
-                        &ns,
-                        &name);
-                if(ns && name) {
-                    DavOrderCriterion *cr = ucx_mempool_malloc(mp, sizeof(DavOrderCriterion));
-                    cr->type = 1;
-                    sstr_t keystr = dav_property_key_a(mp->allocator, ns, name);
-                    cr->column.property = ucx_key(keystr.ptr, keystr.length);
-                    cr->descending = oc->descending;
-                    ordercr = ucx_list_append_a(mp->allocator, ordercr, cr);
+    CxList *ordercr = NULL;
+    if(st->orderby) {
+        ordercr = cxLinkedListCreate(mp->allocator, NULL, sizeof(DavOrderCriterion));
+        CxIterator i = cxListIterator(st->orderby);
+        cx_foreach(DavQLOrderCriterion*, oc, i) {
+            DavQLExpression *column = oc->column;
+            //printf("%.*s %s\n", column->srctext.length, column->srctext.ptr, oc->descending ? "desc" : "asc");
+            if(column->type == DAVQL_IDENTIFIER) {
+                // TODO: remove code duplication (add_cmd)
+                davqlresprop_t resprop;
+                cxstring propertyname = cx_strchr(column->srctext, ':');
+                if(propertyname.length > 0) {
+                    char *ns;
+                    char *name;
+                    dav_get_property_namespace_str(
+                            sn->context,
+                            cx_strdup_a(mp->allocator, column->srctext).ptr,
+                            &ns,
+                            &name);
+                    if(ns && name) {
+                        DavOrderCriterion cr;
+                        cr.type = 1;
+                        cxmutstr keystr = dav_property_key_a(mp->allocator, ns, name);
+                        cr.column.property = cx_hash_key(keystr.ptr, keystr.length);
+                        cr.descending = oc->descending;
+                        cxListAdd(ordercr, &cr);
+                    } else {
+                        // error
+                        // TODO: cleanup
+                        cxMempoolDestroy(mp);
+                        return result;
+                    }
+                } else if(dav_identifier2resprop(column->srctext, &resprop)) {
+                    DavOrderCriterion cr;
+                    cr.type = 0;
+                    cr.column.resprop = resprop;
+                    cr.descending = oc->descending;
+                    cxListAdd(ordercr, &cr);
                 } else {
                     // error
                     // TODO: cleanup
-                    ucx_mempool_destroy(mp);
+                    cxMempoolDestroy(mp);
                     return result;
                 }
-            } else if(dav_identifier2resprop(column->srctext, &resprop)) {
-                DavOrderCriterion *cr = ucx_mempool_malloc(mp, sizeof(DavOrderCriterion));
-                cr->type = 0;
-                cr->column.resprop = resprop;
-                cr->descending = oc->descending;
-                ordercr = ucx_list_append_a(mp->allocator, ordercr, cr);
+
+            } else if(column->type == DAVQL_NUMBER) {
+                // TODO: implement
+                fprintf(stderr, "order by number not supported\n");
+                return result;
             } else {
-                // error
+                // something is broken
                 // TODO: cleanup
-                ucx_mempool_destroy(mp);
+                cxMempoolDestroy(mp);
                 return result;
             }
-            
-        } else if(column->type == DAVQL_NUMBER) {
-            // TODO: implement
-            fprintf(stderr, "order by number not supported\n");
-            return result;
-        } else {
-            // something is broken
-            // TODO: cleanup
-            ucx_mempool_destroy(mp);
-            return result;
         }
     }
     
     DavResource *selroot = dav_resource_new(sn, path.ptr);
     
-    UcxList *stack = NULL; // stack with DavResource* elements
+    CxList *stack = cxLinkedListCreateSimple(sizeof(DavQLRes));
     // initialize the stack with the requested resource
-    DavQLRes *res = ucx_mempool_malloc(mp, sizeof(DavQLRes));
-    res->resource = selroot;
-    res->depth = 0;
-    stack = ucx_list_prepend(stack, res);
+    DavQLRes res;
+    res.resource = selroot;
+    res.depth = 0;
+    cxListInsert(stack, 0, &res);
     
     // reuseable response buffer
-    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, mp->allocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     if(!rpbuf) {
         // TODO: cleanup
-        ucx_mempool_destroy(mp);
+        cxMempoolDestroy(mp);
         return result;
     }
-    ucx_mempool_reg_destr(mp, rpbuf, (ucx_destructor)ucx_buffer_free);
     
     result.result = selroot;
     result.status = 0;
     
     // do a propfind request for each resource on the stack
-    while(stack) {
-        DavQLRes *sr = stack->data; // get first element from the stack
-        stack = ucx_list_remove(stack, stack); // remove first element
+    while(stack->size > 0) {
+        DavQLRes *sr = cxListAt(stack, 0); // get first element from the stack
+        cxListRemove(stack, 0);
+        cxListRemove(stack, 0); // remove first element
         DavResource *root = sr->resource;
         
         util_set_url(sn, dav_resource_get_href(sr->resource));
@@ -672,7 +685,7 @@
                         result.result = NULL;
                         result.status = -1;
                         dav_resource_free_all(selroot);
-                        ucx_list_free(stack);
+                        cxListDestroy(stack);
                         break;
                     }
                 } else {
@@ -691,12 +704,10 @@
                                 if(child->iscollection &&
                                     (depth < 0 || depth > sr->depth+1))
                                 {
-                                    DavQLRes *rs = ucx_mempool_malloc(
-                                            mp,
-                                            sizeof(DavQLRes));
-                                    rs->resource = child;
-                                    rs->depth = sr->depth + 1;
-                                    stack = ucx_list_prepend(stack, rs);
+                                    DavQLRes rs;
+                                    rs.resource = child;
+                                    rs.depth = sr->depth + 1;
+                                    cxListInsert(stack, 0, &rs);
                                 }
                             } else {
                                 dav_resource_free(child);
@@ -717,10 +728,10 @@
         }
         
         // reset response buffer
-        ucx_buffer_seek(rpbuf, SEEK_SET, 0);
+        cxBufferSeek(rpbuf, SEEK_SET, 0);
     }
     
-    ucx_mempool_destroy(mp);
+    cxMempoolDestroy(mp);
     return result;
 }
 
@@ -738,22 +749,22 @@
     return count;
 }
 
-int dav_identifier2resprop(sstr_t src, davqlresprop_t *prop) {
-    if(!sstrcmp(src, S("name"))) {
+int dav_identifier2resprop(cxstring src, davqlresprop_t *prop) {
+    if(!cx_strcmp(src, CX_STR("name"))) {
         *prop = DAVQL_RES_NAME;
-    } else if(!sstrcmp(src, S("path"))) {
+    } else if(!cx_strcmp(src, CX_STR("path"))) {
         *prop = DAVQL_RES_PATH;
-    } else if(!sstrcmp(src, S("href"))) {
+    } else if(!cx_strcmp(src, CX_STR("href"))) {
         *prop = DAVQL_RES_HREF;
-    } else if(!sstrcmp(src, S("contentlength"))) {
+    } else if(!cx_strcmp(src, CX_STR("contentlength"))) {
         *prop = DAVQL_RES_CONTENTLENGTH;
-    } else if(!sstrcmp(src, S("contenttype"))) {
+    } else if(!cx_strcmp(src, CX_STR("contenttype"))) {
         *prop = DAVQL_RES_CONTENTTYPE;
-    } else if(!sstrcmp(src, S("creationdate"))) {
+    } else if(!cx_strcmp(src, CX_STR("creationdate"))) {
         *prop = DAVQL_RES_CREATIONDATE;
-    } else if(!sstrcmp(src, S("lastmodified"))) {
+    } else if(!cx_strcmp(src, CX_STR("lastmodified"))) {
         *prop = DAVQL_RES_LASTMODIFIED;
-    } else if(!sstrcmp(src, S("iscollection"))) {
+    } else if(!cx_strcmp(src, CX_STR("iscollection"))) {
         *prop = DAVQL_RES_ISCOLLECTION;
     } else {
         return 0;
@@ -761,7 +772,7 @@
     return 1;
 }
 
-static int add_cmd(DavContext *ctx, UcxAllocator *a, UcxBuffer *bcode, DavQLExpression *expr, DavQLArgList *ap) {
+static int add_cmd(DavContext *ctx, const CxAllocator *a, CxBuffer *bcode, DavQLExpression *expr, DavQLArgList *ap) {
     if(!expr) {
         return 0;
     }
@@ -771,7 +782,7 @@
     memset(&cmd, 0, sizeof(DavQLCmd));
     davqlerror_t error;
     
-    sstr_t src = expr->srctext;
+    cxstring src = expr->srctext;
     switch(expr->type) {
         default: break;
         case DAVQL_NUMBER: {   
@@ -779,7 +790,7 @@
             if(src.ptr[0] == '%') {
                 cmd.data.integer = dav_ql_getarg_int(ap);
             } else if(util_strtoint(src.ptr, &cmd.data.integer)) {
-                ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
             } else {
                 // error
                 return -1;
@@ -790,14 +801,14 @@
         case DAVQL_STRING: {
             cmd.type = DAVQL_CMD_STRING;
             cmd.data.string = dav_format_string(a, src, ap, &error);
-            ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+            cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
             break;
         }
         case DAVQL_TIMESTAMP: {
             if(src.ptr[0] == '%') {
                 cmd.type = DAVQL_CMD_TIMESTAMP;
                 cmd.data.timestamp = dav_ql_getarg_time(ap);
-                ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
             } else {
                 // error
                 return -1;
@@ -805,7 +816,7 @@
             break;
         }
         case DAVQL_IDENTIFIER: {
-            sstr_t propertyname = sstrchr(src, ':');
+            cxstring propertyname = cx_strchr(src, ':');
             cmd.type = DAVQL_CMD_RES_IDENTIFIER;
             if(propertyname.length > 0) {
                 cmd.type = DAVQL_CMD_PROP_IDENTIFIER;
@@ -813,7 +824,7 @@
                 char *name;
                 dav_get_property_namespace_str(
                         ctx,
-                        sstrdup_a(a, src).ptr,
+                        cx_strdup_a(a, src).ptr,
                         &ns,
                         &name);
                 if(ns && name) {
@@ -824,10 +835,10 @@
                     return -1;
                 }
             } else if(!dav_identifier2resprop(src, &cmd.data.resprop)) {
-                if(!sstrcmp(src, S("true"))) {
+                if(!cx_strcmp(src, CX_STR("true"))) {
                     cmd.type = DAVQL_CMD_INT;
                     cmd.data.integer = 1;
-                } else if(!sstrcmp(src, S("false"))) {
+                } else if(!cx_strcmp(src, CX_STR("false"))) {
                     cmd.type = DAVQL_CMD_INT;
                     cmd.data.integer = 0;
                 } else {
@@ -835,7 +846,7 @@
                     return -1;
                 }
             }
-            ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+            cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
             break;
         }
         case DAVQL_UNARY: {
@@ -848,12 +859,12 @@
                 }
                 case DAVQL_SUB: {
                     cmd.type = DAVQL_CMD_OP_UNARY_SUB;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_NEG: {
                     cmd.type = DAVQL_CMD_OP_UNARY_NEG;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 default: break;
@@ -894,7 +905,7 @@
                 }
                 default: break;
             }
-            ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+            cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
             break;
         }
         case DAVQL_LOGICAL: {
@@ -907,12 +918,12 @@
                 case DAVQL_NOT: {
                     numcmd += add_cmd(ctx, a, bcode, expr->left, ap);
                     cmd.type = DAVQL_CMD_OP_LOGICAL_NOT;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_LAND: {
                     cmd.type = DAVQL_CMD_OP_LOGICAL_AND;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_LOR: {
@@ -920,61 +931,61 @@
                     
                     cmd.type = DAVQL_CMD_OP_LOGICAL_OR_L;
                     DavQLCmd *or_l = (DavQLCmd*)(bcode->space + bcode->pos);
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     
                     int nright = add_cmd(ctx, a, bcode, expr->right, ap);
                     or_l->data.integer = nright + 1;
                     
                     cmd.type = DAVQL_CMD_OP_LOGICAL_OR;
                     cmd.data.integer = 0;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     
                     numcmd += nleft + nright;
                     break;
                 }
                 case DAVQL_LXOR: {
                     cmd.type = DAVQL_CMD_OP_LOGICAL_XOR;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_EQ: {
                     cmd.type = DAVQL_CMD_OP_EQ;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_NEQ: {
                     cmd.type = DAVQL_CMD_OP_NEQ;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_LT: {
                     cmd.type = DAVQL_CMD_OP_LT;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_GT: {
                     cmd.type = DAVQL_CMD_OP_GT;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_LE: {
                     cmd.type = DAVQL_CMD_OP_LE;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_GE: {
                     cmd.type = DAVQL_CMD_OP_GE;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_LIKE: {
                     cmd.type = DAVQL_CMD_OP_LIKE;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 case DAVQL_UNLIKE: {
                     cmd.type = DAVQL_CMD_OP_UNLIKE;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     break;
                 }
                 default: break;
@@ -995,12 +1006,12 @@
                     // numargs
                     cmd.type = DAVQL_CMD_INT;
                     cmd.data.integer = count_func_args(expr);
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     
                     // TODO: resolve function name
                     cmd.type = DAVQL_CMD_CALL;
                     cmd.data.func = NULL;
-                    ucx_buffer_write(&cmd, sizeof(cmd), 1, bcode);
+                    cxBufferWrite(&cmd, sizeof(cmd), 1, bcode);
                     
                     numcmd = 2;
                     numcmd += nright;
@@ -1020,14 +1031,14 @@
     return numcmd;
 }
 
-UcxBuffer* dav_compile_expr(DavContext *ctx, UcxAllocator *a, DavQLExpression *lexpr, DavQLArgList  *ap) {
-    UcxBuffer *bcode = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
+CxBuffer* dav_compile_expr(DavContext *ctx, const CxAllocator *a, DavQLExpression *lexpr, DavQLArgList  *ap) {
+    CxBuffer *bcode = cxBufferCreate(NULL, 512, a, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     if(!bcode) {
         return NULL;
     }
     
     if(add_cmd(ctx, a, bcode, lexpr, ap) <= 0) {
-        ucx_buffer_free(bcode);
+        cxBufferFree(bcode);
         return NULL;
     }
     
@@ -1035,53 +1046,56 @@
 }
 
 static int cmd_str_cmp(DavQLStackObj obj1, DavQLStackObj obj2, davqlcmdtype_t cmd) {
-    sstr_t s1 = obj1.type == 1 ?
-        sstrn(obj1.data.string, obj1.length) :
-        ucx_sprintf("%" PRId64, obj1.data.integer);
-    sstr_t s2 = obj1.type == 1 ?
-        sstrn(obj2.data.string, obj2.length) :
-        ucx_sprintf("%" PRId64, obj2.data.integer);
+    cxmutstr s1m = obj1.type == 1 ?
+        cx_mutstrn(obj1.data.string, obj1.length) :
+        cx_asprintf("%" PRId64, obj1.data.integer);
+    cxmutstr s2m = obj1.type == 1 ?
+        cx_mutstrn(obj2.data.string, obj2.length) :
+        cx_asprintf("%" PRId64, obj2.data.integer);
+    
+    cxstring s1 = cx_strcast(s1m);
+    cxstring s2 = cx_strcast(s2m);
     
     int res = 0;
     switch(cmd) {
         case DAVQL_CMD_OP_EQ: {
-            res = sstrcmp(s1, s2) == 0;
+            res = cx_strcmp(s1, s2) == 0;
             break;
         }
         case DAVQL_CMD_OP_NEQ: {
-            res = sstrcmp(s1, s2) != 0;
+            res = cx_strcmp(s1, s2) != 0;
             break;
         }
         case DAVQL_CMD_OP_LT: {
-            res = sstrcmp(s1, s2) < 0;
+            res = cx_strcmp(s1, s2) < 0;
             break;
         }
         case DAVQL_CMD_OP_GT: {
-            res = sstrcmp(s1, s2) > 0;
+            res = cx_strcmp(s1, s2) > 0;
             break;
         }
         case DAVQL_CMD_OP_LE: {
-            res  = sstrcmp(s1, s2) <= 0;
+            res  = cx_strcmp(s1, s2) <= 0;
             break;
         }
         case DAVQL_CMD_OP_GE: {
-            res = sstrcmp(s1, s2) >= 0;
+            res = cx_strcmp(s1, s2) >= 0;
             break;
         }
         default: break;
     }
     
     if(obj1.type == 0) {
-        free(s1.ptr);
+        free(s1m.ptr);
     }
     if(obj2.type == 0) {
-        free(s2.ptr);
+        free(s2m.ptr);
     }
     
     return res;
 }
 
-int dav_exec_expr(UcxBuffer *bcode, DavResource *res, DavQLStackObj *result) {
+int dav_exec_expr(CxBuffer *bcode, DavResource *res, DavQLStackObj *result) {
     if(!bcode) {
         result->type = 0;
         result->length = 0;
--- a/libidav/davqlexec.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/davqlexec.h	Fri Apr 21 21:25:32 2023 +0200
@@ -32,7 +32,7 @@
 #include "davqlparser.h"
 #include "webdav.h"
 
-#include <ucx/buffer.h>
+#include <cx/buffer.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -66,7 +66,8 @@
 typedef enum {
     DAVQL_OK = 0,
     DAVQL_UNSUPPORTED_FORMATCHAR,
-    DAVQL_UNKNOWN_FORMATCHAR
+    DAVQL_UNKNOWN_FORMATCHAR,
+    DAVQL_OOM
 } davqlerror_t;
 
 typedef enum {
@@ -116,7 +117,7 @@
     davqlcmdtype_t type;
     union DavQLCmdData {
         int64_t        integer;
-        sstr_t         string;
+        cxmutstr       string;
         time_t         timestamp;
         davqlresprop_t resprop;
         DavPropName    property;
@@ -142,14 +143,14 @@
 typedef struct DavCompiledField {
     char *ns;
     char *name;
-    UcxBuffer *code;
+    CxBuffer *code;
 } DavCompiledField;
 
 typedef struct DavOrderCriterion {
     int type; // 0: resprop, 1: property
     union DavQLColumn {
         davqlresprop_t resprop;
-        UcxKey property;
+        CxHashKey property;
     } column;
     _Bool descending;
 } DavOrderCriterion;
@@ -165,16 +166,16 @@
 DavResult dav_statement_exec(DavSession *sn, DavQLStatement *st, ...);
 DavResult dav_statement_execv(DavSession *sn, DavQLStatement *st, va_list ap);
 
-UcxBuffer* dav_path_string(sstr_t src, DavQLArgList *args, davqlerror_t *error);
-sstr_t dav_format_string(UcxAllocator *a, sstr_t fstr, DavQLArgList *ap, davqlerror_t *error);
+CxBuffer* dav_path_string(cxmutstr src, DavQLArgList *args, davqlerror_t *error);
+cxmutstr dav_format_string(const CxAllocator *a, cxstring fstr, DavQLArgList *ap, davqlerror_t *error);
 
 DavResult dav_exec_select(DavSession *sn, DavQLStatement *st, va_list ap);
 
-int dav_identifier2resprop(sstr_t src, davqlresprop_t *prop);
+int dav_identifier2resprop(cxstring src, davqlresprop_t *prop);
 
-UcxBuffer* dav_compile_expr(DavContext *ctx, UcxAllocator *a, DavQLExpression *lexpr, DavQLArgList *ap);
+CxBuffer* dav_compile_expr(DavContext *ctx, const CxAllocator *a, DavQLExpression *lexpr, DavQLArgList *ap);
 
-int dav_exec_expr(UcxBuffer *bcode, DavResource *res, DavQLStackObj *result);
+int dav_exec_expr(CxBuffer *bcode, DavResource *res, DavQLStackObj *result);
 
 
 
--- a/libidav/davqlparser.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/davqlparser.c	Fri Apr 21 21:25:32 2023 +0200
@@ -27,7 +27,9 @@
  */
 
 #include "davqlparser.h"
-#include <ucx/utils.h>
+#include <cx/utils.h>
+#include <cx/linked_list.h>
+#include <cx/printf.h>
 #include <string.h>
 #include <stdio.h>
 #include <ctype.h>
@@ -93,9 +95,9 @@
 static void dav_debug_ql_fnames_print(DavQLStatement *stmt) {
     if (stmt->fields) {
         printf("Field names: ");
-        UCX_FOREACH(field, stmt->fields) {
-            DavQLField *f = field->data;
-            printf("%.*s, ", sfmtarg(f->name));
+        CxIterator i = cxListIterator(stmt->fields);
+        cx_foreach(DavQLField *, f, i) {
+            printf("%.*s, ", (int)f->name.length, f->name.ptr);
         }
         printf("\b\b  \b\b\n");
     }
@@ -103,10 +105,10 @@
 
 static void dav_debug_ql_stmt_print(DavQLStatement *stmt) {
     // Basic information
-    size_t fieldcount = ucx_list_size(stmt->fields);
+    size_t fieldcount = stmt->fields ? stmt->fields->size : 0;
     int specialfield = 0;
-    if (stmt->fields) {
-        DavQLField* firstfield = (DavQLField*)stmt->fields->data;
+    if (stmt->fields && stmt->fields->size > 0) {
+        DavQLField* firstfield = (DavQLField*)cxListAt(stmt->fields, 0);
         if (firstfield->expr->type == DAVQL_IDENTIFIER) {
             switch (firstfield->expr->srctext.ptr[0]) {
             case '*': specialfield = 1; break;
@@ -118,14 +120,14 @@
         fieldcount--;
     }
     printf("Statement: %.*s\nType: %s\nField count: %zu %s\n",
-        sfmtarg(stmt->srctext),
+        (int)stmt->srctext.length, stmt->srctext.ptr,
         _map_querytype(stmt->type),
         fieldcount,
         _map_specialfield(specialfield));
     
     dav_debug_ql_fnames_print(stmt);
     printf("Path: %.*s\nHas where clause: %s\n",
-        sfmtarg(stmt->path),
+        (int)stmt->path.length, stmt->path.ptr,
         stmt->where ? "yes" : "no");
     
     // WITH attributes
@@ -140,11 +142,11 @@
     // order by clause
     printf("Order by: ");
     if (stmt->orderby) {
-        UCX_FOREACH(crit, stmt->orderby) {
-            DavQLOrderCriterion *critdata = crit->data;
-            printf("%.*s %s%s", sfmtarg(critdata->column->srctext),
+        CxIterator i = cxListIterator(stmt->orderby);
+        cx_foreach(DavQLOrderCriterion*, critdata, i) {
+            printf("%.*s %s%s", (int)critdata->column->srctext.length, critdata->column->srctext.ptr,
                 critdata->descending ? "desc" : "asc",
-                crit->next ? ", " : "\n");
+                i.index+1 < stmt->orderby->size ? ", " : "\n");
         }
     } else {
         printf("nothing\n");
@@ -168,7 +170,7 @@
 
 static void dav_debug_ql_expr_print(DavQLExpression *expr) {
     if (dav_debug_ql_expr_selected(expr)) {
-        sstr_t empty = ST("(empty)");
+        cxstring empty = CX_STR("(empty)");
         printf(
             "Text: %.*s\nType: %s\nOperator: %s\n",
             sfmtarg(expr->srctext),
@@ -280,7 +282,7 @@
     }
     
     DavQLExpression *examineexpr = NULL;
-    UcxList *examineelem = NULL;
+    CxList *examineelem = NULL;
     int examineclause = 0;
     
     while(1) {
@@ -294,8 +296,8 @@
         case DQLD_CMD_F:
             examineclause = DQLD_CMD_F;
             examineelem = stmt->fields;
-            if (stmt->fields) {
-                DavQLField* field = ((DavQLField*)stmt->fields->data);
+            if (stmt->fields && stmt->fields->size > 0) {
+                DavQLField* field = cxListAt(stmt->fields, 0);
                 examineexpr = field->expr;
                 dav_debug_ql_field_print(field);
             } else {
@@ -310,14 +312,16 @@
         case DQLD_CMD_O:
             examineclause = DQLD_CMD_O;
             examineelem = stmt->orderby;
-            examineexpr = stmt->orderby ?
-                ((DavQLOrderCriterion*)stmt->orderby->data)->column : NULL;
+            examineexpr = stmt->orderby && stmt->orderby->size > 0 ?
+                ((DavQLOrderCriterion*)cxListAt(stmt->orderby, 0))->column : NULL;
             dav_debug_ql_expr_print(examineexpr);
             break;
         case DQLD_CMD_N:
         case DQLD_CMD_P:
+            printf("TODO: port code to ucx 3\n");
+            /*
             if (examineelem) {
-                UcxList *newelem = (cmd == DQLD_CMD_N ?
+                CxList *newelem = (cmd == DQLD_CMD_N ?
                     examineelem->next : examineelem->prev);
                 if (newelem) {
                     examineelem = newelem;
@@ -338,6 +342,7 @@
             } else {
                 printf("Currently not examining an expression list.\n");
             }
+            */
             break;
         case DQLD_CMD_L:
             if (dav_debug_ql_expr_selected(examineexpr)) {
@@ -411,28 +416,28 @@
 #define _error_invalid_string "string expected " _error_context
 #define _error_invalid_order_criterion "invalid order criterion " _error_context
 
-#define token_sstr(token) (((DavQLToken*)(token)->data)->value)
+#define token_sstr(token) ((token)->value)
 
 static void dav_error_in_context(int errorcode, const char *errormsg,
-        DavQLStatement *stmt, UcxList *token) {
+        DavQLStatement *stmt, DavQLToken *token) {
     
     // we try to achieve two things: get as many information as possible
     // and recover the concrete source string (and not the token strings)
-    sstr_t emptystring = ST("");
-    sstr_t prev = token->prev ? (token->prev->prev ?
+    cxstring emptystring = CX_STR("");
+    cxstring prev = token->prev ? (token->prev->prev ?
         token_sstr(token->prev->prev) : token_sstr(token->prev))
         : emptystring;
-    sstr_t tokenstr = token_sstr(token);
-    sstr_t next = token->next ? (token->next->next ?
+    cxstring tokenstr = token_sstr(token);
+    cxstring next = token->next ? (token->next->next ?
         token_sstr(token->next->next) : token_sstr(token->next))
         : emptystring;
     
     int lp = prev.length == 0 ? 0 : tokenstr.ptr-prev.ptr;
-    char *pn = tokenstr.ptr + tokenstr.length;
+    const char *pn = tokenstr.ptr + tokenstr.length;
     int ln = next.ptr+next.length - pn;
     
     stmt->errorcode = errorcode;
-    stmt->errormessage = ucx_sprintf(errormsg,
+    stmt->errormessage = cx_asprintf(errormsg,
         lp, prev.ptr,
         sfmtarg(tokenstr),
         ln, pn).ptr;
@@ -448,30 +453,18 @@
 #define dqlsec_mallocz(stmt, ptr, type) \
         dqlsec_alloc_failed(ptr = calloc(1, sizeof(type)), stmt)
 
-#define dqlsec_list_append_or_free(stmt, list, data)            \
-    do {                                                        \
-        UcxList *_dqlsecbak_ = list;                            \
-        list = ucx_list_append(list, data);                     \
-        if (!list) {                                            \
-            free(data);                                         \
-            data = NULL;                                        \
-            (stmt)->errorcode = DAVQL_ERROR_OUT_OF_MEMORY;      \
-            list = _dqlsecbak_;                                 \
-            return 0;                                           \
-        }                                                       \
-    } while(0)
 
 // special symbols are single tokens - the % sign MUST NOT be a special symbol
 static const char *special_token_symbols = ",()+-*/&|^~=!<>";
 
 static _Bool iskeyword(DavQLToken *token) {
-    sstr_t keywords[] ={ST("select"), ST("set"), ST("from"), ST("at"), ST("as"),
-        ST("where"), ST("anywhere"), ST("like"), ST("unlike"), ST("and"),
-        ST("or"), ST("not"), ST("xor"), ST("with"), ST("infinity"),
-        ST("order"), ST("by"), ST("asc"), ST("desc")
+    cxstring keywords[] ={CX_STR("select"), CX_STR("set"), CX_STR("from"), CX_STR("at"), CX_STR("as"),
+        CX_STR("where"), CX_STR("anywhere"), CX_STR("like"), CX_STR("unlike"), CX_STR("and"),
+        CX_STR("or"), CX_STR("not"), CX_STR("xor"), CX_STR("with"), CX_STR("infinity"),
+        CX_STR("order"), CX_STR("by"), CX_STR("asc"), CX_STR("desc")
     };
-    for (int i = 0 ; i < sizeof(keywords)/sizeof(sstr_t) ; i++) {
-        if (!sstrcasecmp(token->value, keywords[i])) {
+    for (int i = 0 ; i < sizeof(keywords)/sizeof(cxstring) ; i++) {
+        if (!cx_strcasecmp(token->value, keywords[i])) {
             return 1;
         }
     }
@@ -479,18 +472,45 @@
 }
 
 static _Bool islongoperator(DavQLToken *token) {
-    sstr_t operators[] = {ST("and"), ST("or"), ST("not"), ST("xor"),
-        ST("like"), ST("unlike")
+    cxstring operators[] = {CX_STR("and"), CX_STR("or"), CX_STR("not"), CX_STR("xor"),
+        CX_STR("like"), CX_STR("unlike")
     };
-    for (int i = 0 ; i < sizeof(operators)/sizeof(sstr_t) ; i++) {
-        if (!sstrcasecmp(token->value, operators[i])) {
+    for (int i = 0 ; i < sizeof(operators)/sizeof(cxstring) ; i++) {
+        if (!cx_strcasecmp(token->value, operators[i])) {
             return 1;
         }
     }
     return 0;
 }
 
-static UcxList* dav_parse_add_token(UcxList *tokenlist, DavQLToken *token) {
+static int dav_stmt_add_field(DavQLStatement *stmt, DavQLField *field) {
+    if(!stmt->fields) {
+        stmt->fields = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+        if(!stmt->fields) {
+            stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY;
+            return 1;
+        }
+    }
+    
+    if(cxListAdd(stmt->fields, field)) {
+        stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY;
+            return 1;
+    }
+    
+    return 0;
+}
+    
+
+static void tokenlist_free(DavQLToken *tokenlist) {
+    DavQLToken *token = tokenlist;
+    while(token) {
+        DavQLToken *next = token->next;
+        free(token);
+        token = next;
+    }
+}
+
+static int dav_parse_add_token(DavQLToken **begin, DavQLToken **end, DavQLToken *token) {
     
     // determine token class (order of if-statements is very important!)
     char firstchar = token->value.ptr[0];
@@ -541,24 +561,22 @@
         }
     }
     
-    
-    UcxList *ret = ucx_list_append(tokenlist, token);
-    if (ret) {
-        return ret;
-    } else {
-        ucx_list_free(tokenlist);
-        return NULL;
-    }
+    cx_linked_list_add((void**)begin, (void**)end, offsetof(DavQLToken, prev), offsetof(DavQLToken, next), token);
+    return 0;
 }
 
-static UcxList* dav_parse_tokenize(sstr_t src) {
-#define alloc_token() do {token = malloc(sizeof(DavQLToken));\
-        if(!token) {ucx_list_free(tokens); return NULL;}} while(0)
-#define add_token() do {tokens = dav_parse_add_token(tokens, token); \
-        if(!tokens) {return NULL;}} while(0)
-    UcxList *tokens = NULL;
+
+
+static DavQLToken* dav_parse_tokenize(cxstring src) {
+#define alloc_token() do {token = calloc(1, sizeof(DavQLToken));\
+        if(!token) {tokenlist_free(tokens_begin); return NULL;}} while(0)
+#define add_token() if(dav_parse_add_token(&tokens_begin, &tokens_end, token)) return NULL;
+    
+    DavQLToken *tokens_begin = NULL;
+    DavQLToken *tokens_end = NULL;
     
     DavQLToken *token = NULL;
+    
     char insequence = '\0';
     for (size_t i = 0 ; i < src.length ; i++) {
         // quoted strings / identifiers are a single token
@@ -628,14 +646,10 @@
     
     alloc_token();
     token->tokenclass = DAVQL_TOKEN_END;
-    token->value = S("");
-    UcxList *ret = ucx_list_append(tokens, token);
-    if (ret) {
-        return ret;
-    } else {
-        ucx_list_free(tokens);
-        return NULL;
-    }
+    token->value = CX_STR("");
+    
+    cx_linked_list_add((void**)&tokens_begin, (void**)&tokens_end, offsetof(DavQLToken, prev), offsetof(DavQLToken, next), token);
+    return tokens_begin;
 #undef alloc_token
 #undef add_token
 }
@@ -661,18 +675,17 @@
     if (crit->column) { // do it null-safe though column is expected to be set
         dav_free_expression(crit->column);
     }
-    free(crit);
 }
 
 #define token_is(token, expectedclass) (token && \
-    (((DavQLToken*)(token)->data)->tokenclass == expectedclass))
+    (token->tokenclass == expectedclass))
 
 #define tokenvalue_is(token, expectedvalue) (token && \
-    !sstrcasecmp(((DavQLToken*)(token)->data)->value, S(expectedvalue)))
+    !cx_strcasecmp(token->value, cx_str(expectedvalue)))
 
-typedef int(*exprparser_f)(DavQLStatement*,UcxList*,DavQLExpression*);
+typedef int(*exprparser_f)(DavQLStatement*,DavQLToken*,DavQLExpression*);
 
-static int dav_parse_binary_expr(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_binary_expr(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr, exprparser_f parseL, char* opc, int* opv,
         exprparser_f parseR) {
     
@@ -692,7 +705,7 @@
         return 0;
     }
     total_consumed += consumed;
-    token = ucx_list_get(token, consumed);
+    token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
 
     char *op;
     if (token_is(token, DAVQL_TOKEN_OPERATOR) &&
@@ -731,15 +744,20 @@
     return total_consumed;
 }
 
-static void dav_add_fmt_args(DavQLStatement *stmt, sstr_t str) {
+static void fmt_args_add(DavQLStatement *stmt, void *data) {
+    if(!stmt->args) {
+        stmt->args = cxLinkedListCreateSimple(CX_STORE_POINTERS);
+    }
+    cxListAdd(stmt->args, data);
+}
+
+static void dav_add_fmt_args(DavQLStatement *stmt, cxstring str) {
     int placeholder = 0;
     for (size_t i=0;i<str.length;i++) {
         char c = str.ptr[i];
         if (placeholder) {
             if (c != '%') {
-                stmt->args = ucx_list_append(
-                        stmt->args,
-                        (void*)(intptr_t)c);
+                fmt_args_add(stmt, (void*)(intptr_t)c);
             }
             placeholder = 0;
         } else if (c == '%') {
@@ -748,7 +766,7 @@
     }
 }
 
-static int dav_parse_literal(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_literal(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr) {
     
     expr->srctext = token_sstr(token);
@@ -772,7 +790,7 @@
             return 0;
         }
         // add fmtspec type to query arg list
-        stmt->args = ucx_list_append(stmt->args, (void*)(intptr_t)expr->srctext.ptr[1]);
+        fmt_args_add(stmt, (void*)(intptr_t)expr->srctext.ptr[1]);
     } else {
         return 0;
     }
@@ -781,10 +799,10 @@
 }
 
 // forward declaration
-static int dav_parse_expression(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_expression(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr);
 
-static int dav_parse_arglist(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_arglist(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr) {
     
     expr->srctext.ptr = token_sstr(token).ptr;
@@ -796,7 +814,7 @@
     // RULE:    Expression, {",", Expression};
     DavQLExpression *arglist = expr;
     DavQLExpression arg;
-    char *lastchar = expr->srctext.ptr;
+    const char *lastchar = expr->srctext.ptr;
     int consumed;
     do {
         memset(&arg, 0, sizeof(DavQLExpression));
@@ -804,7 +822,7 @@
         if (consumed) {
             lastchar = arg.srctext.ptr + arg.srctext.length;
             total_consumed += consumed;
-            token = ucx_list_get(token, consumed);
+            token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
             // look ahead for a comma
             if (token_is(token, DAVQL_TOKEN_COMMA)) {
                 total_consumed++;
@@ -837,7 +855,7 @@
     return total_consumed;
 }
 
-static int dav_parse_funccall(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_funccall(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr) {
     
     // RULE:    Identifier, "(", ArgumentList, ")";
@@ -861,7 +879,7 @@
             return 2;
         }
         if (argtokens) {
-            token = ucx_list_get(token, argtokens);
+            token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), argtokens);
             dqlsec_malloc(stmt, expr->right, DavQLExpression);
             memcpy(expr->right, &arg, sizeof(DavQLExpression));
         } else {
@@ -881,10 +899,10 @@
     }
 }
 
-static int dav_parse_unary_expr(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_unary_expr(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr) {
     
-    UcxList *firsttoken = token; // save for srctext recovery
+    DavQLToken *firsttoken = token; // save for srctext recovery
     
     DavQLExpression* atom = expr;
     int total_consumed = 0;
@@ -923,7 +941,7 @@
                 _error_invalid_expr, stmt, token);
             return 0;
         }
-        token = ucx_list_get(token, consumed);
+        token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
         total_consumed += consumed;
         if (token_is(token, DAVQL_TOKEN_CLOSEP)) {
             token = token->next; total_consumed++;
@@ -951,8 +969,8 @@
     // recover source text
     expr->srctext.ptr = token_sstr(firsttoken).ptr;
     if (total_consumed > 0) {
-        sstr_t lasttoken =
-            token_sstr(ucx_list_get(firsttoken, total_consumed-1));
+        cxstring lasttoken =
+            token_sstr((DavQLToken*)cx_linked_list_at(token, 0, offsetof(DavQLToken, next), total_consumed-1));
         expr->srctext.length =
             lasttoken.ptr - expr->srctext.ptr + lasttoken.length;
     } else {
@@ -964,7 +982,7 @@
     return total_consumed;
 }
 
-static int dav_parse_bitexpr(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_bitexpr(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr) {
     
     return dav_parse_binary_expr(stmt, token, expr,
@@ -973,7 +991,7 @@
         dav_parse_bitexpr);
 }
 
-static int dav_parse_multexpr(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_multexpr(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr) {
     
     return dav_parse_binary_expr(stmt, token, expr,
@@ -982,7 +1000,7 @@
         dav_parse_multexpr);
 }
 
-static int dav_parse_expression(DavQLStatement* stmt, UcxList* token,
+static int dav_parse_expression(DavQLStatement* stmt, DavQLToken* token,
         DavQLExpression* expr) {
     
     return dav_parse_binary_expr(stmt, token, expr,
@@ -991,7 +1009,7 @@
         dav_parse_expression);
 }
 
-static int dav_parse_named_field(DavQLStatement *stmt, UcxList *token,
+static int dav_parse_named_field(DavQLStatement *stmt, DavQLToken *token,
         DavQLField *field) {
     int total_consumed = 0, consumed;
     
@@ -1010,7 +1028,7 @@
         return 0;
     }
 
-    token = ucx_list_get(token, consumed);
+    token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
     total_consumed += consumed;    
     
     if (token_is(token, DAVQL_TOKEN_KEYWORD) && tokenvalue_is(token, "as")) {
@@ -1034,13 +1052,16 @@
     }
 }
 
-static int dav_parse_fieldlist(DavQLStatement *stmt, UcxList *token) {
+static int dav_parse_fieldlist(DavQLStatement *stmt, DavQLToken *token) {
     
     // RULE:    "-"
     if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "-")) {
         DavQLField *field;
         dqlsec_malloc(stmt, field, DavQLField);
-        dqlsec_list_append_or_free(stmt, stmt->fields, field);
+        if(dav_stmt_add_field(stmt, field)) {
+            free(field);
+            return 0;
+        }
         dqlsec_mallocz(stmt, field->expr, DavQLExpression);
         field->expr->type = DAVQL_IDENTIFIER;
         field->expr->srctext = field->name = token_sstr(token);
@@ -1051,7 +1072,10 @@
     if (token_is(token, DAVQL_TOKEN_OPERATOR) && tokenvalue_is(token, "*")) {
         DavQLField *field;
         dqlsec_malloc(stmt, field, DavQLField);
-        dqlsec_list_append_or_free(stmt, stmt->fields, field);
+        if(dav_stmt_add_field(stmt, field)) {
+            free(field);
+            return 0;
+        }
         dqlsec_mallocz(stmt, field->expr, DavQLExpression);
         field->expr->type = DAVQL_IDENTIFIER;
         field->expr->srctext = field->name = token_sstr(token);
@@ -1060,7 +1084,7 @@
         int consumed = 1;
         
         do {
-            token = ucx_list_get(token, consumed);
+            token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
             total_consumed += consumed;
             
             if (token_is(token, DAVQL_TOKEN_COMMA)) {
@@ -1071,7 +1095,10 @@
                     DavQLField *field;
                     dqlsec_malloc(stmt, field, DavQLField);
                     memcpy(field, &localfield, sizeof(DavQLField));
-                    dqlsec_list_append_or_free(stmt, stmt->fields, field);
+                    if(dav_stmt_add_field(stmt, field)) {
+                        free(field);
+                        return 0;
+                    }
                 }                
             } else {
                 consumed = 0;
@@ -1092,8 +1119,11 @@
                 DavQLField *field;
                 dqlsec_malloc(stmt, field, DavQLField);
                 memcpy(field, &localfield, sizeof(DavQLField));
-                dqlsec_list_append_or_free(stmt, stmt->fields, field);
-                token = ucx_list_get(token, consumed);
+                if(dav_stmt_add_field(stmt, field)) {
+                    free(field);
+                    return 0;
+                }
+                token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
                 total_consumed += consumed;
             } else if (token_is(token, DAVQL_TOKEN_IDENTIFIER)
                 // look ahead, if the field is JUST the identifier
@@ -1105,7 +1135,10 @@
                 dqlsec_mallocz(stmt, field->expr, DavQLExpression);
                 field->expr->type = DAVQL_IDENTIFIER;
                 field->expr->srctext = field->name = token_sstr(token);
-                dqlsec_list_append_or_free(stmt, stmt->fields, field);
+                if(dav_stmt_add_field(stmt, field)) {
+                    free(field);
+                    return 0;
+                }
 
                 consumed = 1;
                 total_consumed++;
@@ -1137,10 +1170,10 @@
 }
 
 // forward declaration
-static int dav_parse_logical_expr(DavQLStatement *stmt, UcxList *token,
+static int dav_parse_logical_expr(DavQLStatement *stmt, DavQLToken *token,
         DavQLExpression *expr);
 
-static int dav_parse_bool_prim(DavQLStatement *stmt, UcxList *token,
+static int dav_parse_bool_prim(DavQLStatement *stmt, DavQLToken *token,
         DavQLExpression *expr) {
     
     expr->type = DAVQL_LOGICAL;
@@ -1154,9 +1187,9 @@
     if (!total_consumed || stmt->errorcode) {
         return 0;
     }
-    token = ucx_list_get(token, total_consumed);
+    token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), total_consumed);
 
-    UcxList* optok = token;
+    DavQLToken* optok = token;
     // RULE:    Expression, (" like " | " unlike "), String
     if (token_is(optok, DAVQL_TOKEN_OPERATOR) && (tokenvalue_is(optok,
             "like") || tokenvalue_is(optok, "unlike"))) {
@@ -1248,7 +1281,7 @@
     }
 }
 
-static int dav_parse_bool_expr(DavQLStatement *stmt, UcxList *token,
+static int dav_parse_bool_expr(DavQLStatement *stmt, DavQLToken *token,
         DavQLExpression *expr) {
     
     // RULE:    "not ", LogicalExpression
@@ -1264,7 +1297,7 @@
             return 0;
         }
         if (consumed) {
-            sstr_t lasttok = token_sstr(ucx_list_get(token, consumed-1));
+            cxstring lasttok = token_sstr((DavQLToken*)cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed-1));
             expr->srctext.length =
                 lasttok.ptr - expr->srctext.ptr + lasttok.length;
             return consumed + 1;
@@ -1278,7 +1311,7 @@
     else if (token_is(token, DAVQL_TOKEN_OPENP)) {
         int consumed = dav_parse_logical_expr(stmt, token->next, expr);
         if (consumed) {
-            token = ucx_list_get(token->next, consumed);
+            token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
 
             if (token_is(token, DAVQL_TOKEN_CLOSEP)) {
                 token = token->next;
@@ -1301,10 +1334,10 @@
     return dav_parse_bool_prim(stmt, token, expr);
 }
 
-static int dav_parse_logical_expr(DavQLStatement *stmt, UcxList *token,
+static int dav_parse_logical_expr(DavQLStatement *stmt, DavQLToken *token,
         DavQLExpression *expr) {
     
-    UcxList *firsttoken = token;
+    DavQLToken *firsttoken = token;
     int total_consumed = 0;
     
     // RULE:    BooleanLiteral, [LogicalOperator, LogicalExpression];
@@ -1320,7 +1353,7 @@
         return 0;
     }
     total_consumed += consumed;
-    token = ucx_list_get(token, consumed);
+    token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
 
     if (token_is(token, DAVQL_TOKEN_OPERATOR)) {
         expr->type = DAVQL_LOGICAL;
@@ -1354,7 +1387,7 @@
                 return 0;
             }
             total_consumed += consumed;
-            token = ucx_list_get(token, consumed);
+            token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
 
             dqlsec_malloc(stmt, expr->left, DavQLExpression);
             memcpy(expr->left, &left, sizeof(DavQLExpression));
@@ -1368,20 +1401,20 @@
     // set type and recover source text
     if (total_consumed > 0) {        
         expr->srctext.ptr = token_sstr(firsttoken).ptr;
-        sstr_t lasttok = token_sstr(ucx_list_get(firsttoken, total_consumed-1));
+        cxstring lasttok = token_sstr((DavQLToken*)cx_linked_list_at(firsttoken, 0, offsetof(DavQLToken, next), total_consumed-1));
         expr->srctext.length = lasttok.ptr-expr->srctext.ptr+lasttok.length;
     }
     
     return total_consumed;
 }
 
-static int dav_parse_where_clause(DavQLStatement *stmt, UcxList *token) {
+static int dav_parse_where_clause(DavQLStatement *stmt, DavQLToken *token) {
     dqlsec_mallocz(stmt, stmt->where, DavQLExpression);
     
     return dav_parse_logical_expr(stmt, token, stmt->where);
 }
 
-static int dav_parse_with_clause(DavQLStatement *stmt, UcxList *token) {
+static int dav_parse_with_clause(DavQLStatement *stmt, DavQLToken *token) {
 
     int total_consumed = 0;
     
@@ -1404,7 +1437,7 @@
                         if (depthexpr->srctext.ptr[0] == '%') {
                             stmt->depth = DAV_DEPTH_PLACEHOLDER;
                         } else {
-                            sstr_t depthstr = depthexpr->srctext;
+                            cxstring depthstr = depthexpr->srctext;
                             char *conv = malloc(depthstr.length+1);
                             if (!conv) {
                                 dav_free_expression(depthexpr);
@@ -1436,7 +1469,7 @@
     return total_consumed;
 }
 
-static int dav_parse_order_crit(DavQLStatement *stmt, UcxList *token,
+static int dav_parse_order_crit(DavQLStatement *stmt, DavQLToken *token,
     DavQLOrderCriterion *crit) {
     
     // RULE:    (Identifier | Number), [" asc"|" desc"];
@@ -1456,7 +1489,7 @@
     dqlsec_malloc(stmt, crit->column, DavQLExpression);
     memcpy(crit->column, &expr, sizeof(DavQLExpression));
     
-    token = ucx_list_get(token, consumed);
+    token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
     if (token_is(token, DAVQL_TOKEN_KEYWORD) && (
             tokenvalue_is(token, "asc") || tokenvalue_is(token, "desc"))) {
         
@@ -1469,12 +1502,19 @@
     }
 }
 
-static int dav_parse_orderby_clause(DavQLStatement *stmt, UcxList *token) {
+static int dav_parse_orderby_clause(DavQLStatement *stmt, DavQLToken *token) {
     
     int total_consumed = 0, consumed;
     
     DavQLOrderCriterion crit;
     
+    if(!stmt->orderby) {
+        stmt->orderby = cxLinkedListCreateSimple(sizeof(DavQLOrderCriterion));
+        if(!stmt->orderby) {
+            return 0;
+        }
+    }
+    
     // RULE:    OrderByCriterion, {",", OrderByCriterion};
     do {
         consumed = dav_parse_order_crit(stmt, token, &crit);
@@ -1486,13 +1526,13 @@
                 stmt, token);
             return 0;
         }
-        token = ucx_list_get(token, consumed);
+        token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
         total_consumed += consumed;
         
-        DavQLOrderCriterion *criterion;
-        dqlsec_malloc(stmt, criterion, DavQLOrderCriterion);
-        memcpy(criterion, &crit, sizeof(DavQLOrderCriterion));
-        dqlsec_list_append_or_free(stmt, stmt->orderby, criterion);
+        if(cxListAdd(stmt->orderby, &crit)) {
+            stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY;
+            return 0;
+        }
         
         if (token_is(token, DAVQL_TOKEN_COMMA)) {
             total_consumed++;
@@ -1506,7 +1546,7 @@
 }
 
 
-static int dav_parse_assignments(DavQLStatement *stmt, UcxList *token) {
+static int dav_parse_assignments(DavQLStatement *stmt, DavQLToken *token) {
     
     // RULE:    Assignment, {",", Assignment}
     int total_consumed = 0, consumed;
@@ -1540,11 +1580,14 @@
                 dav_free_field(field);
                 return total_consumed;
             }
-            token = ucx_list_get(token, consumed);
+            token = cx_linked_list_at(token, 0, offsetof(DavQLToken, next), consumed);
             total_consumed += consumed;
             
             // Add assignment to list and check if there's another one
-            dqlsec_list_append_or_free(stmt, stmt->fields, field);
+            if(dav_stmt_add_field(stmt, field)) {
+                free(field);
+                return 0;
+            }
             consumed = token_is(token, DAVQL_TOKEN_COMMA) ? 1 : 0;
             if (consumed) {
                 token = token->next;
@@ -1560,7 +1603,7 @@
     return total_consumed;
 }
 
-static int dav_parse_path(DavQLStatement *stmt, UcxList *tokens) {
+static int dav_parse_path(DavQLStatement *stmt, DavQLToken *tokens) {
     if (token_is(tokens, DAVQL_TOKEN_STRING)) {
         stmt->path = token_sstr(tokens);
         tokens = tokens->next;
@@ -1572,7 +1615,7 @@
         int consumed = 1;
         while (!token_is(tokens, DAVQL_TOKEN_KEYWORD) &&
                 !token_is(tokens, DAVQL_TOKEN_END)) {
-            sstr_t toksstr = token_sstr(tokens);
+            cxstring toksstr = token_sstr(tokens);
             stmt->path.length = toksstr.ptr-stmt->path.ptr+toksstr.length;
             tokens = tokens->next;
             consumed++;
@@ -1582,7 +1625,7 @@
             tokenvalue_is(tokens, "%s")) {
         stmt->path = token_sstr(tokens);
         tokens = tokens->next;
-        stmt->args = ucx_list_append(stmt->args, (void*)(intptr_t)'s');
+        fmt_args_add(stmt, (void*)(intptr_t)'s');
         return 1;
     } else {
         dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
@@ -1596,11 +1639,11 @@
  * @param stmt the statement object that shall contain the syntax tree
  * @param tokens the token list
  */
-static void dav_parse_select_statement(DavQLStatement *stmt, UcxList *tokens) {
+static void dav_parse_select_statement(DavQLStatement *stmt, DavQLToken *tokens) {
     stmt->type = DAVQL_SELECT;
 
     // Consume field list
-    tokens = ucx_list_get(tokens, dav_parse_fieldlist(stmt, tokens));
+    tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_fieldlist(stmt, tokens));
     if (stmt->errorcode) {
         return;
     }
@@ -1616,7 +1659,7 @@
     }
     
     // Consume path
-    tokens = ucx_list_get(tokens, dav_parse_path(stmt, tokens));
+    tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_path(stmt, tokens));
     if (stmt->errorcode) {
         return;
     }
@@ -1626,7 +1669,7 @@
     if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
             && tokenvalue_is(tokens, "with")) {
         tokens = tokens->next;
-        tokens = ucx_list_get(tokens,
+        tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next),
             dav_parse_with_clause(stmt, tokens));
     }
     if (stmt->errorcode) {
@@ -1637,7 +1680,7 @@
     if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
             && tokenvalue_is(tokens, "where")) {
         tokens = tokens->next;
-        tokens = ucx_list_get(tokens,
+        tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next),
             dav_parse_where_clause(stmt, tokens));
     } else if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
             && tokenvalue_is(tokens, "anywhere")) {
@@ -1656,7 +1699,7 @@
         if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
                 && tokenvalue_is(tokens, "by")) {
             tokens = tokens->next;
-            tokens = ucx_list_get(tokens,
+            tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next),
                 dav_parse_orderby_clause(stmt, tokens));
         } else {
             dav_error_in_context(DAVQL_ERROR_MISSING_TOKEN,
@@ -1680,11 +1723,11 @@
     }
 }
 
-static void dav_parse_set_statement(DavQLStatement *stmt, UcxList *tokens) {
+static void dav_parse_set_statement(DavQLStatement *stmt, DavQLToken *tokens) {
     stmt->type = DAVQL_SET;
     
     // Consume assignments
-    tokens = ucx_list_get(tokens, dav_parse_assignments(stmt, tokens));
+    tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_assignments(stmt, tokens));
     if (stmt->errorcode) {
         return;
     }
@@ -1700,7 +1743,7 @@
     }
 
     // Consume path
-    tokens = ucx_list_get(tokens, dav_parse_path(stmt, tokens));
+    tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), dav_parse_path(stmt, tokens));
     if (stmt->errorcode) {
         return;
     }
@@ -1709,7 +1752,7 @@
     if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
             && tokenvalue_is(tokens, "with")) {
         tokens = tokens->next;
-        tokens = ucx_list_get(tokens,
+        tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), 
             dav_parse_with_clause(stmt, tokens));
     }
     if (stmt->errorcode) {
@@ -1720,7 +1763,7 @@
     if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
             && tokenvalue_is(tokens, "where")) {
         tokens = tokens->next;
-        tokens = ucx_list_get(tokens,
+        tokens = cx_linked_list_at(tokens, 0, offsetof(DavQLToken, next), 
             dav_parse_where_clause(stmt, tokens));
     } else if (token_is(tokens, DAVQL_TOKEN_KEYWORD)
             && tokenvalue_is(tokens, "anywhere")) {
@@ -1734,7 +1777,7 @@
     }
 }
 
-DavQLStatement* dav_parse_statement(sstr_t srctext) {
+DavQLStatement* dav_parse_statement(cxstring srctext) {
     DavQLStatement *stmt = calloc(1, sizeof(DavQLStatement));
     
     // if we can't even get enough memory for the statement object or an error
@@ -1753,11 +1796,11 @@
     stmt->depth = 1;
     
     // save trimmed source text
-    stmt->srctext = sstrtrim(srctext);
+    stmt->srctext = cx_strtrim(srctext);
     
     if (stmt->srctext.length) {   
         // tokenization
-        UcxList* tokens = dav_parse_tokenize(stmt->srctext);
+        DavQLToken* tokens = dav_parse_tokenize(stmt->srctext);
 
         if (tokens) {
             // use first token to determine query type
@@ -1773,10 +1816,7 @@
             }
 
             // free token data
-            UCX_FOREACH(token, tokens) {
-                free(token->data);
-            }
-            ucx_list_free(tokens);
+            tokenlist_free(tokens);
         } else {
             stmt->errorcode = DAVQL_ERROR_OUT_OF_MEMORY;
         }
@@ -1797,10 +1837,10 @@
 }
 
 void dav_free_statement(DavQLStatement *stmt) {
-    UCX_FOREACH(expr, stmt->fields) {
-        dav_free_field(expr->data);
+    if(stmt->fields) {
+        stmt->fields->simple_destructor = (cx_destructor_func)dav_free_field;
+        cxListDestroy(stmt->fields);
     }
-    ucx_list_free(stmt->fields);
     
     if (stmt->where) {
         dav_free_expression(stmt->where);
@@ -1808,10 +1848,13 @@
     if (stmt->errormessage) {
         free(stmt->errormessage);
     }
-    UCX_FOREACH(crit, stmt->orderby) {
-        dav_free_order_criterion(crit->data);
+    
+    if(stmt->orderby) {
+        stmt->orderby->simple_destructor = (cx_destructor_func)dav_free_order_criterion;
+        cxListDestroy(stmt->orderby);
     }
-    ucx_list_free(stmt->orderby);
-    ucx_list_free(stmt->args);
+    if(stmt->args) {
+        cxListDestroy(stmt->args);
+    }
     free(stmt);
 }
--- a/libidav/davqlparser.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/davqlparser.h	Fri Apr 21 21:25:32 2023 +0200
@@ -34,8 +34,8 @@
 #endif
 
 #include <stdint.h>
-#include "ucx/string.h"
-#include "ucx/list.h"
+#include <cx/string.h>
+#include <cx/list.h>
 
 /**
  * Enumeration of possible statement types.
@@ -74,10 +74,13 @@
     DAVQL_LIKE, DAVQL_UNLIKE // comparisons
 } davqloperator_t;
 
-typedef struct {
+typedef struct DavQLToken DavQLToken;    
+struct DavQLToken {
     davqltokenclass_t tokenclass;
-    sstr_t value;
-} DavQLToken;
+    cxstring value;
+    DavQLToken *prev;
+    DavQLToken *next;
+};
 
 /**
  * An expression within a DAVQL query.
@@ -92,7 +95,7 @@
      * The original expression text.
      * Contains the literal value, if type is LITERAL.
      */
-    sstr_t srctext;
+    cxstring srctext;
     /**
      * The expression type.
      */
@@ -139,7 +142,7 @@
      * <li>SET: the identifier</li>
      * </ul>
      */
-    sstr_t name;
+    cxstring name;
     /**
      * The field expression.
      * <ul>
@@ -242,7 +245,7 @@
     /**
      * The original query text.
      */
-    sstr_t srctext;
+    cxstring srctext;
     /**
      * The statement type.
      */
@@ -258,11 +261,11 @@
     /**
      * The list of DavQLFields.
      */
-    UcxList* fields;
+    CxList* fields;
     /**
      * A string that denotes the queried path.
      */
-    sstr_t path;
+    cxstring path;
     /**
      * Logical expression for selection.
      * <code>NULL</code>, if there is no where clause.
@@ -273,7 +276,7 @@
      * This is <code>NULL</code> for SET queries and may be <code>NULL</code>
      * if the result doesn't need to be sorted.
      */
-    UcxList* orderby;
+    CxList* orderby;
     /**
      * The recursion depth for the statement.
      * Defaults to 1.
@@ -284,7 +287,7 @@
     /**
      * A list of all required arguments
      */
-    UcxList* args;
+    CxList* args;
 } DavQLStatement;
 
 /** Infinity recursion depth for a DavQLStatement. */
@@ -350,12 +353,12 @@
  * @param stmt the sstr_t containing the statement
  * @return a DavQLStatement object
  */
-DavQLStatement* dav_parse_statement(sstr_t stmt);
+DavQLStatement* dav_parse_statement(cxstring stmt);
 
 /**
  * Implicitly converts a cstr to a sstr_t and calls dav_parse_statement.
  */
-#define dav_parse_cstr_statement(stmt) dav_parse_statement(S(stmt))
+#define dav_parse_cstr_statement(stmt) dav_parse_statement(cx_str(stmt))
 
 /**
  * Frees a DavQLStatement.
--- a/libidav/methods.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/methods.c	Fri Apr 21 21:25:32 2023 +0200
@@ -36,21 +36,23 @@
 #include "session.h"
 #include "xml.h"
 
-#include <ucx/utils.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
+#include <cx/hash_map.h>
 
 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
 
 
-int dav_buffer_seek(UcxBuffer *b, curl_off_t offset, int origin) {
-    return ucx_buffer_seek(b, offset, origin) == 0 ? 0:CURL_SEEKFUNC_CANTSEEK;
+int dav_buffer_seek(CxBuffer *b, curl_off_t offset, int origin) {
+    return cxBufferSeek(b, offset, origin) == 0 ? 0:CURL_SEEKFUNC_CANTSEEK;
 }
 
 /* ----------------------------- PROPFIND ----------------------------- */
 
 CURLcode do_propfind_request(
         DavSession *sn,
-        UcxBuffer *request,
-        UcxBuffer *response)
+        CxBuffer *request,
+        CxBuffer *response)
 {
     CURL *handle = sn->handle;
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPFIND");
@@ -64,14 +66,15 @@
     CURLcode ret = 0;
     
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
-    curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
-    curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek);
+    curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead);
+    curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek);
     curl_easy_setopt(handle, CURLOPT_READDATA, request); 
     curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
     
-    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite);
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
-    UcxMap *respheaders = ucx_map_new(32);
+    CxMap *respheaders = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
+    respheaders->simple_destructor = free;
     util_capture_header(handle, respheaders);
     
     for(int i=0;i<maxretry;i++) {
@@ -100,7 +103,7 @@
          *    => the server handled our request and we can stop requesting
          */
         char *msdavexterror;
-        msdavexterror = ucx_map_cstr_get(respheaders, "x-msdavext_error");
+        msdavexterror = cxMapGet(respheaders, cx_hash_key_str("x-msdavext_error"));
         int iishack =  depth == 1 &&
             msdavexterror && !strncmp(msdavexterror, "589831;", 7);
         
@@ -113,71 +116,72 @@
     
     // deactivate header capturing and free captured map
     util_capture_header(handle, NULL);
-    ucx_map_free_content(respheaders, free);
-    ucx_map_free(respheaders);
+    cxMapDestroy(respheaders);
        
     return ret;
 }
 
-UcxBuffer* create_allprop_propfind_request(void) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOFREE);
-    sstr_t s;
+CxBuffer* create_allprop_propfind_request(void) {
+    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxstring s;
     
-    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:propfind xmlns:D=\"DAV:\">\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:propfind xmlns:D=\"DAV:\">\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:allprop/></D:propfind>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:allprop/></D:propfind>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     return buf;
 }
 
-UcxBuffer* create_cryptoprop_propfind_request(void) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOFREE);
-    scstr_t s;
+CxBuffer* create_cryptoprop_propfind_request(void) {
+    CxBuffer *buf = cxBufferCreate(NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxstring s;
     
-    s = SC("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = SC("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = SC("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:prop><idav:crypto-prop/></D:prop></D:propfind>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     return buf;
 }
 
-UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties, char *rootelm, DavBool nocrypt) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
-    sstr_t s;
+CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *rootelm, DavBool nocrypt) {
+    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxstring s;
     
     int add_crypto_name = 1;
     int add_crypto_key = 1;
     int add_crypto_hash = 1;
     char *crypto_ns = "idav";
-    UcxMap *namespaces = ucx_map_new(8);
-    UCX_FOREACH(elm, properties) {
-        DavProperty *p = elm->data;
-        if(strcmp(p->ns->name, "DAV:")) {
-            ucx_map_cstr_put(namespaces, p->ns->prefix, p->ns);
-        }
-        
-        // if the properties list contains the idav properties crypto-name
-        // and crypto-key, mark them as existent 
-        if(!strcmp(p->ns->name, DAV_NS)) {
-            if(!strcmp(p->name, "crypto-name")) {
-                add_crypto_name = 0;
-                crypto_ns = p->ns->prefix;
-            } else if(!strcmp(p->name, "crypto-key")) {
-                add_crypto_key = 0;
-                crypto_ns = p->ns->prefix;
-            } else if(!strcmp(p->name, "crypto-hash")) {
-                add_crypto_hash = 0;
-                crypto_ns = p->ns->prefix;
+    CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8);
+    if(properties) {
+        CxIterator i = cxListIterator(properties);
+        cx_foreach(DavProperty*, p, i) {
+            if(strcmp(p->ns->name, "DAV:")) {
+                cxMapPut(namespaces, cx_hash_key_str(p->ns->prefix), p->ns);
+            }
+
+            // if the properties list contains the idav properties crypto-name
+            // and crypto-key, mark them as existent 
+            if(!strcmp(p->ns->name, DAV_NS)) {
+                if(!strcmp(p->name, "crypto-name")) {
+                    add_crypto_name = 0;
+                    crypto_ns = p->ns->prefix;
+                } else if(!strcmp(p->name, "crypto-key")) {
+                    add_crypto_key = 0;
+                    crypto_ns = p->ns->prefix;
+                } else if(!strcmp(p->name, "crypto-hash")) {
+                    add_crypto_hash = 0;
+                    crypto_ns = p->ns->prefix;
+                }
             }
         }
     }
@@ -186,126 +190,126 @@
     if(add_crypto_name && add_crypto_key && DAV_CRYPTO(sn) && !nocrypt) {
         idav_ns.prefix = "idav";
         idav_ns.name = DAV_NS;
-        ucx_map_cstr_put(namespaces, "idav", &idav_ns);
+        cxMapPut(namespaces, cx_hash_key_str("idav"), &idav_ns);
     }
     
-    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     // write root element and namespaces
-    ucx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm);
+    cx_bprintf(buf, "<D:%s xmlns:D=\"DAV:\"", rootelm);
     
-    UcxMapIterator mapi = ucx_map_iterator(namespaces);
-    UcxKey key;
-    DavNamespace *ns;
-    UCX_MAP_FOREACH(key, ns, mapi) {
-        s = S(" xmlns:");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = sstr(ns->prefix);
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = S("=\"");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = sstr(ns->name);
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = S("\"");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    CxIterator mapi = cxMapIteratorValues(namespaces);
+    cx_foreach(DavNamespace*, ns, mapi) {
+        s = CX_STR(" xmlns:");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        s = cx_str(ns->prefix);
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        s = CX_STR("=\"");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        s = cx_str(ns->name);
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        s = CX_STR("\"");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
     }
-    s = S(">\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR(">\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     // default properties
-    s = S("<D:prop>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:prop>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:creationdate />\n<D:getlastmodified />\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:creationdate />\n<D:getlastmodified />\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:getcontentlength />\n<D:getcontenttype />\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:getcontentlength />\n<D:getcontenttype />\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:resourcetype />\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:resourcetype />\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     // crypto properties
     if(DAV_CRYPTO(sn) && !nocrypt) {
         if(add_crypto_name) {
-            ucx_buffer_putc(buf, '<');
-            ucx_buffer_puts(buf, crypto_ns);
-            s = S(":crypto-name />\n");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            cxBufferPut(buf, '<');
+            cxBufferPutString(buf, crypto_ns);
+            s = CX_STR(":crypto-name />\n");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
         }
         if(add_crypto_key) {
-            ucx_buffer_putc(buf, '<');
-            ucx_buffer_puts(buf, crypto_ns);
-            s = S(":crypto-key />\n");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            cxBufferPut(buf, '<');
+            cxBufferPutString(buf, crypto_ns);
+            s = CX_STR(":crypto-key />\n");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
         }
         if(add_crypto_hash) {
-            ucx_buffer_putc(buf, '<');
-            ucx_buffer_puts(buf, crypto_ns);
-            s = S(":crypto-hash />\n");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            cxBufferPut(buf, '<');
+            cxBufferPutString(buf, crypto_ns);
+            s = CX_STR(":crypto-hash />\n");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
         }
     }
     
     // extra properties
-    UCX_FOREACH(elm, properties) {
-        DavProperty *prop = elm->data;
-        s = S("<");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = sstr(prop->ns->prefix);
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = S(":");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = sstr(prop->name);
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = S(" />\n");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    if(properties) {
+        CxIterator i = cxListIterator(properties);
+        cx_foreach(DavProperty*, prop, i) {
+            s = CX_STR("<");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = cx_str(prop->ns->prefix);
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = CX_STR(":");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = cx_str(prop->name);
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = CX_STR(" />\n");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+        }
     }
     
     // end
-    ucx_bprintf(buf, "</D:prop>\n</D:%s>\n", rootelm);
+    cx_bprintf(buf, "</D:prop>\n</D:%s>\n", rootelm);
     
-    ucx_map_free(namespaces);
+    cxMapDestroy(namespaces);
     return buf;
 }
 
-UcxBuffer* create_basic_propfind_request(void) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
-    sstr_t s;
+CxBuffer* create_basic_propfind_request(void) {
+    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxstring s;
     
-    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);  
-    s = S(DAV_NS);
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
-    s = S("\" >\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:propfind xmlns:D=\"DAV:\" xmlns:i=\"");
+    cxBufferWrite(s.ptr, 1, s.length, buf);  
+    s = CX_STR(DAV_NS);
+    cxBufferWrite(s.ptr, 1, s.length, buf);
+    s = CX_STR("\" >\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     // properties
-    s = S("<D:prop>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
-    s = S("<D:resourcetype />\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
-    s = S("<i:crypto-key />\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
-    s = S("<i:crypto-name />\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
-    s = S("<i:crypto-hash />\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
-    s = S("</D:prop>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:prop>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:resourcetype />\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
+    s = CX_STR("<i:crypto-key />\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
+    s = CX_STR("<i:crypto-name />\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
+    s = CX_STR("<i:crypto-hash />\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
+    s = CX_STR("</D:prop>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     // end
-    s = S("</D:propfind>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("</D:propfind>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     return buf;
 }
 
-PropfindParser* create_propfind_parser(UcxBuffer *response, char *url) {
+PropfindParser* create_propfind_parser(CxBuffer *response, char *url) {
     PropfindParser *parser = malloc(sizeof(PropfindParser));
     if(!parser) {
         return NULL;
@@ -349,11 +353,10 @@
     
     char *href = NULL;
     int iscollection = 0;
-    UcxList *properties = NULL; // xmlNode list
     char *crypto_name = NULL; // name set by crypto-name property
     char *crypto_key = NULL;
     
-    result->properties = NULL;
+    result->properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list
     
     xmlNode *node = parser->current->children;
     while(node) {
@@ -380,13 +383,13 @@
                                 // error
                                 return -1;
                             }
-                            sstr_t status_str = sstr((char*)status_node->content);
+                            cxstring status_str = cx_str((char*)status_node->content);
                             if(status_str.length < 13) {
                                 // error
                                 return -1;
                             }
-                            status_str = sstrsubsl(status_str, 9, 3);
-                            if(!sstrcmp(status_str, S("200"))) {
+                            status_str = cx_strsubsl(status_str, 9, 3);
+                            if(!cx_strcmp(status_str, CX_STR("200"))) {
                                 ok = 1;
                             }
                         }
@@ -398,7 +401,7 @@
                     n = prop_node->children;
                     while(n) {
                         if(n->type == XML_ELEMENT_NODE) {
-                            properties = ucx_list_append(properties, n);
+                            cxListAdd(result->properties, n);
                             if(xstreq(n->name, "resourcetype")) {
                                 if(parse_resource_type(n)) {
                                     iscollection = TRUE;
@@ -421,7 +424,6 @@
     
     result->href = util_url_path(href);
     result->iscollection = iscollection;
-    result->properties = properties;
     result->crypto_name = crypto_name;
     result->crypto_key = crypto_key;
     
@@ -442,27 +444,27 @@
 
 void cleanup_response(ResponseTag *result) {
     if(result) {
-        ucx_list_free(result->properties);
+        cxListDestroy(result->properties);
     }
 }
 
-int hrefeq(DavSession *sn, char *href1, char *href2) {
-    sstr_t href_s = sstr(util_url_decode(sn, href1));
-    sstr_t href_r = sstr(util_url_decode(sn, href2));
+int hrefeq(DavSession *sn, const char *href1, const char *href2) {
+    cxmutstr href_s = cx_mutstr(util_url_decode(sn, href1));
+    cxmutstr href_r = cx_mutstr(util_url_decode(sn, href2));
     int ret = 0;
-    if(!sstrcmp(href_s, href_r)) {
+    if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) {
         ret = 1;
     } else if(href_s.length == href_r.length + 1) {
         if(href_s.ptr[href_s.length-1] == '/') {
             href_s.length--;
-            if(!sstrcmp(href_s, href_r)) {
+            if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) {
                 ret = 1;
             }
         }
     } else if(href_r.length == href_s.length + 1) {
         if(href_r.ptr[href_r.length-1] == '/') {
             href_r.length--;
-            if(!sstrcmp(href_s, href_r)) {
+            if(!cx_strcmp(cx_strcast(href_s), cx_strcast(href_r))) {
                 ret = 1;
             }
         }
@@ -475,7 +477,7 @@
 }
 
 
-DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response) {
+DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response) {
     char *url = NULL;
     curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &url);
     if(!root) {
@@ -523,7 +525,7 @@
             return NULL;
         }
     } else {
-        sstr_t resname = sstr(util_resource_name(response->href));
+        cxstring resname = cx_str(util_resource_name(response->href));
         int nlen = 0;
         char *uname = curl_easy_unescape(
                 sn->handle,
@@ -555,19 +557,21 @@
     char *crypto_key = NULL;
     
     // add properties
-    UCX_FOREACH(elm, response->properties) {
-        xmlNode *prop = elm->data;
-        resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children);
+    if(response->properties) {
+        CxIterator i = cxListIterator(response->properties);
+        cx_foreach(xmlNode*, prop, i) {
+            resource_add_property(res, (char*)prop->ns->href, (char*)prop->name, prop->children);
         
-        if (decrypt_props &&
-            prop->children &&
-            prop->children->type == XML_TEXT_NODE &&
-            xstreq(prop->ns->href, DAV_NS))
-        {
-            if(xstreq(prop->name, "crypto-prop")) {
-                crypto_prop = prop;
-            } else if(xstreq(prop->name, "crypto-key")) {
-                crypto_key = util_xml_get_text(prop);
+            if (decrypt_props &&
+                prop->children &&
+                prop->children->type == XML_TEXT_NODE &&
+                xstreq(prop->ns->href, DAV_NS))
+            {
+                if(xstreq(prop->name, "crypto-prop")) {
+                    crypto_prop = prop;
+                } else if(xstreq(prop->name, "crypto-key")) {
+                    crypto_key = util_xml_get_text(prop);
+                }
             }
         }
     }
@@ -576,7 +580,7 @@
         char *crypto_prop_content = util_xml_get_text(crypto_prop);
         DavKey *key = dav_context_get_key(res->session->context, crypto_key);
         if(crypto_prop_content) {
-            UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content);
+            CxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content);
             resource_set_crypto_properties(res, cprops);
         }
     }
@@ -589,8 +593,8 @@
     
     //DavResource *res = resource;
     DavResource *res = NULL;
-    char *href = NULL;
-    UcxList *properties = NULL; // xmlNode list
+    const char *href = NULL;
+    CxList *properties = cxLinkedListCreateSimple(CX_STORE_POINTERS); // xmlNode list
     char *crypto_name = NULL; // name set by crypto-name property
     char *crypto_key = NULL;
     
@@ -607,7 +611,7 @@
                     return 1;
                 }
                 //char *href = (char*)href_node->content;
-                href = util_url_path((char*)href_node->content);
+                href = util_url_path((const char*)href_node->content);
                 
                 char *href_s = util_url_decode(resource->session, href);
                 char *href_r = util_url_decode(resource->session, resource->href);
@@ -633,13 +637,13 @@
                                 sn->error = DAV_ERROR;
                                 return 1;
                             }
-                            sstr_t status_str = sstr((char*)status_node->content);
+                            cxstring status_str = cx_str((char*)status_node->content);
                             if(status_str.length < 13) {
                                 sn->error = DAV_ERROR;
                                 return 1;
                             }
-                            status_str = sstrsubsl(status_str, 9, 3);
-                            if(!sstrcmp(status_str, S("200"))) {
+                            status_str = cx_strsubsl(status_str, 9, 3);
+                            if(!cx_strcmp(status_str, CX_STR("200"))) {
                                 ok = 1;
                             }
                         }
@@ -651,7 +655,7 @@
                     n = prop_node->children;
                     while(n) {
                         if(n->type == XML_ELEMENT_NODE) {
-                            properties = ucx_list_append(properties, n);
+                            cxListAdd(properties, n);
                             if(xstreq(n->name, "resourcetype")) {
                                 if(parse_resource_type(n)) {
                                     iscollection = TRUE;
@@ -689,7 +693,7 @@
                 return -1;
             }
         } else {
-            sstr_t resname = sstr(util_resource_name(href));
+            cxstring resname = cx_str(util_resource_name(href));
             int nlen = 0;
             char *uname = curl_easy_unescape(
                     sn->handle,
@@ -700,8 +704,8 @@
             curl_free(uname);
         }
         
-        href = dav_session_strdup(sn, href);
-        res = dav_resource_new_full(sn, resource->path, name, href);
+        char *href_cp = dav_session_strdup(sn, href);
+        res = dav_resource_new_full(sn, resource->path, name, href_cp);
         
         dav_session_free(sn, name);
     }
@@ -711,8 +715,8 @@
     int decrypt_props = DAV_ENCRYPT_PROPERTIES(res->session);
     xmlNode *crypto_prop = NULL;
     
-    UCX_FOREACH(elm, properties) {
-        xmlNode *prop = elm->data;
+    CxIterator i = cxListIterator(properties);
+    cx_foreach(xmlNode*, prop, i) {
         if(!prop->ns) {
             continue;
         }
@@ -728,13 +732,13 @@
             }
         }
     }
-    ucx_list_free(properties);
+    cxListDestroy(properties);
     
     if(crypto_prop && crypto_key) {
         char *crypto_prop_content = util_xml_get_text(crypto_prop);
         DavKey *key = dav_context_get_key(res->session->context, crypto_key);
         if(crypto_prop_content && key) {
-            UcxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content);
+            CxMap *cprops = parse_crypto_prop_str(res->session, key, crypto_prop_content);
             resource_set_crypto_properties(res, cprops);
         }
     }
@@ -784,8 +788,8 @@
 CURLcode do_proppatch_request(
         DavSession *sn,
         char *lock,
-        UcxBuffer *request,
-        UcxBuffer *response)
+        CxBuffer *request,
+        CxBuffer *response)
 {       
     CURL *handle = sn->handle;
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "PROPPATCH");
@@ -795,7 +799,7 @@
     if(lock) {
         char *url = NULL;
         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
-        char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+        char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr;
         headers = curl_slist_append(headers, ltheader);
         free(ltheader);
         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
@@ -803,15 +807,15 @@
     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
     
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
-    curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
-    curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek);
+    curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead);
+    curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek);
     curl_easy_setopt(handle, CURLOPT_READDATA, request); 
     curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
     
-    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite);
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
     
-    ucx_buffer_seek(request, 0, SEEK_SET);
+    cxBufferSeek(request, 0, SEEK_SET);
     CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL);
     curl_slist_free_all(headers);
     
@@ -820,168 +824,171 @@
     return ret;
 }
 
-UcxBuffer* create_proppatch_request(DavResourceData *data) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
-    scstr_t s;
+CxBuffer* create_proppatch_request(DavResourceData *data) {
+    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxstring s;
     
-    UcxMap *namespaces = ucx_map_new(8);
+    CxMap *namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8);
+    namespaces->simple_destructor = free;
+    
     char prefix[8];
     int pfxnum = 0;
-    UCX_FOREACH(elm, data->set) {
-        DavProperty *p = elm->data;
-        if(strcmp(p->ns->name, "DAV:")) {
-            snprintf(prefix, 8, "x%d", pfxnum++);
-            ucx_map_cstr_put(namespaces, p->ns->name, strdup(prefix));
+    if(data->set) {
+        CxIterator i = cxListIterator(data->set);
+        cx_foreach(DavProperty*, p, i) {
+            if(strcmp(p->ns->name, "DAV:")) {
+                snprintf(prefix, 8, "x%d", pfxnum++);
+                cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix));
+            }
         }
     }
-    UCX_FOREACH(elm, data->remove) {
-        DavProperty *p = elm->data;
-        if(strcmp(p->ns->name, "DAV:")) {
-            snprintf(prefix, 8, "x%d", pfxnum++);
-            ucx_map_cstr_put(namespaces, p->ns->name, strdup(prefix));
+    if(data->remove) {
+        CxIterator i = cxListIterator(data->remove);
+        cx_foreach(DavProperty*, p, i) {
+            if(strcmp(p->ns->name, "DAV:")) {
+                snprintf(prefix, 8, "x%d", pfxnum++);
+                cxMapPut(namespaces, cx_hash_key_str(p->ns->name), strdup(prefix));
+            }
         }
     }
     
-    s = SC("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     // write root element and namespaces
-    s = SC("<D:propertyupdate xmlns:D=\"DAV:\"");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
-    UcxMapIterator mapi = ucx_map_iterator(namespaces);
-    UcxKey key;
-    char *pfxval;
-    UCX_MAP_FOREACH(key, pfxval, mapi) {
-        s = SC(" xmlns:");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = scstr(pfxval);
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = SC("=\"");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = scstrn(key.data, key.len);
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        s = SC("\"");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\"");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
+    CxIterator mapi = cxMapIterator(namespaces);
+    cx_foreach(CxMapEntry*, entry, mapi) {
+        s = CX_STR(" xmlns:");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        s = cx_str(entry->value);
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        s = CX_STR("=\"");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        s = cx_strn(entry->key->data, entry->key->len);
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        s = CX_STR("\"");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
     }
-    s = SC(">\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR(">\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     if(data->set) {
-        s = SC("<D:set>\n<D:prop>\n");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        UCX_FOREACH(elm, data->set) {
-            DavProperty *property = elm->data;
-            char *prefix = ucx_map_cstr_get(namespaces, property->ns->name);
+        s = CX_STR("<D:set>\n<D:prop>\n");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        CxIterator i = cxListIterator(data->set);
+        cx_foreach(DavProperty*, property, i) {
+            char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name));
             if(!prefix) {
                 prefix = "D";
             }
             
             // begin tag
-            s = SC("<");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = scstr(prefix);
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = SC(":");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = scstr(property->name);
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = SC(">");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = CX_STR("<");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = cx_str(prefix);
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = CX_STR(":");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = cx_str(property->name);
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = CX_STR(">");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
             
             // content
             DavXmlNode *content = property->value;
             if(content->type == DAV_XML_TEXT && !content->next) {
-                ucx_buffer_write(content->content, 1, content->contentlength, buf);
+                cxBufferWrite(content->content, 1, content->contentlength, buf);
             } else {
-                dav_print_node(buf, (write_func)ucx_buffer_write, namespaces, content);
+                dav_print_node(buf, (cx_write_func)cxBufferWrite, namespaces, content);
             }
             
             // end tag
-            s = SC("</");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = scstr(prefix);
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = SC(":");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = scstr(property->name);
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = SC(">\n");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = CX_STR("</");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = cx_str(prefix);
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = CX_STR(":");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = cx_str(property->name);
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = CX_STR(">\n");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
         }
-        s = SC("</D:prop>\n</D:set>\n");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = CX_STR("</D:prop>\n</D:set>\n");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
     }
     if(data->remove) {
-        s = SC("<D:remove>\n<D:prop>\n");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        UCX_FOREACH(elm, data->remove) {
-            DavProperty *property = elm->data;
-            char *prefix = ucx_map_cstr_get(namespaces, property->ns->name);
+        s = CX_STR("<D:remove>\n<D:prop>\n");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        CxIterator i = cxListIterator(data->remove);
+        cx_foreach(DavProperty*, property, i) {
+            char *prefix = cxMapGet(namespaces, cx_hash_key_str(property->ns->name));
             
-            s = SC("<");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = scstr(prefix);
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = SC(":");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = scstr(property->name);
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
-            s = SC(" />\n");
-            ucx_buffer_write(s.ptr, 1, s.length, buf);
+            s = CX_STR("<");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = cx_str(prefix);
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = CX_STR(":");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = cx_str(property->name);
+            cxBufferWrite(s.ptr, 1, s.length, buf);
+            s = CX_STR(" />\n");
+            cxBufferWrite(s.ptr, 1, s.length, buf);
         }
-        s = SC("</D:prop>\n</D:remove>\n");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = CX_STR("</D:prop>\n</D:remove>\n");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
     }
     
-    s = SC("</D:propertyupdate>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("</D:propertyupdate>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     // cleanup namespace map
-    ucx_map_free_content(namespaces, free);
-    ucx_map_free(namespaces);
+    cxMapDestroy(namespaces);
     
     return buf;
 }
 
-UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
-    sstr_t s;
+CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash) {
+    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxstring s;
     
-    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:propertyupdate xmlns:D=\"DAV:\" xmlns:idav=\"" DAV_NS "\">\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:set>\n<D:prop>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<D:set>\n<D:prop>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     if(DAV_ENCRYPT_NAME(sn)) {
-        s = S("<idav:crypto-name>");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = CX_STR("<idav:crypto-name>");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
         char *crname = aes_encrypt(name, strlen(name), key);
-        ucx_buffer_puts(buf, crname);
+        cxBufferPutString(buf, crname);
         free(crname);
-        s = S("</idav:crypto-name>\n");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = CX_STR("</idav:crypto-name>\n");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
     }
     
-    s = S("<idav:crypto-key>");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
-    ucx_buffer_puts(buf, key->name);
-    s = S("</idav:crypto-key>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<idav:crypto-key>");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
+    cxBufferPutString(buf, key->name);
+    s = CX_STR("</idav:crypto-key>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     if(hash) {
-        s = S("<idav:crypto-hash>");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
-        ucx_buffer_puts(buf, hash);
-        s = S("</idav:crypto-hash>\n");
-        ucx_buffer_write(s.ptr, 1, s.length, buf);
+        s = CX_STR("<idav:crypto-hash>");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
+        cxBufferPutString(buf, hash);
+        s = CX_STR("</idav:crypto-hash>\n");
+        cxBufferWrite(s.ptr, 1, s.length, buf);
     }
     
-    s = S("</D:prop>\n</D:set>\n</D:propertyupdate>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("</D:prop>\n</D:set>\n</D:propertyupdate>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     return buf;
 }
@@ -1006,10 +1013,10 @@
         char *ltheader = NULL;
         if(create) {
             url = util_parent_path(url);
-            ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+            ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr;
             free(url);
         } else {
-            ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+            ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr;
         }
         headers = curl_slist_append(headers, ltheader);
         free(ltheader);
@@ -1017,12 +1024,12 @@
     }
     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
     
-    UcxBuffer *buf = NULL;
+    CxBuffer *buf = NULL;
     if(!read_func) {
-        buf = ucx_buffer_new(data, length, 0);
+        buf = cxBufferCreate(data, length, cxDefaultAllocator, 0);
         buf->size = length;
         data = buf;
-        read_func = (dav_read_func)ucx_buffer_read;
+        read_func = (dav_read_func)cxBufferRead;
         curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)length);
     } else if(length == 0) {
         headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
@@ -1042,19 +1049,19 @@
     CURLcode ret = dav_session_curl_perform(sn, NULL);
     curl_slist_free_all(headers);
     if(buf) {
-        ucx_buffer_free(buf);
+        cxBufferFree(buf);
     }
     
     return ret;
 }
 
-CURLcode do_delete_request(DavSession *sn, char *lock, UcxBuffer *response) { 
+CURLcode do_delete_request(DavSession *sn, char *lock, CxBuffer *response) { 
     CURL *handle = sn->handle;
     struct curl_slist *headers = NULL;
     if(lock) {
         char *url = NULL;
         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
-        char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+        char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr;
         headers = curl_slist_append(headers, ltheader);
         free(ltheader);
         curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
@@ -1065,7 +1072,7 @@
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "DELETE");
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 0L);
     
-    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite);
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
     
     CURLcode ret = dav_session_curl_perform(sn, NULL);
@@ -1080,7 +1087,7 @@
         char *url = NULL;
         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
         url = util_parent_path(url);
-        char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+        char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr;
         free(url);
         headers = curl_slist_append(headers, ltheader);
         free(ltheader);
@@ -1138,12 +1145,12 @@
     if(lock) {
         char *url = NULL;
         curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &url);
-        char *ltheader = ucx_sprintf("If: <%s> (<%s>)", url, lock).ptr;
+        char *ltheader = cx_asprintf("If: <%s> (<%s>)", url, lock).ptr;
         headers = curl_slist_append(headers, ltheader);
         free(ltheader);
     }
-    //sstr_t deststr = ucx_sprintf("Destination: %s", dest);
-    sstr_t deststr = sstrcat(2, S("Destination: "), sstr(dest));
+    //cxstring deststr = ucx_sprintf("Destination: %s", dest);
+    cxmutstr deststr = cx_strcat(2, CX_STR("Destination: "), cx_str(dest));
     headers = curl_slist_append(headers, deststr.ptr);
     if(override) {
         headers = curl_slist_append(headers, "Overwrite: T");
@@ -1161,26 +1168,26 @@
 }
 
 
-UcxBuffer* create_lock_request(void) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
-    sstr_t s;
+CxBuffer* create_lock_request(void) {
+    CxBuffer *buf = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    cxstring s;
     
-    s = S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("<D:lockinfo xmlns:D=\"DAV:\">\n"
+    s = CX_STR("<D:lockinfo xmlns:D=\"DAV:\">\n"
           "<D:lockscope><D:exclusive/></D:lockscope>\n"
           "<D:locktype><D:write/></D:locktype>\n"
           "<D:owner><D:href>http://davutils.org/libidav/</D:href></D:owner>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
-    s = S("</D:lockinfo>\n");
-    ucx_buffer_write(s.ptr, 1, s.length, buf);
+    s = CX_STR("</D:lockinfo>\n");
+    cxBufferWrite(s.ptr, 1, s.length, buf);
     
     return buf;
 }
 
-int parse_lock_response(DavSession *sn, UcxBuffer *response, LockDiscovery *lock) {
+int parse_lock_response(DavSession *sn, CxBuffer *response, LockDiscovery *lock) {
     lock->locktoken = NULL;
     lock->timeout = NULL;
     
@@ -1240,7 +1247,7 @@
     return ret;
 }
 
-CURLcode do_lock_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response, time_t timeout) { 
+CURLcode do_lock_request(DavSession *sn, CxBuffer *request, CxBuffer *response, time_t timeout) { 
     CURL *handle = sn->handle;
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "LOCK");  
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1L);
@@ -1250,11 +1257,11 @@
     struct curl_slist *headers = NULL;
     
     if(timeout != 0) {
-        sstr_t thdr;
+        cxmutstr thdr;
         if(timeout < 0) {
-            thdr = ucx_sprintf("%s", "Timeout: Infinite");
+            thdr = cx_asprintf("%s", "Timeout: Infinite");
         } else {
-            thdr = ucx_sprintf("Timeout: Second-%u", (unsigned int)timeout);
+            thdr = cx_asprintf("Timeout: Second-%u", (unsigned int)timeout);
         }
         headers = curl_slist_append(headers, thdr.ptr);
         free(thdr.ptr);
@@ -1262,12 +1269,12 @@
     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
     
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
-    curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
-    curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek);
+    curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead);
+    curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek);
     curl_easy_setopt(handle, CURLOPT_READDATA, request); 
     curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
     
-    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite);
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
     
     CURLcode ret = dav_session_curl_perform_buf(sn, request, response, NULL);
@@ -1288,7 +1295,7 @@
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
     
     // set lock-token header
-    sstr_t ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken);
+    cxmutstr ltheader = cx_asprintf("Lock-Token: <%s>", locktoken);
     struct curl_slist *headers = curl_slist_append(NULL, ltheader.ptr);
     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
     
@@ -1308,10 +1315,10 @@
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, NULL);
     
     // set lock-token header
-    sstr_t ltheader;
+    cxmutstr ltheader;
     struct curl_slist *headers = NULL;
     if(locktoken) {
-        ltheader = ucx_sprintf("Lock-Token: <%s>", locktoken);
+        ltheader = cx_asprintf("Lock-Token: <%s>", locktoken);
         headers = curl_slist_append(NULL, ltheader.ptr);
     }
     curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
@@ -1326,17 +1333,17 @@
 }
 
 
-CURLcode do_report_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response) {
+CURLcode do_report_request(DavSession *sn, CxBuffer *request, CxBuffer *response) {
     CURL *handle = sn->handle;
     curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, "REPORT");
     
     curl_easy_setopt(handle, CURLOPT_UPLOAD, 1); 
-    curl_easy_setopt(handle, CURLOPT_READFUNCTION, ucx_buffer_read);
-    curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, dav_buffer_seek);
+    curl_easy_setopt(handle, CURLOPT_READFUNCTION, cxBufferRead);
+    curl_easy_setopt(handle, CURLOPT_SEEKFUNCTION, cxBufferSeek);
     curl_easy_setopt(handle, CURLOPT_READDATA, request); 
     curl_easy_setopt(handle, CURLOPT_INFILESIZE, request->size);
     
-    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, ucx_buffer_write);
+    curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, cxBufferWrite);
     curl_easy_setopt(handle, CURLOPT_WRITEDATA, response);
     
     struct curl_slist *headers = NULL;
--- a/libidav/methods.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/methods.h	Fri Apr 21 21:25:32 2023 +0200
@@ -32,7 +32,7 @@
 #include "webdav.h"
 #include "resource.h"
 
-#include <ucx/list.h>
+#include <cx/list.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -48,11 +48,11 @@
 };
 
 struct ResponseTag {
-    char    *href;
-    int     iscollection;
-    UcxList *properties;
-    char    *crypto_name;
-    char    *crypto_key;
+    const char *href;
+    int        iscollection;
+    CxList     *properties;
+    const char *crypto_name;
+    const char *crypto_key;
 };
 
 struct LockDiscovery {
@@ -60,18 +60,18 @@
     char    *locktoken;
 };
 
-int dav_buffer_seek(UcxBuffer *b, curl_off_t offset, int origin);
+int dav_buffer_seek(CxBuffer *b, curl_off_t offset, int origin);
 
 CURLcode do_propfind_request(
         DavSession *sn,
-        UcxBuffer *request,
-        UcxBuffer *response);
+        CxBuffer *request,
+        CxBuffer *response);
 
 CURLcode do_proppatch_request(
         DavSession *sn,
         char *lock,
-        UcxBuffer *request,
-        UcxBuffer *response);
+        CxBuffer *request,
+        CxBuffer *response);
 
 CURLcode do_put_request(
         DavSession *sn,
@@ -82,21 +82,21 @@
         dav_seek_func seek_func,
         size_t length);
 
-UcxBuffer* create_allprop_propfind_request(void);
-UcxBuffer* create_cryptoprop_propfind_request(void);
-UcxBuffer* create_propfind_request(DavSession *sn, UcxList *properties, char *rootelm, DavBool nocrypt);
-UcxBuffer* create_basic_propfind_request(void);
+CxBuffer* create_allprop_propfind_request(void);
+CxBuffer* create_cryptoprop_propfind_request(void);
+CxBuffer* create_propfind_request(DavSession *sn, CxList *properties, char *rootelm, DavBool nocrypt);
+CxBuffer* create_basic_propfind_request(void);
 
-PropfindParser* create_propfind_parser(UcxBuffer *response, char *url);
+PropfindParser* create_propfind_parser(CxBuffer *response, char *url);
 void destroy_propfind_parser(PropfindParser *parser);
 int get_propfind_response(PropfindParser *parser, ResponseTag *result);
 void cleanup_response(ResponseTag *result);
 
-int hrefeq(DavSession *sn, char *href1, char *href2);
+int hrefeq(DavSession *sn, const char *href1, const char *href2);
 DavResource* response2resource(DavSession *sn, ResponseTag *response, char *parent_path);
 void add_properties(DavResource *res, ResponseTag *response);
 
-DavResource* parse_propfind_response(DavSession *sn, DavResource *root, UcxBuffer *response);
+DavResource* parse_propfind_response(DavSession *sn, DavResource *root, CxBuffer *response);
 int parse_response_tag(DavResource *resource, xmlNode *node);
 void set_davprops(DavResource *res);
 
@@ -106,10 +106,10 @@
  */
 int parse_resource_type(xmlNode *node);
 
-UcxBuffer* create_proppatch_request(DavResourceData *data);
-UcxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash);
+CxBuffer* create_proppatch_request(DavResourceData *data);
+CxBuffer* create_crypto_proppatch_request(DavSession *sn, DavKey *key, const char *name, const char *hash);
 
-CURLcode do_delete_request(DavSession *sn, char *lock, UcxBuffer *response);
+CURLcode do_delete_request(DavSession *sn, char *lock, CxBuffer *response);
 
 CURLcode do_mkcol_request(DavSession *sn, char *lock);
 
@@ -117,14 +117,14 @@
 
 CURLcode do_copy_move_request(DavSession *sn, char *dest, char *lock, DavBool copy, DavBool override);
 
-UcxBuffer* create_lock_request(void);
-int parse_lock_response(DavSession *sn, UcxBuffer *response, LockDiscovery *lock);
-CURLcode do_lock_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response, time_t timeout);
+CxBuffer* create_lock_request(void);
+int parse_lock_response(DavSession *sn, CxBuffer *response, LockDiscovery *lock);
+CURLcode do_lock_request(DavSession *sn, CxBuffer *request, CxBuffer *response, time_t timeout);
 CURLcode do_unlock_request(DavSession *sn, char *locktoken);
 
 CURLcode do_simple_request(DavSession *sn, char *method, char *locktoken);
 
-CURLcode do_report_request(DavSession *sn, UcxBuffer *request, UcxBuffer *response);
+CURLcode do_report_request(DavSession *sn, CxBuffer *request, CxBuffer *response);
 
 #ifdef	__cplusplus
 }
--- a/libidav/resource.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/resource.c	Fri Apr 21 21:25:32 2023 +0200
@@ -36,8 +36,12 @@
 #include "session.h"
 #include "methods.h"
 #include "crypto.h"
-#include "ucx/buffer.h"
-#include "ucx/utils.h"
+#include <cx/buffer.h>
+#include <cx/utils.h>
+#include <cx/hash_map.h>
+#include <cx/printf.h>
+#include <cx/basic_mempool.h>
+#include <cx/array_list.h>
 
 #include "resource.h"
 #include "xml.h"
@@ -45,11 +49,11 @@
 
 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
 
-DavResource* dav_resource_new(DavSession *sn, char *path) {
+DavResource* dav_resource_new(DavSession *sn, const char *path) {
     //char *href = util_url_path(url);
     //DavResource *res = dav_resource_new_href(sn, href);
     char *parent = util_parent_path(path);
-    char *name = util_resource_name(path); 
+    const char *name = util_resource_name(path); 
     char *href = dav_session_create_plain_href(sn, path);
     
     DavResource *res = dav_resource_new_full(sn, parent, name, href);
@@ -57,7 +61,7 @@
     return res;
 }
 
-DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, char *name) {
+DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, const char *name) {
     char *path = util_concat_path(parent->path, name);
     char *href = dav_session_create_plain_href(sn, path);
     DavResource *res = dav_resource_new_full(sn, parent->path, name, href);
@@ -66,8 +70,8 @@
 }
 
 
-DavResource* dav_resource_new_href(DavSession *sn, char *href) {  
-    DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource));
+DavResource* dav_resource_new_href(DavSession *sn, const char *href) {  
+    DavResource *res = cxCalloc(sn->mp->allocator, 1, sizeof(DavResource));
     res->session = sn;
     
     // set name, path and href
@@ -79,14 +83,14 @@
     return res;
 }
 
-DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href) {
-    sstr_t n = sstr(name);
+DavResource* dav_resource_new_full(DavSession *sn, const char *parent_path, const char *name, char *href) {
+    cxstring n = cx_str(name);
     // the name must not contain path separators
     if(n.length > 0 && href) {
         for(int i=0;i<n.length-1;i++) {
             char c = n.ptr[i];
             if(c == '/' || c == '\\') {
-                n = sstr(util_resource_name(href));
+                n = cx_str(util_resource_name(href));
                 break;
             }
         }
@@ -96,11 +100,11 @@
         n.length--;
     }
     
-    DavResource *res = ucx_mempool_calloc(sn->mp, 1, sizeof(DavResource));
+    DavResource *res = cxCalloc(sn->mp->allocator, 1, sizeof(DavResource));
     res->session = sn;
     
     // set name, path and href
-    res->name = sstrdup_a(sn->mp->allocator, n).ptr;
+    res->name = cx_strdup_a(sn->mp->allocator, n).ptr;
     
     char *path = util_concat_path(parent_path, name); 
     res->path = dav_session_strdup(sn, path);
@@ -112,23 +116,23 @@
     
     // cache href/path
     if(href) {
-        dav_session_cache_path(sn, sstr(path), sstr(href));
+        dav_session_cache_path(sn, cx_str(path), cx_str(href));
     }
     free(path);
     
     return res;
 }
 
-void resource_free_properties(DavSession *sn, UcxMap *properties) {
+void resource_free_properties(DavSession *sn, CxMap *properties) {
     if(!properties) return;
     
-    UcxMapIterator i = ucx_map_iterator(properties);
+    CxIterator i = cxMapIteratorValues(properties);
     DavProperty *property;
-    UCX_MAP_FOREACH(key, property, i) {
+    cx_foreach(DavProperty*, property, i) {
         // TODO: free everything
         dav_session_free(sn, property);
     }
-    ucx_map_free(properties);
+    cxMapDestroy(properties);
 }
 
 void dav_resource_free(DavResource *res) {
@@ -144,29 +148,62 @@
     resource_free_properties(sn, data->properties);
     resource_free_properties(sn, data->crypto_properties);
     
-    UCX_FOREACH(elm, data->set) {
-        DavProperty *p = elm->data;
-        dav_session_free(sn, p->ns->name);
-        if(p->ns->prefix) {
-            dav_session_free(sn, p->ns->prefix);
+    if(data->set) {
+        CxIterator i = cxListIterator(data->set);
+        cx_foreach(DavProperty *, p, i) {
+            dav_session_free(sn, p->ns->name);
+            if(p->ns->prefix) {
+                dav_session_free(sn, p->ns->prefix);
+            }
+            dav_session_free(sn, p->ns);
+
+            dav_session_free(sn, p->name);
+            dav_free_xml_node_sn(sn, p->value);
+            dav_session_free(sn, p);
         }
-        dav_session_free(sn, p->ns);
-        
-        dav_session_free(sn, p->name);
-        dav_free_xml_node_sn(sn, p->value);
-        dav_session_free(sn, p);
     }
     
-    UCX_FOREACH(elm, data->remove) {
-        DavProperty *p = elm->data;
-        dav_session_free(sn, p->ns->name);
-        if(p->ns->prefix) {
-            dav_session_free(sn, p->ns->prefix);
+    if(data->remove) {
+        CxIterator i = cxListIterator(data->remove);
+        cx_foreach(DavProperty *, p, i) {
+            dav_session_free(sn, p->ns->name);
+            if(p->ns->prefix) {
+                dav_session_free(sn, p->ns->prefix);
+            }
+            dav_session_free(sn, p->ns);
+
+            dav_session_free(sn, p->name);
+            dav_session_free(sn, p);
         }
-        dav_session_free(sn, p->ns);
-        
-        dav_session_free(sn, p->name);
-        dav_session_free(sn, p);
+    }
+    
+    if(data->crypto_set) {
+        CxIterator i = cxListIterator(data->crypto_set);
+        cx_foreach(DavProperty *, p, i) {
+            dav_session_free(sn, p->ns->name);
+            if(p->ns->prefix) {
+                dav_session_free(sn, p->ns->prefix);
+            }
+            dav_session_free(sn, p->ns);
+
+            dav_session_free(sn, p->name);
+            dav_free_xml_node_sn(sn, p->value);
+            dav_session_free(sn, p);
+        }
+    }
+    
+    if(data->crypto_remove) {
+        CxIterator i = cxListIterator(data->crypto_remove);
+        cx_foreach(DavProperty *, p, i) {
+            dav_session_free(sn, p->ns->name);
+            if(p->ns->prefix) {
+                dav_session_free(sn, p->ns->prefix);
+            }
+            dav_session_free(sn, p->ns);
+
+            dav_session_free(sn, p->name);
+            dav_session_free(sn, p);
+        }
     }
     
     if(!data->read && data->content) {
@@ -187,20 +224,20 @@
     }
 }
 
-void resource_set_href(DavResource *res, sstr_t href) {
-    res->href = sstrdup_a(res->session->mp->allocator, href).ptr;
+void resource_set_href(DavResource *res, cxstring href) {
+    res->href = cx_strdup_a(res->session->mp->allocator, href).ptr;
 }
 
-void resource_set_info(DavResource *res, char *href_str) {
+void resource_set_info(DavResource *res, const char *href_str) {
     char *url_str = NULL;
     curl_easy_getinfo(res->session->handle, CURLINFO_EFFECTIVE_URL, &url_str);
-    sstr_t name = sstr(util_resource_name(href_str));
-    sstr_t href = sstr(href_str);
+    cxstring name = cx_str(util_resource_name(href_str));
+    cxstring href = cx_str(href_str);
     
-    sstr_t base_href = sstr(util_url_path(res->session->base_url));
-    sstr_t path = sstrsubs(href, base_href.length - 1);
+    cxstring base_href = cx_str(util_url_path(res->session->base_url));
+    cxstring path = cx_strsubs(href, base_href.length - 1);
     
-    UcxAllocator *a = res->session->mp->allocator;
+    const CxAllocator *a = res->session->mp->allocator;
     CURL *handle = res->session->handle;
     
     int nlen = 0;
@@ -208,22 +245,22 @@
     int plen = 0;
     char *upath = curl_easy_unescape(handle, path.ptr, path.length, &plen); 
     
-    res->name = sstrdup_a(a, sstrn(uname, nlen)).ptr;
-    res->href = sstrdup_a(a, href).ptr;
-    res->path = sstrdup_a(a, sstrn(upath, plen)).ptr;
+    res->name = cx_strdup_a(a, cx_strn(uname, nlen)).ptr;
+    res->href = cx_strdup_a(a, href).ptr;
+    res->path = cx_strdup_a(a, cx_strn(upath, plen)).ptr;
     
     curl_free(uname);
     curl_free(upath);
 }
 
 DavResourceData* resource_data_new(DavSession *sn) {
-    DavResourceData *data = ucx_mempool_malloc(
-            sn->mp,
+    DavResourceData *data = cxMalloc(
+            sn->mp->allocator,
             sizeof(DavResourceData));
     if(!data) {
         return NULL;
     }
-    data->properties = ucx_map_new_a(sn->mp->allocator, 32);
+    data->properties = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 32);
     data->crypto_properties = NULL;
     data->set = NULL;
     data->remove = NULL;
@@ -257,9 +294,10 @@
     prop->ns = namespace;
     prop->value = val;
     
-    sstr_t key = dav_property_key(ns, name);
-    ucx_map_sstr_put(((DavResourceData*)res->data)->properties, key, prop);
-    free(key.ptr);
+    cxmutstr keystr = dav_property_key(ns, name);
+    CxHashKey key = cx_hash_key(keystr.ptr, keystr.length);
+    cxMapPut(((DavResourceData*)res->data)->properties, key, prop);
+    free(keystr.ptr);
 }
 
 void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val) {
@@ -278,15 +316,15 @@
     resource_add_prop(res, ns, name, dav_text_node(res->session, val));
 }
 
-void resource_set_crypto_properties(DavResource *res, UcxMap *cprops) {
+void resource_set_crypto_properties(DavResource *res, CxMap *cprops) {
     DavResourceData *data = res->data;
     resource_free_properties(res->session, data->crypto_properties);
     data->crypto_properties = cprops;
 }
 
 DavXmlNode* resource_get_property(DavResource *res, const char *ns, const char *name) {
-    sstr_t keystr = dav_property_key(ns, name);
-    UcxKey key = ucx_key(keystr.ptr, keystr.length);
+    cxmutstr keystr = dav_property_key(ns, name);
+    CxHashKey key = cx_hash_key(keystr.ptr, keystr.length);
     DavXmlNode *ret = resource_get_property_k(res, key);
     free(keystr.ptr);
     
@@ -294,37 +332,37 @@
 }
 
 DavXmlNode* resource_get_encrypted_property(DavResource *res, const char *ns, const char *name) {
-    sstr_t keystr = dav_property_key(ns, name);
-    UcxKey key = ucx_key(keystr.ptr, keystr.length);
+    cxmutstr keystr = dav_property_key(ns, name);
+    CxHashKey key = cx_hash_key(keystr.ptr, keystr.length);
     DavXmlNode *ret = resource_get_encrypted_property_k(res, key);
     free(keystr.ptr);
     
     return ret;
 }
 
-DavXmlNode* resource_get_property_k(DavResource *res, UcxKey key) {
+DavXmlNode* resource_get_property_k(DavResource *res, CxHashKey key) {
     DavResourceData *data = (DavResourceData*)res->data;
-    DavProperty *property = ucx_map_get(data->properties, key);
+    DavProperty *property = cxMapGet(data->properties, key);
     
     return property ? property->value : NULL;
 }
 
-DavXmlNode* resource_get_encrypted_property_k(DavResource *res, UcxKey key) {
+DavXmlNode* resource_get_encrypted_property_k(DavResource *res, CxHashKey key) {
     DavResourceData *data = (DavResourceData*)res->data;
-    DavProperty *property = ucx_map_get(data->crypto_properties, key);
+    DavProperty *property = cxMapGet(data->crypto_properties, key);
     
     return property ? property->value : NULL;
 }
 
-sstr_t dav_property_key(const char *ns, const char *name) {
-    return dav_property_key_a(ucx_default_allocator(), ns, name);
+cxmutstr dav_property_key(const char *ns, const char *name) {
+    return dav_property_key_a(cxDefaultAllocator, ns, name);
 }
 
-sstr_t dav_property_key_a(UcxAllocator *a, const char *ns, const char *name) {
-    scstr_t ns_str = scstr(ns);
-    scstr_t name_str = scstr(name);
+cxmutstr dav_property_key_a(const CxAllocator *a, const char *ns, const char *name) {
+    cxstring ns_str = cx_str(ns);
+    cxstring name_str = cx_str(name);
     
-    return sstrcat_a(a, 4, ns_str, S("\0"), name_str, S("\0"));
+    return cx_strcat_a(a, 4, ns_str, CX_STR("\0"), name_str, CX_STR("\0"));
 }
 
 
@@ -411,7 +449,7 @@
     return cr->descending ? -ret : ret;
 }
 
-void resource_add_ordered_child(DavResource *parent, DavResource *child, UcxList *ordercr) {
+void resource_add_ordered_child(DavResource *parent, DavResource *child, CxList *ordercr) {
     if(!ordercr) {
         resource_add_child(parent, child);
         return;
@@ -427,8 +465,8 @@
         DavResource *resource = parent->children;
         while(resource) {
             int r = 0;
-            UCX_FOREACH(elm, ordercr) {
-                DavOrderCriterion *cr = elm->data;
+            CxIterator i = cxListIterator(ordercr);
+            cx_foreach(DavOrderCriterion*, cr, i) {
                 r = resource_cmp(child, resource, cr);
                 if(r != 0) {
                     break;
@@ -495,8 +533,8 @@
     DavResourceData *data = res->data;
     
     DavXmlNode *property = NULL;
-    UcxList *remove_list = NULL;
-    UcxList *set_list = NULL;
+    CxList *remove_list = NULL;
+    CxList *set_list = NULL;
     
     if(encrypted) {
         // check if crypto_properties because it will only be created
@@ -515,23 +553,27 @@
     
     // resource_get_property only returns persistent properties
     // check the remove and set list
-    if(property) {
+    if(property && remove_list) {
         // if the property is in the remove list, we return NULL
-        UCX_FOREACH(elm, remove_list) {
-            DavProperty *p = elm->data;
+        CxIterator i = cxListIterator(remove_list);
+        cx_foreach(DavProperty*, p, i) {
             if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) {
                 return NULL;
             }
         }
     }
+    
     // the set list contains property updates
     // we return an updated property if possible
-    UCX_FOREACH(elm, set_list) {
-        DavProperty *p = elm->data;
-        if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) {
-            return p->value; // TODO: fix
+    if(set_list) {
+        CxIterator i = cxListIterator(set_list);
+        cx_foreach(DavProperty*, p, i) {
+            if(!strcmp(p->name, name) && !strcmp(p->ns->name, ns)) {
+                return p->value; // TODO: fix
+            }
         }
     }
+    
     // no property update
     
     return property;
@@ -572,18 +614,30 @@
     dav_set_string_property_ns(res, pns, pname, value);
 }
 
+static int add2propertylist(const CxAllocator *a, CxList **list, DavProperty *property) {
+    if(!*list) {
+        CxList *newlist = cxLinkedListCreate(a, NULL, CX_STORE_POINTERS);
+        if(!newlist) {
+            return 1;
+        }
+        *list = newlist;
+    }
+    cxListAdd(*list, property);
+    return 0;
+}
+
 void dav_set_string_property_ns(DavResource *res, char *ns, char *name, char *value) {
     DavSession *sn = res->session;
-    UcxAllocator *a = res->session->mp->allocator;
+    const CxAllocator *a = res->session->mp->allocator;
     DavResourceData *data = res->data;
     
     DavProperty *property = createprop(res->session, ns, name);
     property->value = dav_text_node(res->session, value);
     
     if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) {
-        data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
+        add2propertylist(a, &data->crypto_set, property);
     } else {
-        data->set = ucx_list_append_a(a, data->set, property);
+        add2propertylist(a, &data->set, property);
     }
 }
 
@@ -596,7 +650,7 @@
 
 void dav_set_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) {
     DavSession *sn = res->session;
-    UcxAllocator *a = sn->mp->allocator; 
+    const CxAllocator *a = sn->mp->allocator; 
     DavResourceData *data = res->data;
     
     DavProperty *property = createprop(sn, ns, name);
@@ -605,9 +659,9 @@
     property->value = value;
     
     if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) {
-        data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
+        add2propertylist(a, &data->crypto_set, property);
     } else {
-        data->set = ucx_list_append_a(a, data->set, property);
+        add2propertylist(a, &data->set, property);
     }
 }
 
@@ -621,44 +675,44 @@
 void dav_remove_property_ns(DavResource *res, char *ns, char *name) {
     DavSession *sn = res->session;
     DavResourceData *data = res->data;
-    UcxAllocator *a = res->session->mp->allocator;
+    const CxAllocator *a = res->session->mp->allocator;
     
     DavProperty *property = createprop(res->session, ns, name);
     
     if(DAV_ENCRYPT_PROPERTIES(sn) && dav_namespace_is_encrypted(sn->context, ns)) {
-        data->crypto_remove = ucx_list_append_a(a, data->crypto_remove, property);
+        add2propertylist(a, &data->crypto_remove, property);
     } else {
-        data->remove = ucx_list_append_a(a, data->remove, property);
+        add2propertylist(a, &data->remove, property);
     }
 }
 
 void dav_set_encrypted_property_ns(DavResource *res, char *ns, char *name, DavXmlNode *value) {
-    UcxAllocator *a = res->session->mp->allocator;
+    const CxAllocator *a = res->session->mp->allocator;
     DavResourceData *data = res->data;
     
     DavProperty *property = createprop(res->session, ns, name);
     property->value = value; // TODO: copy node?
     
-    data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
+    add2propertylist(a, &data->crypto_set, property);
 }
 
 void dav_set_encrypted_string_property_ns(DavResource *res, char *ns, char *name, char *value) {
-    UcxAllocator *a = res->session->mp->allocator;
+    const CxAllocator *a = res->session->mp->allocator;
     DavResourceData *data = res->data;
     
     DavProperty *property = createprop(res->session, ns, name);
     property->value = dav_text_node(res->session, value);
     
-    data->crypto_set = ucx_list_append_a(a, data->crypto_set, property);
+    add2propertylist(a, &data->crypto_set, property);
 }
 
 void dav_remove_encrypted_property_ns(DavResource *res, char *ns, char *name) {
     DavResourceData *data = res->data;
-    UcxAllocator *a = res->session->mp->allocator;
+    const CxAllocator *a = res->session->mp->allocator;
     
     DavProperty *property = createprop(res->session, ns, name);
     
-    data->crypto_remove = ucx_list_append_a(a, data->crypto_remove, property);
+    add2propertylist(a, &data->crypto_remove, property);
 }
 
 static int compare_propname(const void *a, const void *b) {
@@ -676,17 +730,17 @@
 DavPropName* dav_get_property_names(DavResource *res, size_t *count) {
     DavResourceData *data = res->data;
     
-    *count = data->properties->count;
+    *count = data->properties->size;
     DavPropName *names = dav_session_calloc(
             res->session,
             *count,
             sizeof(DavPropName));
     
     
-    UcxMapIterator i = ucx_map_iterator(data->properties);
+    CxIterator i = cxMapIteratorValues(data->properties);
     DavProperty *value;
     int j = 0;
-    UCX_MAP_FOREACH(key, value, i) {
+    cx_foreach(DavProperty*, value, i) {
         DavPropName *name = &names[j];
         
         name->ns = value->ns->name;
@@ -726,34 +780,35 @@
 
 
 int dav_load(DavResource *res) {
-    UcxBuffer *rqbuf = create_allprop_propfind_request();
+    CxBuffer *rqbuf = create_allprop_propfind_request();
     int ret = dav_propfind(res->session, res, rqbuf);
-    ucx_buffer_free(rqbuf);
+    cxBufferFree(rqbuf);
     return ret;
 }
 
 int dav_load_prop(DavResource *res, DavPropName *properties, size_t numprop) {
-    UcxMempool *mp = ucx_mempool_new(64);
+    CxMempool *mp = cxBasicMempoolCreate(64);
+    const CxAllocator *a = mp->allocator;
     
-    UcxList *proplist = NULL;
+    CxList *proplist = cxArrayListCreate(a, NULL, sizeof(DavProperty), numprop);
     for(size_t i=0;i<numprop;i++) {
-        DavProperty *p = ucx_mempool_malloc(mp, sizeof(DavProperty));
-        p->name = properties[i].name;
-        p->ns = ucx_mempool_malloc(mp, sizeof(DavNamespace));
-        p->ns->name = properties[i].ns;
+        DavProperty p;
+        p.name = properties[i].name;
+        p.ns = cxMalloc(a, sizeof(DavNamespace));
+        p.ns->name = properties[i].ns;
         if(!strcmp(properties[i].ns, "DAV:")) {
-            p->ns->prefix = "D";
+            p.ns->prefix = "D";
         } else {
-            p->ns->prefix = ucx_asprintf(mp->allocator, "x%d", i).ptr;
+            p.ns->prefix = cx_asprintf_a(a, "x%d", (int)i).ptr;
         }
-        p->value = NULL;
-        proplist = ucx_list_append_a(mp->allocator, proplist, p);
+        p.value = NULL;
+        cxListAdd(proplist, &p);
     }
     
-    UcxBuffer *rqbuf = create_propfind_request(res->session, proplist, "propfind", 0);
+    CxBuffer *rqbuf = create_propfind_request(res->session, proplist, "propfind", 0);
     int ret = dav_propfind(res->session, res, rqbuf);
-    ucx_buffer_free(rqbuf);
-    ucx_mempool_destroy(mp);
+    cxBufferFree(rqbuf);
+    cxMempoolDestroy(mp);
     return ret;
 }
 
@@ -805,7 +860,7 @@
         CURLcode ret;
         if(encryption) {
             AESEncrypter *enc = NULL;
-            UcxBuffer *buf = NULL;
+            CxBuffer *buf = NULL;
             if(data->read) {
                 enc = aes_encrypter_new(
                         sn->key,
@@ -813,13 +868,13 @@
                         data->read,
                         data->seek);
             } else {
-                buf = ucx_buffer_new(data->content, data->length, 0);
+                buf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0);
                 buf->size = data->length;
                 enc = aes_encrypter_new(
                         sn->key,
                         buf,
-                        (dav_read_func)ucx_buffer_read,
-                        (dav_seek_func)dav_buffer_seek);
+                        (dav_read_func)cxBufferRead,
+                        (dav_seek_func)cxBufferSeek);
             }
               
             // put resource
@@ -838,7 +893,7 @@
             
             aes_encrypter_close(enc);
             if(buf) {
-                ucx_buffer_free(buf);
+                cxBufferFree(buf);
             }
             
             // add crypto properties
@@ -851,15 +906,15 @@
             free(enc_hash);
         } else if((sn->flags & DAV_SESSION_STORE_HASH) == DAV_SESSION_STORE_HASH) {
             HashStream hstr;
-            UcxBuffer *iobuf = NULL;
+            CxBuffer *iobuf = NULL;
             if(!data->read) {
-                iobuf = ucx_buffer_new(data->content, data->length, 0);
+                iobuf = cxBufferCreate(data->content, data->length, cxDefaultAllocator, 0);
                 iobuf->size = data->length;
                 init_hash_stream(
                         &hstr,
                         iobuf,
-                        (dav_read_func)ucx_buffer_read,
-                        (dav_seek_func)ucx_buffer_seek);
+                        (dav_read_func)cxBufferRead,
+                        (dav_seek_func)cxBufferSeek);
             } else {
                 init_hash_stream(
                         &hstr,
@@ -900,7 +955,7 @@
             res->session->error = 0;
             // cleanup node data
             if(!data->read) {
-                ucx_mempool_free(sn->mp, data->content);
+                cxFree(sn->mp->allocator, data->content);
             }
             data->content = NULL;
             data->read = NULL;
@@ -917,51 +972,56 @@
         int ret = 1;
         
         if(crypto_res) {
-            UcxBuffer *rqbuf = create_cryptoprop_propfind_request();
+            CxBuffer *rqbuf = create_cryptoprop_propfind_request();
             ret = dav_propfind(res->session, res, rqbuf);
-            ucx_buffer_free(rqbuf);
+            cxBufferFree(rqbuf);
         }
         
         if(!ret) {
             DavXmlNode *crypto_prop_node = dav_get_property_ns(crypto_res, DAV_NS, "crypto-prop");
-            UcxMap *crypto_props = parse_crypto_prop(sn, sn->key, crypto_prop_node);
+            CxMap *crypto_props = parse_crypto_prop(sn, sn->key, crypto_prop_node);
             if(!crypto_props) {
                 // resource hasn't encrypted properties yet
-                crypto_props = ucx_map_new(32); // create new map
+                crypto_props = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32); // create new map
             }
             
             // remove all properties
-            UCX_FOREACH(elm, data->crypto_remove) {
-                if(crypto_props->count == 0) {
-                    break; // map already empty, can't remove any more
+            if(data->crypto_remove) {
+                CxIterator i = cxListIterator(data->crypto_remove);
+                cx_foreach(DavProperty *, property, i) {
+                    if(crypto_props->size == 0) {
+                        break; // map already empty, can't remove any more
+                    }
+
+                    cxmutstr key = dav_property_key(property->ns->name, property->name);
+                    DavProperty *existing_prop = cxMapGet(crypto_props, cx_hash_key(key.ptr, key.length));
+                    if(existing_prop) {
+                        // TODO: free existing_prop
+                    }                
+                    free(key.ptr);
                 }
-                
-                DavProperty *property = elm->data;
-                sstr_t key = dav_property_key(property->ns->name, property->name);
-                DavProperty *existing_prop = ucx_map_sstr_remove(crypto_props, key);
-                if(existing_prop) {
-                    // TODO: free existing_prop
-                }                
-                free(key.ptr);
             }
             
             // set properties
-            UCX_FOREACH(elm, data->crypto_set) {
-                DavProperty *property = elm->data;
-                sstr_t key = dav_property_key(property->ns->name, property->name);
-                DavProperty *existing_prop = ucx_map_sstr_remove(crypto_props, key);
-                ucx_map_sstr_put(crypto_props, key, property);
-                if(existing_prop) {
-                    // TODO: free existing_prop
-                }  
-                free(key.ptr);
+            if(data->crypto_set) {
+                CxIterator i = cxListIterator(data->crypto_set);
+                cx_foreach(DavProperty *, property, i) {
+                    cxmutstr keystr = dav_property_key(property->ns->name, property->name);
+                    CxHashKey key = cx_hash_key(keystr.ptr, keystr.length);
+                    DavProperty *existing_prop = cxMapRemoveAndGet(crypto_props, key);
+                    cxMapPut(crypto_props, key, property);
+                    if(existing_prop) {
+                        // TODO: free existing_prop
+                    }  
+                    free(keystr.ptr);
+                }
             }
             
             DavXmlNode *crypto_prop_value = create_crypto_prop(sn, crypto_props);
             if(crypto_prop_value) {
                 DavProperty *new_crypto_prop = createprop(sn, DAV_NS, "crypto-prop");
                 new_crypto_prop->value = crypto_prop_value;
-                data->set = ucx_list_prepend_a(sn->mp->allocator, data->set, new_crypto_prop);
+                add2propertylist(sn->mp->allocator, &data->set, new_crypto_prop);
             }
             
             dav_resource_free(crypto_res);
@@ -976,8 +1036,8 @@
     int r = 0;
     sn->error = DAV_OK;
     if(data->set || data->remove) {
-        UcxBuffer *request = create_proppatch_request(data);
-        UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
+        CxBuffer *request = create_proppatch_request(data);
+        CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
         //printf("request:\n%.*s\n\n", request->pos, request->space);
 
         CURLcode ret = do_proppatch_request(sn, locktoken, request, response);
@@ -994,8 +1054,8 @@
             r = -1;
         }
         
-        ucx_buffer_free(request);
-        ucx_buffer_free(response);
+        cxBufferFree(request);
+        cxBufferFree(response);
     }
      
     return r;
@@ -1129,7 +1189,7 @@
     DavLock *lock = dav_get_lock(res->session, res->path);
     char *locktoken = lock ? lock->token : NULL;
     
-    UcxBuffer *response = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *response = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     CURLcode ret = do_delete_request(res->session, locktoken, response);
     long status = 0;
     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
@@ -1145,7 +1205,7 @@
         r = 1;
     }
     
-    ucx_buffer_free(response);
+    cxBufferFree(response);
     return r;
 }
 
@@ -1172,7 +1232,7 @@
         curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
         if(status == 201) {
             // resource successfully created
-            char *name = util_resource_name(p);
+            char *name = (char*)util_resource_name(p);
             int len = strlen(name);
             if(name[len - 1] == '/') {
                 name[len - 1] = '\0';
@@ -1225,9 +1285,9 @@
         // if the session has encrypted file names, add crypto infos
         if(!resource_add_crypto_info(sn, res->href, res->name, NULL)) {
             // do a minimal propfind request
-            UcxBuffer *rqbuf = create_propfind_request(sn, NULL, "propfind", 0);
+            CxBuffer *rqbuf = create_propfind_request(sn, NULL, "propfind", 0);
             int ret = dav_propfind(sn, res, rqbuf);
-            ucx_buffer_free(rqbuf);
+            cxBufferFree(rqbuf);
             return ret;
         } else {
             return 1;
@@ -1331,26 +1391,26 @@
     CURL *handle = sn->handle;
     util_set_url(sn, dav_resource_get_href(res));
     
-    UcxBuffer *request = create_lock_request();
-    UcxBuffer *response = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *request = create_lock_request();
+    CxBuffer *response = cxBufferCreate(NULL, 512, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     CURLcode ret = do_lock_request(sn, request, response, timeout);
     
     //printf("\nlock\n");
     //printf("%.*s\n\n", request->size, request->space);
     //printf("%.*s\n\n", response->size, response->space);
     
-    ucx_buffer_free(request);
+    cxBufferFree(request);
     
     long status = 0;
     curl_easy_getinfo (handle, CURLINFO_RESPONSE_CODE, &status);
     if(ret == CURLE_OK && (status >= 200 && status < 300)) {
         LockDiscovery lock;
-        if(parse_lock_response(sn, response, &lock)) {
+        int parse_error = parse_lock_response(sn, response, &lock);
+        cxBufferFree(response);
+        if(parse_error) {
             sn->error = DAV_ERROR;
-            ucx_buffer_free(response);
             return -1;
         }
-        ucx_buffer_free(response);
         
         DavLock *l = dav_create_lock(sn, lock.locktoken, lock.timeout);
         free(lock.locktoken);
@@ -1373,7 +1433,7 @@
         }
     } else {
         dav_session_set_error(sn, ret, status);
-        ucx_buffer_free(response);
+        cxBufferFree(response);
         return -1;
     }
 }
@@ -1408,46 +1468,47 @@
         return 0;
     }
     
-    UcxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash);
-    UcxBuffer *response = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *request = create_crypto_proppatch_request(sn, sn->key, name, hash);
+    CxBuffer *response = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     util_set_url(sn, href);
     // TODO: lock
     CURLcode ret = do_proppatch_request(sn, NULL, request, response);
-    ucx_buffer_free(request);
+    cxBufferFree(request);
     long status = 0;
     curl_easy_getinfo (sn->handle, CURLINFO_RESPONSE_CODE, &status);
     if(ret == CURLE_OK && status == 207) {
         // TODO: parse response
         sn->error = DAV_OK;   
-        ucx_buffer_free(response);
+        cxBufferFree(response);
         return 0;
     } else {
         dav_session_set_error(sn, ret, status);
-        ucx_buffer_free(response);
+        cxBufferFree(response);
         return 1;
     }
 }
 
 /* ----------------------------- crypto-prop  ----------------------------- */
 
-DavXmlNode* create_crypto_prop(DavSession *sn, UcxMap *properties) {
+DavXmlNode* create_crypto_prop(DavSession *sn, CxMap *properties) {
     if(!sn->key) {
         return NULL;
     }
     
-    UcxBuffer *content = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *content = cxBufferCreate(NULL, 2048, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     // create an xml document containing all properties
-    UcxMap *nsmap = ucx_map_new(8);
-    ucx_map_cstr_put(nsmap, "DAV:", strdup("D"));
+    CxMap *nsmap = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 8);
+    nsmap->simple_destructor = free;
+    cxMapPut(nsmap, cx_hash_key_str("DAV:"), strdup("D"));
     
-    ucx_buffer_puts(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
-    ucx_buffer_puts(content, "<D:prop xmlns:D=\"DAV:\">\n");
+    cxBufferPutString(content, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+    cxBufferPutString(content, "<D:prop xmlns:D=\"DAV:\">\n");
     
-    UcxMapIterator i = ucx_map_iterator(properties);
+    CxIterator i = cxMapIteratorValues(properties);
     DavProperty *prop;
-    UCX_MAP_FOREACH(key, prop, i) {
+    cx_foreach(DavProperty*, prop, i) {
         DavXmlNode pnode;
         pnode.type = DAV_XML_ELEMENT;
         pnode.namespace = prop->ns->name;
@@ -1460,18 +1521,17 @@
         pnode.content = NULL;
         pnode.contentlength = 0;
         
-        dav_print_node(content, (write_func)ucx_buffer_write, nsmap, &pnode);
-        ucx_buffer_putc(content, '\n');
+        dav_print_node(content, (cx_write_func)cxBufferWrite, nsmap, &pnode);
+        cxBufferPut(content, '\n');
     }
     
-    ucx_buffer_puts(content, "</D:prop>");
+    cxBufferPutString(content, "</D:prop>");
     
-    ucx_map_free_content(nsmap, (ucx_destructor)free);
-    ucx_map_free(nsmap);
+    cxMapDestroy(nsmap);
     
     // encrypt xml document
     char *crypto_prop_content = aes_encrypt(content->space, content->size, sn->key);
-    ucx_buffer_free(content);
+    cxBufferDestroy(content);
     
     DavXmlNode *ret = NULL;
     if(crypto_prop_content) {
@@ -1481,7 +1541,7 @@
     return ret;
 }
 
-UcxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) {
+CxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node) {
     if(!node || node->type != DAV_XML_TEXT || node->contentlength == 0) {
         return NULL;
     }
@@ -1489,7 +1549,7 @@
     return parse_crypto_prop_str(sn, key, node->content);
 }
 
-UcxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) {
+CxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content) {
     size_t len = 0;
     char *dec_str = aes_decrypt(content, &len, key);
     
@@ -1519,7 +1579,7 @@
     }
     
     // ready to get the properties
-    UcxMap *map = ucx_map_new(32);
+    CxMap *map = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 32);
     xmlNode *n = xml_root->children;
     while(n) {
         if(n->type == XML_ELEMENT_NODE && n->ns && n->ns->href) {
@@ -1531,16 +1591,16 @@
                     dav_session_strdup(sn, (const char*)n->ns->prefix) : NULL;
             property->value = n->children ? dav_convert_xml(sn, n->children) : NULL;
             
-            sstr_t key = dav_property_key(property->ns->name, property->name);
-            ucx_map_sstr_put(map, key, property);
+            cxmutstr key = dav_property_key(property->ns->name, property->name);
+            cxMapPut(map, cx_hash_key(key.ptr, key.length), property);
             free(key.ptr);
         }
         n = n->next;
     }
     
     xmlFreeDoc(doc);
-    if(map->count == 0) {
-        ucx_map_free(map);
+    if(map->size == 0) {
+        cxMapDestroy(map);
         return NULL;
     }
     return map;
--- a/libidav/resource.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/resource.h	Fri Apr 21 21:25:32 2023 +0200
@@ -31,7 +31,8 @@
 
 #include "webdav.h"
 #include "crypto.h"
-#include <ucx/string.h>
+#include <cx/string.h>
+#include <cx/hash_key.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -40,16 +41,16 @@
 typedef struct DavResourceData   DavResourceData;
 
 struct DavResourceData {
-    UcxMap  *properties;
-    UcxList *set;
-    UcxList *remove;
-    UcxList *crypto_set;
-    UcxList *crypto_remove;
+    CxMap  *properties;
+    CxList *set;
+    CxList *remove;
+    CxList *crypto_set;
+    CxList *crypto_remove;
     
     /*
      * properties encapsulated in a crypto-prop property or NULL
      */
-    UcxMap *crypto_properties;
+    CxMap *crypto_properties;
     
     /*
      * char* or stream
@@ -58,7 +59,7 @@
     /*
      * if NULL, content is a char*
      */
-    read_func read;
+    dav_read_func read;
     /*
      * curl seek func
      */
@@ -109,29 +110,29 @@
     int eof;
 };
 
-DavResource* dav_resource_new_full(DavSession *sn, char *parent_path, char *name, char *href);
+DavResource* dav_resource_new_full(DavSession *sn, const char *parent_path, const char *name, char *href);
 
-void resource_free_properties(DavSession *sn, UcxMap *properties);
+void resource_free_properties(DavSession *sn, CxMap *properties);
 
-void resource_set_href(DavResource *res, sstr_t href);
+void resource_set_href(DavResource *res, cxstring href);
 
-void resource_set_info(DavResource *res, char *href_str);
+void resource_set_info(DavResource *res, const char *href_str);
 DavResourceData* resource_data_new(DavSession *sn);
 void resource_add_property(DavResource *res, const char *ns, const char *name, xmlNode *val);
-void resource_set_crypto_properties(DavResource *res, UcxMap *cprops);
+void resource_set_crypto_properties(DavResource *res, CxMap *cprops);
 DavXmlNode* resource_get_property(DavResource *res, const char *ns, const char *name);
 DavXmlNode* resource_get_encrypted_property(DavResource *res, const char *ns, const char *name);
-DavXmlNode* resource_get_property_k(DavResource *res, UcxKey key);
-DavXmlNode* resource_get_encrypted_property_k(DavResource *res, UcxKey key);
+DavXmlNode* resource_get_property_k(DavResource *res, CxHashKey key);
+DavXmlNode* resource_get_encrypted_property_k(DavResource *res, CxHashKey key);
 void resource_add_child(DavResource *parent, DavResource *child);
-void resource_add_ordered_child(DavResource *parent, DavResource *child, UcxList *ordercr);
+void resource_add_ordered_child(DavResource *parent, DavResource *child, CxList *ordercr);
 int resource_add_crypto_info(DavSession *sn, const char *href, const char *name, const char *hash);
 
-sstr_t dav_property_key_a(UcxAllocator *a, const char *ns, const char *name);
+cxmutstr dav_property_key_a(const CxAllocator *a, const char *ns, const char *name);
 
-DavXmlNode* create_crypto_prop(DavSession *sn, UcxMap *properties);
-UcxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node);
-UcxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content);
+DavXmlNode* create_crypto_prop(DavSession *sn, CxMap *properties);
+CxMap* parse_crypto_prop(DavSession *sn, DavKey *key, DavXmlNode *node);
+CxMap* parse_crypto_prop_str(DavSession *sn, DavKey *key, const char *content);
 
 #ifdef	__cplusplus
 }
--- a/libidav/session.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/session.c	Fri Apr 21 21:25:32 2023 +0200
@@ -30,8 +30,10 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <ucx/buffer.h>
-#include <ucx/utils.h>
+#include <cx/buffer.h>
+#include <cx/utils.h>
+#include <cx/basic_mempool.h>
+#include <cx/hash_map.h>
 
 #include "utils.h"
 #include "session.h"
@@ -42,14 +44,14 @@
     if(!base_url) {
         return NULL;
     }
-    sstr_t url = sstr(base_url);
+    cxstring url = cx_str(base_url);
     if(url.length == 0) {
         return NULL;
     }
     DavSession *sn = malloc(sizeof(DavSession));
     memset(sn, 0, sizeof(DavSession));
-    sn->mp = ucx_mempool_new(DAV_SESSION_MEMPOOL_SIZE);
-    sn->pathcache = ucx_map_new_a(sn->mp->allocator, DAV_PATH_CACHE_SIZE);
+    sn->mp = cxBasicMempoolCreate(DAV_SESSION_MEMPOOL_SIZE);
+    sn->pathcache = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, DAV_PATH_CACHE_SIZE);
     sn->key = NULL;
     sn->errorstr = NULL;
     sn->error = DAV_OK;
@@ -60,14 +62,11 @@
     sn->handle = curl_easy_init();
     curl_easy_setopt(sn->handle, CURLOPT_FOLLOWLOCATION, 1L);
     
-    // create lock manager
-    DavLockManager *locks = ucx_mempool_malloc(sn->mp, sizeof(DavLockManager));
-    locks->resource_locks = ucx_map_new_a(sn->mp->allocator, 16);
-    locks->collection_locks = NULL;
-    sn->locks = locks;
+    // lock manager is created on-demand
+    sn->locks = NULL;
 
     // set proxy
-    DavProxy *proxy = sstrprefix(url, S("https")) ? context->https_proxy
+    DavProxy *proxy = cx_strprefix(url, CX_STR("https")) ? context->https_proxy
                                                   : context->http_proxy;
     
     if (proxy->url) {
@@ -95,7 +94,7 @@
     curl_easy_setopt(sn->handle, CURLOPT_URL, base_url);
     
     // add to context
-    context->sessions = ucx_list_append(context->sessions, sn);
+    cxListAdd(context->sessions, sn);
     sn->context = context;
     
     return sn;
@@ -128,16 +127,17 @@
 }
 
 void dav_session_set_baseurl(DavSession *sn, char *base_url) {
+    const CxAllocator *a = sn->mp->allocator;
     if(sn->base_url) {
-        ucx_mempool_free(sn->mp, sn->base_url);
+        cxFree(a, sn->base_url);
     }
     
-    sstr_t url = sstr(base_url);
+    cxstring url = cx_str(base_url);
     if(url.ptr[url.length - 1] == '/') {
-        sstr_t url = sstrdup_a(sn->mp->allocator, sstr(base_url));
-        sn->base_url = url.ptr;
+        cxmutstr url_m = cx_strdup_a(a, cx_str(base_url));
+        sn->base_url = url_m.ptr;
     } else {
-        char *url_str = ucx_mempool_malloc(sn->mp, url.length + 2);
+        char *url_str = cxMalloc(a, url.length + 2);
         memcpy(url_str, base_url, url.length);
         url_str[url.length]     = '/';
         url_str[url.length + 1] = '\0';
@@ -170,17 +170,17 @@
     return dav_session_curl_perform_buf(sn, NULL, NULL, status);
 }
 
-CURLcode dav_session_curl_perform_buf(DavSession *sn, UcxBuffer *request, UcxBuffer *response, long *status) {
+CURLcode dav_session_curl_perform_buf(DavSession *sn, CxBuffer *request, CxBuffer *response, long *status) {
     CURLcode ret = curl_easy_perform(sn->handle);
     long http_status;
     curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status);
     if(ret == CURLE_OK && http_status == 401 && sn->auth_prompt) {
         if(!sn->auth_prompt(sn, sn->authprompt_userdata)) {
             if(request) {
-                ucx_buffer_seek(request, 0, SEEK_SET);
+                cxBufferSeek(request, 0, SEEK_SET);
             }
             if(response) {
-                ucx_buffer_seek(response, 0, SEEK_SET);
+                cxBufferSeek(response, 0, SEEK_SET);
             }
             ret = curl_easy_perform(sn->handle);
             curl_easy_getinfo(sn->handle, CURLINFO_RESPONSE_CODE, &http_status);
@@ -274,43 +274,40 @@
 
 void dav_session_destroy(DavSession *sn) { 
     // remove session from context
-    UcxList *sessions = sn->context->sessions;
-    ssize_t i = ucx_list_find(sessions, sn, ucx_cmp_ptr, NULL);
-    if(i >= 0)  {
-        UcxList *elm = ucx_list_get(sessions, i);
-        if(elm) {
-            sn->context->sessions = ucx_list_remove(sessions, elm);
-        }
+    CxList *sessions = sn->context->sessions;
+    ssize_t i = cxListFind(sessions, sn);
+    if(i >= 0) {
+        cxListRemove(sessions, i);
     }
     
-    ucx_mempool_destroy(sn->mp);
+    cxMempoolDestroy(sn->mp);
     curl_easy_cleanup(sn->handle);
     free(sn);
 }
 
 
 void* dav_session_malloc(DavSession *sn, size_t size) {
-    return ucx_mempool_malloc(sn->mp, size);
+    return cxMalloc(sn->mp->allocator, size);
 }
 
 void* dav_session_calloc(DavSession *sn, size_t nelm, size_t size) {
-    return ucx_mempool_calloc(sn->mp, nelm, size);
+    return cxCalloc(sn->mp->allocator, nelm, size);
 }
 
 void* dav_session_realloc(DavSession *sn, void *ptr, size_t size) {
-    return ucx_mempool_realloc(sn->mp, ptr, size);
+    return cxRealloc(sn->mp->allocator, ptr, size);
 }
 
 void  dav_session_free(DavSession *sn, void *ptr) {
-    ucx_mempool_free(sn->mp, ptr);
+    cxFree(sn->mp->allocator, ptr);
 }
 
 char* dav_session_strdup(DavSession *sn, const char *str) {
-    return sstrdup_a(sn->mp->allocator, sstr((char*)str)).ptr;
+    return cx_strdup_a(sn->mp->allocator, cx_str((char*)str)).ptr;
 }
 
 
-char* dav_session_create_plain_href(DavSession *sn, char *path) {
+char* dav_session_create_plain_href(DavSession *sn, const char *path) {
     if(!DAV_ENCRYPT_NAME(sn) && !DAV_DECRYPT_NAME(sn)) {
         // non encrypted file names
         char *url = util_path_to_url(sn, path);
@@ -322,11 +319,14 @@
     }
 }
 
-char* dav_session_get_href(DavSession *sn, char *path) {
+char* dav_session_get_href(DavSession *sn, const char *path) {
     if(DAV_DECRYPT_NAME(sn) || DAV_ENCRYPT_NAME(sn)) {
-        sstr_t p = sstr(path);
-        UcxBuffer *href = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
-        UcxBuffer *pbuf = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
+        cxstring p = cx_str(path);
+        CxBuffer href;
+        CxBuffer pbuf;
+        cxBufferInit(&href, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+        cxBufferInit(&pbuf, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+        
         int start = 0;
         int begin = 0;
         
@@ -334,11 +334,11 @@
         char *cp = strdup(path);
         //printf("cp: %s\n", cp);
         while(strlen(cp) > 1) {
-            char *cached = ucx_map_cstr_get(sn->pathcache, cp);
+            char *cached = cxMapGet(sn->pathcache, cx_hash_key_str(cp));
             if(cached) {
                 start = strlen(cp);
                 begin = start;
-                ucx_buffer_puts(href, cached);
+                cxBufferPutString(&href, cached);
                 break;
             } else {
                 // check, if the parent path is cached
@@ -348,101 +348,96 @@
             }
         }
         free(cp);
-        if(href->pos == 0) {
+        if(href.pos == 0) {
             // if there are no cached elements we have to add the base url path
             // to the href buffer
-            ucx_buffer_puts(href, util_url_path(sn->base_url));
+            cxBufferPutString(&href, util_url_path(sn->base_url));
         }
         
         // create resource for name lookup
-        sstr_t rp = sstrdup(sstrn(path, start));
+        cxmutstr rp = cx_strdup(cx_strn(path, start));
         DavResource *root = dav_resource_new(sn, rp.ptr);
         free(rp.ptr);
-        resource_set_href(root, sstrn(href->space, href->pos));
+        resource_set_href(root, cx_strn(href.space, href.pos));
         
         // create request buffer for propfind requests
-        UcxBuffer *rqbuf = create_basic_propfind_request();
+        CxBuffer *rqbuf = create_basic_propfind_request();
         
-        sstr_t remaining = sstrsubs(p, start);
-        ssize_t nelm = 0;
-        sstr_t *elms = sstrsplit(remaining, S("/"), &nelm);
+        cxstring remaining = cx_strsubs(p, start);
+        CxStrtokCtx elms = cx_strtok(remaining, CX_STR("/"), INT_MAX);
         DavResource *res = root;
-        ucx_buffer_puts(pbuf, res->path);
+        cxBufferPutString(&pbuf, res->path);
         // iterate over all remaining path elements
-        for(int i=0;i<nelm;i++) {
-            sstr_t elm = elms[i];
+        cxstring elm;
+        while(cx_strtok_next(&elms, &elm)) {
             if(elm.length > 0) {
                 //printf("elm: %.*s\n", elm.length, elm.ptr);
                 DavResource *child = dav_find_child(sn, res, rqbuf, elm.ptr);
                 
                 // if necessary add a path separator
-                if(pbuf->space[pbuf->pos-1] != '/') {
-                    if(href->space[href->pos-1] != '/') {
-                        ucx_buffer_putc(href, '/');
+                if(pbuf.space[pbuf.pos-1] != '/') {
+                    if(href.space[href.pos-1] != '/') {
+                        cxBufferPut(&href, '/');
                     }
-                    ucx_buffer_putc(pbuf, '/');
+                    cxBufferPut(&pbuf, '/');
                 }
                 // add last path/href to the cache
-                sstr_t pp = sstrn(pbuf->space, pbuf->size);
-                sstr_t hh = sstrn(href->space, href->size);
+                cxstring pp = cx_strn(pbuf.space, pbuf.size);
+                cxstring hh = cx_strn(href.space, href.size);
                 dav_session_cache_path(sn, pp, hh);
                 
-                ucx_buffer_write(elm.ptr, 1, elm.length, pbuf);
+                cxBufferWrite(elm.ptr, 1, elm.length, &pbuf);
                 if(child) {
                     // href is already URL encoded, so don't encode again
-                    ucx_buffer_puts(href, util_resource_name(child->href));
+                    cxBufferPutString(&href, util_resource_name(child->href));
                     res = child;
                 } else if(DAV_ENCRYPT_NAME(sn)) {
                     char *random_name = util_random_str();
-                    ucx_buffer_puts(href, random_name);
+                    cxBufferPutString(&href, random_name);
                     free(random_name);
                 } else {
                     // path is not URL encoded, so we have to do this here
-                    scstr_t resname = scstr(util_resource_name(path));
+                    cxstring resname = cx_str(util_resource_name((const char*)path));
                     // the name of collections ends with
                     // a trailing slash, which MUST NOT be encoded
                     if(resname.ptr[resname.length-1] == '/') {
                         char *esc = curl_easy_escape(sn->handle,
                                 resname.ptr, resname.length-1);
-                        ucx_buffer_write(esc, 1, strlen(esc), href);
-                        ucx_buffer_putc(href, '/');
+                        cxBufferWrite(esc, 1, strlen(esc), &href);
+                        cxBufferPut(&href, '/');
                         curl_free(esc);
                     } else  {
                         char *esc = curl_easy_escape(sn->handle,
                                 resname.ptr, resname.length);
-                        ucx_buffer_write(esc, 1, strlen(esc), href);
+                        cxBufferWrite(esc, 1, strlen(esc), &href);
                         curl_free(esc);
                     }
                 }
             }
-            
-            // cleanup
-            free(elm.ptr);
         }
-        free(elms);
         
         // if necessary add a path separator
         if(p.ptr[p.length-1] == '/') {
-            if(href->space[href->pos-1] != '/') {
-                ucx_buffer_putc(href, '/');
+            if(href.space[href.pos-1] != '/') {
+                cxBufferPut(&href, '/');
             }
-            ucx_buffer_putc(pbuf, '/');
+            cxBufferPut(&pbuf, '/');
         }
         // add the final path to the cache
-        sstr_t pp = sstrn(pbuf->space, pbuf->size);
-        sstr_t hh = sstrn(href->space, href->size);
+        cxstring pp = cx_strn(pbuf.space, pbuf.size);
+        cxstring hh = cx_strn(href.space, href.size);
         dav_session_cache_path(sn, pp, hh);
         
-        sstr_t href_str = sstrdup_a(
+        cxmutstr href_str = cx_strdup_a(
                 sn->mp->allocator,
-                sstrn(href->space,
-                href->size));
+                cx_strn(href.space, href.size));
         
         // cleanup
         dav_resource_free_all(root);
-        ucx_buffer_free(rqbuf);
-        ucx_buffer_free(pbuf);
-        ucx_buffer_free(href);
+        cxBufferFree(rqbuf);
+        
+        cxBufferDestroy(&pbuf);
+        cxBufferDestroy(&href);
         
         return href_str.ptr;
     } else {
@@ -450,7 +445,7 @@
     }
 }
 
-DavResource* dav_find_child(DavSession *sn, DavResource *res, UcxBuffer *rqbuf, char *name) {
+DavResource* dav_find_child(DavSession *sn, DavResource *res, CxBuffer *rqbuf, const char *name) {
     if(res && !dav_propfind(sn, res, rqbuf)) {
         DavResource *child = res->children;
         while(child) {
@@ -463,16 +458,17 @@
     return NULL;
 }
 
-void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href) {
-    char *elm = ucx_map_sstr_get(sn->pathcache, path);
+void dav_session_cache_path(DavSession *sn, cxstring path, cxstring href) {
+    CxHashKey path_key = cx_hash_key(path.ptr, path.length);
+    char *elm = cxMapGet(sn->pathcache, path_key);
     if(!elm) {
-        href = sstrdup_a(sn->mp->allocator, href);
-        ucx_map_sstr_put(sn->pathcache, path, href.ptr);
+        cxmutstr href_s = cx_strdup_a(sn->mp->allocator, href);
+        cxMapPut(sn->pathcache, path_key, href_s.ptr);
     }
 }
 
 
-DavLock* dav_create_lock(DavSession *sn, char *token, char *timeout) {
+DavLock* dav_create_lock(DavSession *sn, const char *token, char *timeout) {
     DavLock *lock = dav_session_malloc(sn, sizeof(DavLock));
     lock->path = NULL;
     lock->token = dav_session_strdup(sn, token);
@@ -490,77 +486,79 @@
     dav_session_free(sn, lock);
 }
 
-int dav_add_resource_lock(DavSession *sn, char *path, DavLock *lock) {
-    DavLockManager *locks = sn->locks;
-    if(ucx_map_cstr_get(locks->resource_locks, path)) {
-        return -1;
-    }
-    
-    ucx_map_cstr_put(locks->resource_locks, path, lock);
+
+static int dav_lock_cmp(void const *left, void const *right) {
+    const DavLock *l = left;
+    const DavLock *r = right;
+    return strcmp(l->path, r->path);
+}
+
+static int create_lock_manager(DavSession *sn) {
+    // create lock manager
+    DavLockManager *locks = cxMalloc(sn->mp->allocator, sizeof(DavLockManager));
+    locks->resource_locks = cxHashMapCreate(sn->mp->allocator, CX_STORE_POINTERS, 16);
+    locks->collection_locks = cxLinkedListCreate(sn->mp->allocator, dav_lock_cmp, CX_STORE_POINTERS);
+    sn->locks = locks;
     return 0;
 }
 
-static void insert_lock(DavSession *sn, UcxList *elm, UcxList *newelm) {
-    UcxList *next = elm->next;
-    if(next) {
-        next->prev = newelm;
-        newelm->next = next;
+static DavLockManager* get_lock_manager(DavSession *sn) {
+    DavLockManager *locks = sn->locks;
+    if(!locks) {
+        if(create_lock_manager(sn)) {
+            return NULL;
+        }
+        locks = sn->locks;
     }
-    newelm->prev = elm;
-    elm->next = newelm;
+    return locks;
 }
 
-int dav_add_collection_lock(DavSession *sn, char *path, DavLock *lock) {
-    DavLockManager *locks = sn->locks;
-    if(!locks->collection_locks) {
-        locks->collection_locks = ucx_list_append_a(
-                sn->mp->allocator,
-                NULL,
-                lock);
-        lock->path = dav_session_strdup(sn, path);
-        return 0;
+int dav_add_resource_lock(DavSession *sn, const char *path, DavLock *lock) {
+    DavLockManager *locks = get_lock_manager(sn);
+    if(!locks) {
+        return -1;
     }
     
-    UcxList *elm = locks->collection_locks;
-    for(;;) {
-        DavLock *l = elm->data;
-        int cmp = strcmp(path, l->path);
-        if(cmp > 0) {
-            UcxList *newelm = ucx_list_append_a(sn->mp->allocator, NULL, lock);
-            lock->path = dav_session_strdup(sn, path);
-            insert_lock(sn, elm, newelm);
-        } else if(cmp == 0) {
-            return -1;
-        }
-        
-        if(elm->next) {
-            elm = elm->next;
-        } else {
-            UcxList *newelm = ucx_list_append_a(sn->mp->allocator, NULL, lock);
-            lock->path = dav_session_strdup(sn, path);
-            ucx_list_concat(elm, newelm);
-            break;
-        }
+    CxHashKey path_key = cx_hash_key_str(path);
+    if(cxMapGet(locks->resource_locks, path_key)) {
+        return -1;
     }
     
+    cxMapPut(locks->resource_locks, path_key, lock);
+    return 0;
+}
+
+int dav_add_collection_lock(DavSession *sn, const char *path, DavLock *lock) {
+    DavLockManager *locks = get_lock_manager(sn);
+    if(!locks) {
+        return -1;
+    }
+    
+    cxListAdd(locks->collection_locks, lock);
+    cxListSort(locks->collection_locks);
+    
     return 0;
 }
 
-DavLock* dav_get_lock(DavSession *sn, char *path) {
-    DavLockManager *locks = sn->locks;
+DavLock* dav_get_lock(DavSession *sn, const char *path) {
+    DavLockManager *locks = get_lock_manager(sn);
+    if(!locks) {
+        return NULL;
+    }
     
-    DavLock *lock = ucx_map_cstr_get(locks->resource_locks, path);
+    cxstring p = cx_str(path);
+    
+    DavLock *lock = cxMapGet(locks->resource_locks, cx_hash_key(p.ptr, p.length));
     if(lock) {
         return lock;
     }
     
-    sstr_t p = sstr(path);
-    UCX_FOREACH(elm, locks->collection_locks) {
-        DavLock *cl = elm->data;
+    CxIterator i = cxListIterator(locks->collection_locks);
+    cx_foreach(DavLock*, cl, i) {
         int cmd = strcmp(path, cl->path);
         if(cmd == 0) {
             return cl;
-        } else if(sstrprefix(p, sstr(cl->path)))  {
+        } else if(cx_strprefix(p, cx_str(cl->path)))  {
             return cl;
         } else if(cmd > 0) {
             break;
@@ -570,26 +568,25 @@
     return NULL;
 }
 
-void dav_remove_lock(DavSession *sn, char *path, DavLock *lock) {
-    DavLockManager *locks = sn->locks;
+void dav_remove_lock(DavSession *sn, const char *path, DavLock *lock) {
+    DavLockManager *locks = get_lock_manager(sn);
+    if(!locks) {
+        return;
+    }
     
-    if(ucx_map_cstr_remove(locks->resource_locks, path)) {
+    if(cxMapRemoveAndGet(locks->resource_locks, cx_hash_key_str(path))) {
         return;
     }
     
-    UcxList *rm = NULL;
-    UCX_FOREACH(elm, locks->collection_locks) {
-        DavLock *cl = elm->data;
+    CxMutIterator i = cxListMutIterator(locks->collection_locks);
+    int rm = 0;
+    cx_foreach(DavLock* , cl, i) {
+        if(rm) {
+            break;
+        }
         if(cl == lock) {
-            rm = elm;
-            break;
+            cxIteratorFlagRemoval(i);
+            rm = 1;
         }
     }
-    
-    if(rm) {
-        locks->collection_locks = ucx_list_remove_a(
-                sn->mp->allocator,
-                locks->collection_locks,
-                rm);
-    }
 }
--- a/libidav/session.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/session.h	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #ifndef DAV_SESSION_H
 #define	DAV_SESSION_H
 
-#include <ucx/buffer.h>
+#include <cx/buffer.h>
 #include "webdav.h"
 
 #ifdef	__cplusplus
@@ -74,19 +74,19 @@
 } DavPathCacheElement;
 */
     
-typedef struct DavLock {
+typedef struct DavLock DavLock;
+struct DavLock {
     char *path;
     char *token;
-    
-} DavLock;
+};
 
 typedef struct DavLockManager {
-    UcxMap  *resource_locks;
-    UcxList *collection_locks;
+    CxMap  *resource_locks;
+    CxList *collection_locks;
 } DavLockManager;
 
 CURLcode dav_session_curl_perform(DavSession *sn, long *status);
-CURLcode dav_session_curl_perform_buf(DavSession *sn, UcxBuffer *request, UcxBuffer *response, long *status);
+CURLcode dav_session_curl_perform_buf(DavSession *sn, CxBuffer *request, CxBuffer *response, long *status);
 
 int dav_session_get_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
 int dav_session_put_progress(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
@@ -94,23 +94,23 @@
 void dav_session_set_error(DavSession *sn, CURLcode c, int status);
 void dav_session_set_errstr(DavSession *sn, const char *str);
 
-char* dav_session_create_plain_href(DavSession *sn, char *path);
+char* dav_session_create_plain_href(DavSession *sn, const char *path);
 
-char* dav_session_get_href(DavSession *sn, char *path);
+char* dav_session_get_href(DavSession *sn, const char *path);
 
-DavResource* dav_find_child(DavSession *sn, DavResource *res, UcxBuffer *rqbuf, char *name);
+DavResource* dav_find_child(DavSession *sn, DavResource *res, CxBuffer *rqbuf, const char *name);
 
-void dav_session_cache_path(DavSession *sn, sstr_t path, sstr_t href);
+void dav_session_cache_path(DavSession *sn, cxstring path, cxstring href);
 
 
-DavLock* dav_create_lock(DavSession *sn, char *token, char *timeout);
+DavLock* dav_create_lock(DavSession *sn, const char *token, char *timeout);
 void dav_destroy_lock(DavSession *sn, DavLock *lock);
 
-int dav_add_resource_lock(DavSession *sn, char *path, DavLock *lock);
-int dav_add_collection_lock(DavSession *sn, char *path, DavLock *lock);
+int dav_add_resource_lock(DavSession *sn, const char *path, DavLock *lock);
+int dav_add_collection_lock(DavSession *sn, const char *path, DavLock *lock);
 
-DavLock* dav_get_lock(DavSession *sn, char *path);
-void dav_remove_lock(DavSession *sn, char *path, DavLock *lock);
+DavLock* dav_get_lock(DavSession *sn, const char *path);
+void dav_remove_lock(DavSession *sn, const char *path, DavLock *lock);
 
 #ifdef	__cplusplus
 }
--- a/libidav/utils.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/utils.c	Fri Apr 21 21:25:32 2023 +0200
@@ -32,9 +32,10 @@
 #include <string.h>
 #include <errno.h>
 #include <ctype.h>
-#include <ucx/string.h>
-#include <ucx/buffer.h>
-#include <ucx/utils.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>
 
@@ -66,7 +67,7 @@
 #include <openssl/rand.h>
 */
 
-static size_t extractval(sstr_t str, char *result, char delim) {
+static size_t extractval(cxstring str, char *result, char delim) {
     size_t n = 0;
     for(size_t i = 0; i < str.length ; i++) {
         if(isdigit(str.ptr[i])) {
@@ -93,19 +94,19 @@
     char conv[16];
     
     // work on the trimmed string
-    sstr_t date = sstrtrim(sstr(iso8601str));
+    cxstring date = cx_strtrim(cx_str(iso8601str));
 
-    sstr_t time = sstrchr(date, 'T');
+    cxstring time = cx_strchr(date, 'T');
     if(time.length == 0) {
         return 0;
     }
     date.length = time.ptr - date.ptr;
     time.ptr++; time.length--;
     
-    sstr_t tzinfo;
-    if((tzinfo = sstrchr(time, 'Z')).length > 0 ||
-        (tzinfo = sstrchr(time, '+')).length > 0 ||
-        (tzinfo = sstrchr(time, '-')).length > 0) {
+    cxstring tzinfo;
+    if((tzinfo = cx_strchr(time, 'Z')).length > 0 ||
+        (tzinfo = cx_strchr(time, '+')).length > 0 ||
+        (tzinfo = cx_strchr(time, '-')).length > 0) {
         
         time.length = tzinfo.ptr - time.ptr;
     }
@@ -124,9 +125,9 @@
     tparts.tm_year = val / 10000 - 1900;
     
     // parse time and skip possible fractional seconds
-    sstr_t frac;
-    if((frac = sstrchr(time, '.')).length > 0 ||
-        (frac = sstrchr(time, ',')).length > 0) {
+    cxstring frac;
+    if((frac = cx_strchr(time, '.')).length > 0 ||
+        (frac = cx_strchr(time, ',')).length > 0) {
         time.length = frac.ptr - time.ptr;
     }
     if((time.length != 6 && time.length != 8)
@@ -144,7 +145,7 @@
         // local time
         tparts.tm_isdst = -1;
         return mktime(&tparts);
-    } else if(!sstrcmp(tzinfo, S("Z"))) {
+    } else if(!cx_strcmp(tzinfo, cx_str("Z"))) {
 #ifdef __FreeBSD__
         return timegm(&tparts);
 #else
@@ -276,13 +277,13 @@
     }
 }
 
-char* util_url_base_s(sstr_t url) {
+char* util_url_base_s(cxstring url) {
     size_t i = 0;
     if(url.length > 0) {
         int slmax;
-        if(sstrprefix(url, SC("http://"))) {
+        if(cx_strprefix(url, cx_str("http://"))) {
             slmax = 3;
-        } else if(sstrprefix(url, SC("https://"))) {
+        } else if(cx_strprefix(url, cx_str("https://"))) {
             slmax = 3;
         } else {
             slmax = 1;
@@ -298,16 +299,16 @@
             }
         }
     }
-    sstr_t server = sstrsubsl(url, 0, i);
-    return sstrdup(server).ptr;
+    cxstring server = cx_strsubsl(url, 0, i);
+    return cx_strdup(server).ptr;
 }
 
 char* util_url_base(char *url) {
-    return util_url_base_s(sstr(url));
+    return util_url_base_s(cx_str(url));
 }
 
-char* util_url_path(char *url) {
-    char *path = NULL;
+const char* util_url_path(const char *url) {
+    const char *path = NULL;
     size_t len = strlen(url);
     int slashcount = 0;
     int slmax;
@@ -335,7 +336,7 @@
     return path;
 }
 
-char* util_url_decode(DavSession *sn, char *url) {
+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_free(unesc);
@@ -345,24 +346,24 @@
 static size_t util_header_callback(char *buffer, size_t size,
         size_t nitems, void *data) {
     
-    sstr_t sbuffer = sstrn(buffer, size*nitems);
+    cxstring sbuffer = cx_strn(buffer, size*nitems);
     
-    UcxMap *map = (UcxMap*) data;
+    CxMap *map = (CxMap*) data;
     
     // if we get a status line, clear the map and exit
-    if(sstrprefix(sbuffer, S("HTTP/"))) {
-        ucx_map_free_content(map, free);
-        ucx_map_clear(map);
+    if(cx_strprefix(sbuffer, cx_str("HTTP/"))) {
+        // TODO: use new map destructor   ucx_map_free_content(map, free);
+        cxMapClear(map);
         return size*nitems;
     }
     
     // if we get the terminating CRLF, just exit
-    if(!sstrcmp(sbuffer, S("\r\n"))) {
+    if(!cx_strcmp(sbuffer, cx_str("\r\n"))) {
         return 2;
     }
     
-    sstr_t key = sbuffer;
-    sstr_t value = sstrchr(sbuffer, ':');
+    cxstring key = sbuffer;
+    cxstring value = cx_strchr(sbuffer, ':');
     
     if(value.length == 0) {
         return 0; // invalid header line
@@ -371,19 +372,20 @@
     key.length = value.ptr - key.ptr;
     value.ptr++; value.length--;
     
-    key = sstrlower(sstrtrim(key));
-    value = sstrdup(sstrtrim(value));
+    cxmutstr key_cp = cx_strdup(cx_strtrim(key));
+    cx_strlower(key_cp);
+    cxmutstr value_cp = cx_strdup(cx_strtrim(value));
         
-    ucx_map_sstr_put(map, key, value.ptr);
+    cxMapPut(map, cx_hash_key(key_cp.ptr, key_cp.length), value_cp.ptr);
     
-    free(key.ptr);
+    free(key_cp.ptr);
     
     return sbuffer.length;
 }
 
 int util_path_isrelated(const char *path1, const char *path2) {
-    scstr_t p1 = scstr(path1);
-    scstr_t p2 = scstr(path2);
+    cxstring p1 = cx_str(path1);
+    cxstring p2 = cx_str(path2);
     
     if(IS_PATH_SEPARATOR(p1.ptr[p1.length-1])) {
         p1.length--;
@@ -396,11 +398,11 @@
         return 0;
     }
     
-    if(!sstrcmp(p1, p2)) {
+    if(!cx_strcmp(p1, p2)) {
         return 1;
     }
     
-    if(sstrprefix(p2, p1)) {
+    if(cx_strprefix(p2, p1)) {
         if(IS_PATH_SEPARATOR(p2.ptr[p1.length])) {
             return 1;
         }
@@ -434,10 +436,11 @@
 
 char* util_path_normalize(const char *path) {
     size_t len = strlen(path);
-    UcxBuffer *buf = ucx_buffer_new(NULL, len+1, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, len+1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     if(path[0] == '/') {
-        ucx_buffer_putc(buf, '/');
+        cxBufferPut(&buf, '/');
     }
     
     int add_separator = 0;
@@ -453,25 +456,25 @@
             }
             
             if(seg_len > 0) {
-                scstr_t seg = scstrn(seg_ptr, seg_len);
-                if(!sstrcmp(seg, SC(".."))) {
-                    for(int j=buf->pos;j>=0;j--) {
-                        char t = buf->space[j];
+                cxstring seg = cx_strn(seg_ptr, seg_len);
+                if(!cx_strcmp(seg, CX_STR(".."))) {
+                    for(int j=buf.pos;j>=0;j--) {
+                        char t = buf.space[j];
                         if(IS_PATH_SEPARATOR(t) || j == 0) {
-                            buf->pos = j;
-                            buf->size = j;
-                            buf->space[j] = 0;
+                            buf.pos = j;
+                            buf.size = j;
+                            buf.space[j] = 0;
                             add_separator = IS_PATH_SEPARATOR(t) ? 1 : 0;
                             break;
                         }
                     }
-                } else if(!sstrcmp(seg, SC("."))) {
+                } else if(!cx_strcmp(seg, CX_STR("."))) {
                     // ignore
                 } else {
                     if(add_separator) {
-                        ucx_buffer_putc(buf, PATH_SEPARATOR);
+                        cxBufferPut(&buf, PATH_SEPARATOR);
                     }
-                    ucx_buffer_write(seg_ptr, 1, seg_len, buf);
+                    cxBufferWrite(seg_ptr, 1, seg_len, &buf);
                     add_separator = 1;
                 }
             }
@@ -480,13 +483,9 @@
         }
     }
     
-    ucx_buffer_putc(buf, 0);
-    
+    cxBufferPut(&buf, 0);
     
-    char *space = buf->space;
-    buf->flags = 0; // disable autofree
-    ucx_buffer_free(buf);
-    return space;
+    return buf.space;
 }
 
 static char* create_relative_path(const char *abspath, const char *base) {
@@ -523,7 +522,7 @@
     }
     
     char *ret = NULL;
-    UcxBuffer *out = NULL;
+    CxBuffer out;
     if(last_dir+1 < base_len) {
         // base is deeper than the link root, we have to go backwards
         int dircount = 0;
@@ -533,22 +532,19 @@
             }
         }
         
-        out = ucx_buffer_new(NULL, dircount*3+path_len-last_dir, UCX_BUFFER_AUTOEXTEND);
+        cxBufferInit(&out, NULL, dircount*3+path_len-last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
         
         for(int i=0;i<dircount;i++) {
-            ucx_buffer_puts(out, "../");
+            cxBufferPutString(&out, "../");
         }
     } else {
-        out = ucx_buffer_new(NULL, 1024, path_len - last_dir);
+        cxBufferInit(&out, NULL, path_len - last_dir, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     }
     
-    ucx_buffer_puts(out, abspath + last_dir + 1);
-    ucx_buffer_putc(out, 0);
-    out->flags = 0;
-    ret = out->space;
-    ucx_buffer_free(out);
+    cxBufferPutString(&out, abspath + last_dir + 1);
+    cxBufferPut(&out, 0);
     
-    return ret;
+    return out.space;
 }
 
 #ifdef _WIN32
@@ -581,7 +577,7 @@
 #endif
 
 
-void util_capture_header(CURL *handle, UcxMap* map) {
+void util_capture_header(CURL *handle, CxMap* map) {
     if(map) {
         curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, util_header_callback);
         curl_easy_setopt(handle, CURLOPT_HEADERDATA, map);
@@ -591,12 +587,12 @@
     }
 }
 
-char* util_resource_name(char *url) {
-    sstr_t urlstr = sstr(url);
+const char* util_resource_name(const char *url) {
+    cxstring urlstr = cx_str(url);
     if(urlstr.ptr[urlstr.length-1] == '/') {
         urlstr.length--;
     }
-    sstr_t resname = sstrrchr(urlstr, '/');
+    cxstring resname = cx_strrchr(urlstr, '/');
     if(resname.length > 1) {
         return resname.ptr+1;
     } else {
@@ -613,12 +609,12 @@
 }
 
 char* util_concat_path(const char *url_base, const char *p) {
-    sstr_t base = sstr((char*)url_base);
-    sstr_t path;
+    cxstring base = cx_str((char*)url_base);
+    cxstring path;
     if(p) {
-        path = sstr((char*)p);
+        path = cx_str((char*)p);
     } else {
-        path = sstrn("", 0);
+        path = CX_STR("");
     }
     
     int add_separator = 0;
@@ -632,24 +628,24 @@
         }
     }
     
-    sstr_t url;
+    cxmutstr url;
     if(add_separator) {
-        url = sstrcat(3, base, sstr("/"), path);
+        url = cx_strcat(3, base, CX_STR("/"), path);
     } else {
-        url = sstrcat(2, base, path);
+        url = cx_strcat(2, base, path);
     }
     
     return url.ptr;
 }
 
 char* util_get_url(DavSession *sn, const char *href) {
-    scstr_t base = scstr(sn->base_url);
-    scstr_t href_str = scstr(href);
+    cxstring base = cx_str(sn->base_url);
+    cxstring href_str = cx_str(href);
     
-    char *base_path = util_url_path(sn->base_url);
+    const char *base_path = util_url_path(sn->base_url);
     base.length -= strlen(base_path);
     
-    sstr_t url = sstrcat(2, base, href_str);
+    cxmutstr url = cx_strcat(2, base, href_str);
     return url.ptr;
 }
 
@@ -659,43 +655,38 @@
     free(url);
 }
 
-char* util_path_to_url(DavSession *sn, char *path) {
-    char *space = malloc(256);
-    UcxBuffer *url = ucx_buffer_new(space, 256, UCX_BUFFER_AUTOEXTEND);
+char* util_path_to_url(DavSession *sn, const char *path) {
+    CxBuffer url;
+    cxBufferInit(&url, NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     // add base url
-    ucx_buffer_write(sn->base_url, 1, strlen(sn->base_url), url);
+    cxBufferWrite(sn->base_url, 1, strlen(sn->base_url), &url);
     // remove trailing slash
-    ucx_buffer_seek(url, -1, SEEK_CUR);
+    cxBufferSeek(&url, -1, SEEK_CUR);
     
-    sstr_t p = sstr(path);
-    ssize_t ntk = 0;
-    sstr_t *tks = sstrsplit(p, S("/"), &ntk);
+    cxstring p = cx_str(path);
     
-    for(int i=0;i<ntk;i++) {
-        sstr_t node = tks[i];
+    CxStrtokCtx tkctx = cx_strtok(p, CX_STR("/"), INT_MAX);
+    cxstring node;
+    while(cx_strtok_next(&tkctx, &node)) {
         if(node.length > 0) {
             char *esc = curl_easy_escape(sn->handle, node.ptr, node.length);
-            ucx_buffer_putc(url, '/');
-            ucx_buffer_write(esc, 1, strlen(esc), url);
+            cxBufferPut(&url, '/');
+            cxBufferWrite(esc, 1, strlen(esc), &url);
             curl_free(esc);
         }
-        free(node.ptr);
-    }
-    free(tks);
-    if(path[p.length-1] == '/') {
-        ucx_buffer_putc(url, '/');
     }
-    ucx_buffer_putc(url, 0);
     
-    space = url->space;
-    ucx_buffer_free(url);
+    if(path[p.length-1] == '/') {
+        cxBufferPut(&url, '/');
+    }
+    cxBufferPut(&url, 0);
     
-    return space;
+    return url.space;
 }
 
 char* util_parent_path(const char *path) {
-    char *name = util_resource_name((char*)path);
+    const char *name = util_resource_name(path);
     size_t namelen = strlen(name);
     size_t pathlen = strlen(path);
     size_t parentlen = pathlen - namelen;
@@ -952,11 +943,11 @@
     return out;
 }
 
-char* util_encrypt_str(DavSession *sn, char *str, char *key) {
+char* util_encrypt_str(DavSession *sn, const char *str, const char *key) {
     DavKey *k = dav_context_get_key(sn->context, key);
     if(!k) {
         sn->error = DAV_ERROR;
-        sstr_t err = ucx_sprintf("Key %s not found", key);
+        cxmutstr err = cx_asprintf("Key %s not found", key);
         dav_session_set_errstr(sn, err.ptr);
         free(err.ptr);
         return NULL;
@@ -965,18 +956,18 @@
     return util_encrypt_str_k(sn, str, k);
 }
 
-char* util_encrypt_str_k(DavSession *sn, char *str, DavKey *key) {
+char* util_encrypt_str_k(DavSession *sn, const char *str, DavKey *key) {
     char *enc_str = aes_encrypt(str, strlen(str), key);
     char *ret_str = dav_session_strdup(sn, enc_str);
     free(enc_str);
     return ret_str;
 }
 
-char* util_decrypt_str(DavSession *sn, char *str, char *key) {
+char* util_decrypt_str(DavSession *sn, const char *str, const char *key) {
     DavKey *k = dav_context_get_key(sn->context, key);
     if(!k) {
         sn->error = DAV_ERROR;
-        sstr_t err = ucx_sprintf("Key %s not found", key);
+        cxmutstr err = cx_asprintf("Key %s not found", key);
         dav_session_set_errstr(sn, err.ptr);
         free(err.ptr);
         return NULL;
@@ -985,7 +976,7 @@
     return util_decrypt_str_k(sn, str, k);
 }
 
-char* util_decrypt_str_k(DavSession *sn, char *str, DavKey *key) {
+char* util_decrypt_str_k(DavSession *sn, const char *str, DavKey *key) {
     size_t len = 0;
     char *dec_str = aes_decrypt(str, &len, key);
     char *ret_str = dav_session_strdup(sn, dec_str);
@@ -997,7 +988,7 @@
     unsigned char *str = malloc(25);
     str[24] = '\0';
     
-    sstr_t t = S(
+    cxstring t = CX_STR(
             "01234567890"
             "abcdefghijklmnopqrstuvwxyz"
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
@@ -1021,6 +1012,8 @@
  * tokens are separated by space
  * sets sub to the substring and returns the remaining string
  */
+// TODO: remove if it isn't used
+/*
 sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub) {  
     int i;
     int token_start = -1;
@@ -1060,20 +1053,22 @@
         return str;
     }
 }
+*/
 
-sstr_t util_readline(FILE *stream) {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
+cxmutstr util_readline(FILE *stream) {
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     int c;
     while((c = fgetc(stream)) != EOF) {
         if(c == '\n') {
             break;
         }
-        ucx_buffer_putc(buf, c);
+        cxBufferPut(&buf, c);
     }
     
-    sstr_t str = sstrdup(sstrtrim(sstrn(buf->space, buf->size)));
-    ucx_buffer_free(buf);
+    cxmutstr str = cx_strdup(cx_strtrim(cx_strn(buf.space, buf.size)));
+    cxBufferDestroy(&buf);
     return str;
 }
 
@@ -1097,15 +1092,16 @@
 #endif
     
     // read password input
-    UcxBuffer *buf = ucx_buffer_new(NULL, 128, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 128, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     int c = 0;
     while((c = getpasswordchar()) != EOF) {
         if(c == '\n' || c == '\r') {
             break;
         }
-        ucx_buffer_putc(buf, c);
+        cxBufferPut(&buf, c);
     }
-    ucx_buffer_putc(buf, 0);
+    cxBufferPut(&buf, 0);
     fflush(stdin);
     
 #ifndef _WIN32
@@ -1115,12 +1111,10 @@
     }
 #endif
     
-    char *str = buf->space;
-    free(buf); // only free the UcxBuffer struct
-    return str;
+    return buf.space;
 }
 
-int util_exec_command(char *command, UcxBuffer *outbuf) {
+int util_exec_command(char *command, CxBuffer *outbuf) {
 #ifdef _WIN32
     fprintf(stderr, "util_exec_command unsupported\n");
     return 1;
@@ -1156,7 +1150,7 @@
         ssize_t r;
         char buf[1024];
         while((r = read(pout[0], buf, 1024)) > 0) {
-            ucx_buffer_write(buf, 1, r, outbuf);
+            cxBufferWrite(buf, 1, r, outbuf);
         }
     }
     
@@ -1173,14 +1167,13 @@
 
 char* util_hexstr(const unsigned char *data, size_t len) {
     size_t buflen = 2*len + 4;
-    UcxBuffer *buf = ucx_buffer_new(malloc(buflen), buflen + 1, 0);
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, buflen + 1, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     for(int i=0;i<len;i++) {
-        ucx_bprintf(buf, "%02x", data[i]);
+        cx_bprintf(&buf, "%02x", data[i]);
     }
-    ucx_buffer_putc(buf, 0);
-    char *str = buf->space;
-    ucx_buffer_free(buf);
-    return str;
+    cxBufferPut(&buf, 0);
+    return buf.space;
 }
 
 void util_remove_trailing_pathseparator(char *path) {
@@ -1215,3 +1208,25 @@
     
     return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH);    
 }
+
+struct regdestructor {
+    cx_destructor_func destructor;
+    void *data;
+};
+
+static void call_destructor(struct regdestructor *d) {
+    d->destructor(d->data);
+}
+
+int util_regdestr(CxMempool *mp, void *data, cx_destructor_func destructor) {
+    // the ucx maintainer doesn't like me anymore and forces me to 
+    // implement basic stuff by myself
+    struct regdestructor *reg = cxMalloc(mp->allocator, sizeof(struct regdestructor));
+    if(!reg) {
+        return 1;
+    }
+    reg->destructor = destructor;
+    reg->data = data;
+    cxMempoolSetDestructor(mp, reg, (cx_destructor_func)call_destructor);
+    return 0;
+}
--- a/libidav/utils.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/utils.h	Fri Apr 21 21:25:32 2023 +0200
@@ -36,8 +36,8 @@
 
 #include <sys/types.h>
 #include <libxml/tree.h>
-#include <ucx/string.h>
-#include <ucx/buffer.h>
+#include <cx/string.h>
+#include <cx/buffer.h>
 #include <sys/stat.h>
 #include <inttypes.h>
 
@@ -66,10 +66,10 @@
 int util_mkdir(char *path, mode_t mode);
 
 char* util_url_base(char *url);
-char* util_url_base_s(sstr_t url);
-char* util_url_path(char *url);
-char* util_url_decode(DavSession *sn, char *url);
-char* util_resource_name(char *url);
+char* util_url_base_s(cxstring url);
+const char* util_url_path(const char *url);
+char* util_url_decode(DavSession *sn, const char *url);
+const char* util_resource_name(const char *url);
 char* util_concat_path(const char *url_base, const char *path);
 char* util_get_url(DavSession *sn, const char *href);
 void util_set_url(DavSession *sn, const char *href);
@@ -84,9 +84,9 @@
 char* util_path_normalize(const char *path);
 char* util_create_relative_path(const char *abspath, const char *base);
 
-void util_capture_header(CURL *handle, UcxMap* map);
+void util_capture_header(CURL *handle, CxMap* map);
 
-char* util_path_to_url(DavSession *sn, char *path);
+char* util_path_to_url(DavSession *sn, const char *path);
 char* util_parent_path(const char *path);
 
 char* util_size_str(DavBool iscollection, uint64_t contentlength);
@@ -105,19 +105,19 @@
 char* util_base64decode_len(const char *in, int *outlen);
 char* util_base64encode(const char *in, size_t len);
 
-char* util_encrypt_str(DavSession *sn, char *str, char *key);
-char* util_encrypt_str_k(DavSession *sn, char *str, DavKey *key);
-char* util_decrypt_str(DavSession *sn, char *str, char *key);
-char* util_decrypt_str_k(DavSession *sn, char *str, DavKey *key);
+char* util_encrypt_str(DavSession *sn, const char *str, const char *key);
+char* util_encrypt_str_k(DavSession *sn, const char *str, DavKey *key);
+char* util_decrypt_str(DavSession *sn, const char *str, const char *key);
+char* util_decrypt_str_k(DavSession *sn, const char *str, DavKey *key);
 
 char* util_random_str();
 
-sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub);
+//sstr_t util_getsubstr_until_token(sstr_t str, sstr_t token, sstr_t *sub);
 
-sstr_t util_readline(FILE *stream);
+cxmutstr util_readline(FILE *stream);
 char* util_password_input(char *prompt);
 
-int util_exec_command(char *command, UcxBuffer *outbuf);
+int util_exec_command(char *command, CxBuffer *outbuf);
 
 char* util_hexstr(const unsigned char *data, size_t len);
 
@@ -125,6 +125,8 @@
 
 char* util_file_hash(const char *path);
 
+int util_regdestr(CxMempool *mp, void *data, cx_destructor_func destructor);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/libidav/versioning.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/versioning.c	Fri Apr 21 21:25:32 2023 +0200
@@ -74,32 +74,34 @@
     DavSession *sn = res->session;
     util_set_url(sn, dav_resource_get_href(res));
     
-    UcxList *proplist = NULL;
+    CxList *proplist = NULL;
     if(properties) {
-        proplist = parse_properties_string(sn->context, sstr(properties));
+        proplist = parse_properties_string(sn->context, cx_str(properties));
+        
+        // check if the list already contains a D:version-name property
+        int add_vname = 1;
+        CxIterator i = cxListIterator(proplist);
+        cx_foreach(DavProperty *, p, i) {
+            if(!strcmp(p->ns->name, "DAV:") && !strcmp(p->name, "version-name")) {
+                add_vname = 0;
+                break;
+            }
+        }
+        if(add_vname) {
+            // we need at least the D:version-name prop
+            DavProperty p;
+            p.ns = dav_get_namespace(sn->context, "D");
+            p.name = strdup("version-name");
+            p.value = NULL;
+            cxListInsert(proplist, 0, &p);
+        }
     }
     
-    // check if the list already contains a D:version-name property
-    int add_vname = 1;
-    UCX_FOREACH(elm, proplist) {
-        DavProperty *p = elm->data;
-        if(!strcmp(p->ns->name, "DAV:") && !strcmp(p->name, "version-name")) {
-            add_vname = 0;
-            break;
-        }
-    }
-    if(add_vname) {
-        // we need at least the D:version-name prop
-        DavProperty *p = malloc(sizeof(DavProperty));
-        p->ns = dav_get_namespace(sn->context, "D");
-        p->name = strdup("version-name");
-        p->value = NULL;
-        proplist = ucx_list_prepend(proplist, p);
-    }
+    
     
     // create a version-tree request, which is almost the same as propfind
-    UcxBuffer *rqbuf = create_propfind_request(sn, proplist, "version-tree", 1);
-    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *rqbuf = create_propfind_request(sn, proplist, "version-tree", 1);
+    CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     // do the request
     CURLcode ret = do_report_request(sn, rqbuf, rpbuf);
@@ -151,14 +153,14 @@
     }
     
     // cleanup
-    while(proplist) {
-        DavProperty *p = proplist->data;
-        free(p->name);
-        free(p);
-        UcxList *next = proplist->next;
-        free(proplist);
-        proplist = next;
+    if(proplist) {
+        CxIterator i = cxListIterator(proplist);
+        cx_foreach(DavProperty*, p, i) {
+            free(p->name);
+        }
+        cxListDestroy(proplist);
     }
+    
     if(error && versions) {
         DavResource *cur = versions;
         while(cur) {
--- a/libidav/webdav.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/webdav.c	Fri Apr 21 21:25:32 2023 +0200
@@ -35,8 +35,11 @@
 #include "webdav.h"
 #include "session.h"
 #include "methods.h"
-#include "ucx/buffer.h"
-#include "ucx/utils.h"
+#include <cx/buffer.h>
+#include <cx/utils.h>
+#include <cx/linked_list.h>
+#include <cx/hash_map.h>
+#include <cx/compare.h>
 #include "davqlparser.h"
 #include "davqlexec.h"
 
@@ -47,7 +50,7 @@
     if(!context) {
         return NULL;
     }
-    context->sessions = NULL;
+    context->sessions = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_intptr, CX_STORE_POINTERS);
     context->http_proxy = calloc(1, sizeof(DavProxy));
     if(!context->http_proxy) {
         dav_context_destroy(context);
@@ -58,16 +61,16 @@
         dav_context_destroy(context);
         return NULL;
     }
-    context->namespaces = ucx_map_new(16);
+    context->namespaces = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     if(!context->namespaces) {
         dav_context_destroy(context);
         return NULL;
     }
-    context->namespaceinfo = ucx_map_new(16);
+    context->namespaceinfo = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     if(!context->namespaceinfo) {
         dav_context_destroy(context);
     }
-    context->keys = ucx_map_new(16);
+    context->keys = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 16);
     if(!context->keys) {
         dav_context_destroy(context);
         return NULL;
@@ -97,12 +100,11 @@
 
 void dav_context_destroy(DavContext *ctx) {
     // destroy all sessions assoziated with this context
-    UcxList *elm = ctx->sessions;
-    while(elm) {
-        DavSession *sn = elm->data;
-        elm = elm->next;
+    CxIterator i = cxListIterator(ctx->sessions);
+    cx_foreach(DavSession*, sn, i) {
         dav_session_destroy(sn);
     }
+    cxListDestroy(ctx->sessions);
     if(ctx->http_proxy) {
         free(ctx->http_proxy);
     }
@@ -111,10 +113,8 @@
     }
     
     if(ctx->namespaces) {
-        UcxMapIterator i = ucx_map_iterator(ctx->namespaces);
-        UcxKey k;
-        DavNamespace *ns;
-        UCX_MAP_FOREACH(k, ns, i) {
+        i = cxMapIteratorValues(ctx->namespaces);
+        cx_foreach(DavNamespace*, ns, i) {
             if(!ns) continue;
             if(ns->prefix) {
                 free(ns->prefix);
@@ -124,16 +124,14 @@
             }
             free(ns);
         }
-        ucx_map_free(ctx->namespaces);
+        cxMapDestroy(ctx->namespaces);
     }
     if(ctx->namespaceinfo) {
         // TODO: implement
     }
     if(ctx->keys) {
-        UcxMapIterator i = ucx_map_iterator(ctx->keys);
-        UcxKey k;
-        DavKey *key;
-        UCX_MAP_FOREACH(k, key, i) {
+        i = cxMapIteratorValues(ctx->keys);
+        cx_foreach(DavKey*, key, i) {
             if(!key) continue;
             if(key->name) {
                 free(key->name);
@@ -143,19 +141,19 @@
             }
             free(key);
         }
-        ucx_map_free(ctx->keys);
+        cxMapDestroy(ctx->keys);
     }    
     
     free(ctx);
 }
 
 void dav_context_add_key(DavContext *context, DavKey *key) {
-    ucx_map_cstr_put(context->keys, key->name, key);
+    cxMapPut(context->keys, cx_hash_key_str(key->name), key);
 }
 
-DavKey* dav_context_get_key(DavContext *context, char *name) {
+DavKey* dav_context_get_key(DavContext *context, const char *name) {
     if(name) {
-        return ucx_map_cstr_get(context->keys, name);
+        return cxMapGet(context->keys, cx_hash_key_str(name));
     }
     return NULL;
 }
@@ -173,7 +171,7 @@
     if(p && n) {
         namespace->prefix = p;
         namespace->name = n;
-        err = ucx_map_cstr_put(context->namespaces, prefix, namespace);
+        err = cxMapPut(context->namespaces, cx_hash_key_str(prefix), namespace);
     }
     
     if(err) {
@@ -186,19 +184,20 @@
 }
 
 DavNamespace* dav_get_namespace(DavContext *context, const char *prefix) {
-    return ucx_map_cstr_get(context->namespaces, prefix);
+    return cxMapGet(context->namespaces, cx_hash_key_str(prefix));
 }
 
-DavNamespace* dav_get_namespace_s(DavContext *context, sstr_t prefix) {
-    return ucx_map_sstr_get(context->namespaces, prefix);
+DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix) {
+    return cxMapGet(context->namespaces, cx_hash_key(prefix.ptr, prefix.length));
 }
 
 int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt) {
-    DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns);
+    CxHashKey hkey = cx_hash_key_str(ns);
+    DavNSInfo *info = cxMapGet(context->namespaceinfo, hkey);
     if(!info) {
         info = calloc(1, sizeof(DavNSInfo));
         info->encrypt = encrypt;
-        ucx_map_cstr_put(context->namespaceinfo, ns, info);
+        cxMapPut(context->namespaceinfo, hkey, info);
     } else {
         info->encrypt = encrypt;
     }
@@ -206,7 +205,7 @@
 }
 
 int dav_namespace_is_encrypted(DavContext *context, const char *ns) {
-    DavNSInfo *info = ucx_map_cstr_get(context->namespaceinfo, ns);
+    DavNSInfo *info = cxMapGet(context->namespaceinfo, cx_hash_key_str(ns));
     if(info) {
         return info->encrypt;
     }
@@ -226,7 +225,7 @@
     if(pname) {
         DavNamespace *ns = dav_get_namespace_s(
                 ctx,
-                sstrn(prefixed_name, pname-prefixed_name));
+                cx_strn(prefixed_name, pname-prefixed_name));
         if(ns) {
             pns = ns->name;
             pname++;
@@ -250,7 +249,7 @@
     if(pname) {
         DavNamespace *ns = dav_get_namespace_s(
                 ctx,
-                sstrn(prefixed_name, pname-prefixed_name));
+                cx_strn(prefixed_name, pname-prefixed_name));
         if(ns) {
             *name = pname +1;
             return ns;
@@ -260,7 +259,7 @@
         }
     } else {
         *name = prefixed_name;
-        return dav_get_namespace_s(ctx, S("D"));
+        return dav_get_namespace_s(ctx, cx_str("D"));
     }
 }
 
@@ -270,7 +269,7 @@
     char *eff_url;
     curl_easy_getinfo(sn->handle, CURLINFO_EFFECTIVE_URL, &eff_url);
     if(eff_url) {
-        char *href = util_url_path(eff_url);
+        const char *href = util_url_path(eff_url);
         if(strcmp(href, resource->href)) {
             dav_session_free(sn, resource->href);
             resource->href = dav_session_strdup(sn, href);
@@ -278,17 +277,17 @@
     }
 }
 
-DavResource* dav_get(DavSession *sn, char *path, char *properties) {  
+DavResource* dav_get(DavSession *sn, char *path, const char *properties) {  
     CURL *handle = sn->handle;
     DavResource *resource = dav_resource_new(sn, path);
     util_set_url(sn, dav_resource_get_href(resource));
     
-    UcxList *proplist = NULL;
+    CxList *proplist = NULL;
     if(properties) {
-        proplist = parse_properties_string(sn->context, sstr(properties));
+        proplist = parse_properties_string(sn->context, cx_str(properties));
     }
-    UcxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0);
-    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *rqbuf = create_propfind_request(sn, proplist, "propfind", 0);
+    CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     //fwrite(rqbuf->space, 1, rqbuf->size, stdout);
     //printf("\n");
@@ -310,30 +309,30 @@
         resource = NULL;
     }
     
-    ucx_buffer_free(rqbuf);
-    ucx_buffer_free(rpbuf);
-    while(proplist) {
-        DavProperty *p = proplist->data;
-        free(p->name);
-        free(p);
-        UcxList *next = proplist->next;
-        free(proplist);
-        proplist = next;
+    cxBufferFree(rqbuf);
+    cxBufferFree(rpbuf);
+    
+    if(proplist) {
+        CxIterator i = cxListIterator(proplist);
+        cx_foreach(DavProperty*, p, i) {
+            free(p->name);
+        }
+        cxListDestroy(proplist);
     }
     
     return resource;
 }
 
 
-int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf) {
+int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf) {
     // clean resource properties
     DavResourceData *data = root->data;
-    ucx_map_clear(data->properties); // TODO: free existing content
+    cxMapClear(data->properties); // TODO: free existing content
     
     CURL *handle = sn->handle;
     util_set_url(sn, dav_resource_get_href(root));
      
-    UcxBuffer *rpbuf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *rpbuf = cxBufferCreate(NULL, 4096, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     DavResource *resource = root;
     CURLcode ret = do_propfind_request(sn, rqbuf, rpbuf);
     long status = 0;
@@ -350,41 +349,40 @@
         dav_session_set_error(sn, ret, status);
         error = 1;
     }
-    ucx_buffer_free(rpbuf);
+    cxBufferFree(rpbuf);
     return error;
 }
 
-UcxList* parse_properties_string(DavContext *context, sstr_t str) {
-    UcxList *proplist = NULL;
-    ssize_t nprops = 0;
-    sstr_t *props = sstrsplit(str, S(","), &nprops);
-    for(int i=0;i<nprops;i++) {
-        sstr_t s = props[i];
-        sstr_t nsname = sstrchr(s, ':');
+CxList* parse_properties_string(DavContext *context, cxstring str) {
+    CxList *proplist = cxLinkedListCreateSimple(sizeof(DavProperty));
+    
+    CxStrtokCtx tok = cx_strtok(str, cx_str(","), INT_MAX);
+    cxstring s;
+    while(cx_strtok_next(&tok, &s)) {
+        cxstring nsname = cx_strchr(s, ':');
         if(nsname.length > 0) {
-            sstr_t nspre = sstrsubsl(s, 0, nsname.ptr - s.ptr);
+            cxstring nspre = cx_strsubsl(s, 0, nsname.ptr - s.ptr);
             nsname.ptr++;
             nsname.length--;
             
-            DavProperty *dp = malloc(sizeof(DavProperty));
-            sstr_t pre = sstrtrim(nspre);
-            dp->ns = dav_get_namespace_s(context, pre);
-            dp->name = sstrdup(nsname).ptr;
-            if(dp->ns && dp->name) {
-                proplist = ucx_list_append(proplist, dp);
+            DavProperty dp;
+            cxstring pre = cx_strtrim(nspre);
+            dp.ns = dav_get_namespace_s(context, pre);
+            dp.name = cx_strdup(nsname).ptr;
+            dp.value = NULL;
+            if(dp.ns && dp.name) {
+                cxListAdd(proplist, &dp);
             } else {
-                free(dp->name);
-                free(dp);
+                free(dp.name);
             }
         }
-        free(s.ptr);
     }
-    free(props);
+    
     return proplist;
 }
 
 DavResource* dav_query(DavSession *sn, char *query, ...) {
-    DavQLStatement *stmt = dav_parse_statement(sstr(query));
+    DavQLStatement *stmt = dav_parse_statement(cx_str(query));
     if(!stmt) {
         sn->error = DAV_ERROR;
         return NULL;
--- a/libidav/webdav.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/webdav.h	Fri Apr 21 21:25:32 2023 +0200
@@ -30,10 +30,11 @@
 #define	WEBDAV_H
 
 #include <inttypes.h>
-#include <ucx/map.h>
-#include <ucx/mempool.h>
-#include <ucx/list.h>
-#include <ucx/buffer.h>
+#include <cx/map.h>
+#include <cx/mempool.h>
+#include <cx/linked_list.h>
+#include <cx/string.h>
+#include <cx/buffer.h>
 #include <curl/curl.h>
 #include <libxml/tree.h>
 
@@ -148,8 +149,8 @@
     DavContext    *context;
     CURL          *handle;
     char          *base_url;
-    UcxMempool    *mp;
-    UcxMap        *pathcache;
+    CxMempool     *mp;
+    CxMap         *pathcache;
     DavKey        *key;
     void          *locks;
     uint32_t      flags;
@@ -165,10 +166,10 @@
 };
 
 struct DavContext {
-    UcxMap   *namespaces;
-    UcxMap   *namespaceinfo;
-    UcxMap   *keys;
-    UcxList  *sessions;
+    CxMap    *namespaces;
+    CxMap    *namespaceinfo;
+    CxMap    *keys;
+    CxList   *sessions;
     DavProxy *http_proxy;
     DavProxy *https_proxy;
 };
@@ -238,10 +239,11 @@
 void dav_context_destroy(DavContext *ctx);
 
 void dav_context_add_key(DavContext *context, DavKey *key);
-DavKey* dav_context_get_key(DavContext *context, char *name);
+DavKey* dav_context_get_key(DavContext *context, const char *name);
 
 int dav_add_namespace(DavContext *context, const char *prefix, const char *ns);
 DavNamespace* dav_get_namespace(DavContext *context, const char *prefix);
+DavNamespace* dav_get_namespace_s(DavContext *context, cxstring prefix);
 
 int dav_enable_namespace_encryption(DavContext *context, const char *ns, DavBool encrypt);
 int dav_namespace_is_encrypted(DavContext *context, const char *ns);
@@ -268,13 +270,13 @@
 char* dav_session_strdup(DavSession *sn, const char *str);
 
 void dav_set_effective_href(DavSession *sn, DavResource *resource);
-DavResource* dav_get(DavSession *sn, char *path, char *properties);
+DavResource* dav_get(DavSession *sn, char *path, const char *properties);
 
-UcxList* parse_properties_string(DavContext *context, sstr_t str);
+CxList* parse_properties_string(DavContext *context, cxstring str);
 
 DavResource* dav_query(DavSession *sn, char *query, ...);
 
-sstr_t dav_property_key(const char *ns, const char *name);
+cxmutstr dav_property_key(const char *ns, const char *name);
 void dav_get_property_namespace_str(
         DavContext *ctx,
         char *prefixed_name,
@@ -287,9 +289,9 @@
 
 /* ------------------------ resource functions ------------------------ */
 
-DavResource* dav_resource_new(DavSession *sn, char *path);
-DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, char *name);
-DavResource* dav_resource_new_href(DavSession *sn, char *href);
+DavResource* dav_resource_new(DavSession *sn, const char *path);
+DavResource* dav_resource_new_child(DavSession *sn, DavResource *parent, const char *name);
+DavResource* dav_resource_new_href(DavSession *sn, const char *href);
 
 void dav_resource_free(DavResource *res);
 void dav_resource_free_all(DavResource *res);
@@ -347,7 +349,7 @@
 int dav_outputstream_close(DavOutputStream *out);
 
 // private
-int dav_propfind(DavSession *sn, DavResource *root, UcxBuffer *rqbuf);
+int dav_propfind(DavSession *sn, DavResource *root, CxBuffer *rqbuf);
 
 
 /* --------------------------- DeltaV ---------------------------- */
--- a/libidav/xml.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/xml.c	Fri Apr 21 21:25:32 2023 +0200
@@ -30,7 +30,8 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <ucx/utils.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
 
 #include "xml.h"
 
@@ -58,23 +59,27 @@
         return NULL;
     }
     
-    UcxMempool *mp = sn->mp;
+    const CxAllocator *a = sn->mp->allocator;
     
-    ConvXmlElm *ce = malloc(sizeof(ConvXmlElm));
-    ce->node = node;
-    ce->parent = NULL;
-    UcxList *stack = ucx_list_prepend(NULL, ce);
+    ConvXmlElm ce;
+    ce.node = node;
+    ce.parent = NULL;
+    CxList *stack = cxLinkedListCreate(cxDefaultAllocator, NULL, sizeof(ConvXmlElm));
+    if(!stack) {
+        return NULL;
+    }
+    cxListInsert(stack, 0, &ce);
     
     DavXmlNode *ret = NULL;
     
-    while(stack) {
-        ConvXmlElm *c = stack->data;
-        stack = ucx_list_remove(stack, stack);
+    while(stack->size > 0) {
+        ConvXmlElm *c = cxListAt(stack, 0);
+        cxListRemove(stack, 0);
         
         xmlNode *n = c->node;
         DavXmlNode *prev = NULL;
         while(n) {
-            DavXmlNode *newxn = ucx_mempool_calloc(mp, 1, sizeof(DavXmlNode));
+            DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode));
             if(!ret) {
                 ret = newxn;
             }
@@ -98,7 +103,7 @@
                 DavXmlAttr *newattr = NULL;
                 DavXmlAttr *newattr_last = NULL;
                 while(attr) {
-                    DavXmlAttr *na = ucx_mempool_calloc(mp, 1, sizeof(DavXmlAttr));
+                    DavXmlAttr *na = cxCalloc(a, 1, sizeof(DavXmlAttr));
                     na->name = dav_session_strdup(sn, (char*)attr->name);
                     if(attr->children && attr->children->type == XML_TEXT_NODE) {
                         na->value = dav_session_strdup(sn, (char*)attr->children->content);
@@ -115,13 +120,13 @@
                 newxn->attributes = newattr;
                 
                 if(n->children) {
-                    ConvXmlElm *convc = malloc(sizeof(ConvXmlElm));
-                    convc->node = n->children;
-                    convc->parent = newxn;
-                    stack = ucx_list_prepend(stack, convc);
+                    ConvXmlElm convc;
+                    convc.node = n->children;
+                    convc.parent = newxn;
+                    cxListInsert(stack, 0, &convc);
                 }
             } else if(newxn->type == DAV_XML_TEXT) {
-                sstr_t content = sstrdup_a(mp->allocator, sstr((char*)n->content));
+                cxmutstr content = cx_strdup_a(a, cx_str((char*)n->content));
                 newxn->content = content.ptr;
                 newxn->contentlength = content.length;
             }
@@ -129,8 +134,6 @@
             prev = newxn;
             n = n->next;
         }
-        
-        free(c);
     }
     
     return ret;
@@ -161,19 +164,21 @@
     }
 }
 
-void dav_print_node(void *stream, write_func writef, UcxMap *nsmap, DavXmlNode *node) {
+void dav_print_node(void *stream, cx_write_func writef, CxMap *nsmap, DavXmlNode *node) {
     while(node) {
         if(node->type == DAV_XML_ELEMENT) {
             char *tagend = node->children ? ">" : " />";          
             char *prefix = NULL;
+            char *prefix_fr = NULL;
             if(node->namespace) {
-                prefix = ucx_map_cstr_get(nsmap, node->namespace);
+                prefix = cxMapGet(nsmap, cx_hash_key_str(node->namespace));
                 if(!prefix) {
-                    sstr_t newpre = ucx_sprintf("x%d", (int)nsmap->count+1);
+                    cxmutstr newpre = cx_asprintf("x%d", (int)nsmap->size+1);
                     // TODO: fix namespace declaration
                     //ucx_map_cstr_put(nsmap, node->namespace, newpre.ptr);
                     prefix = newpre.ptr;
-                    ucx_fprintf(
+                    prefix_fr = prefix;
+                    cx_fprintf(
                             stream,
                             writef,
                             "<%s:%s xmlns:%s=\"%s\"",
@@ -182,15 +187,15 @@
                             prefix,
                             node->namespace);
                 } else {
-                    ucx_fprintf(stream, writef, "<%s:%s", prefix, node->name);
+                    cx_fprintf(stream, writef, "<%s:%s", prefix, node->name);
                 }
             } else {
-                ucx_fprintf(stream, writef, "<%s", node->name);
+                cx_fprintf(stream, writef, "<%s", node->name);
             }
             
             DavXmlAttr *attr = node->attributes;
             while(attr) {
-                ucx_fprintf(stream, writef, " %s=\"%s\"", attr->name, attr->value);
+                cx_fprintf(stream, writef, " %s=\"%s\"", attr->name, attr->value);
                 attr = attr->next;
             }
             writef(tagend, 1, strlen(tagend), stream); // end xml tag
@@ -198,11 +203,15 @@
             if(node->children) {
                 dav_print_node(stream, writef, nsmap, node->children);
                 if(prefix) {
-                    ucx_fprintf(stream, writef, "</%s:%s>", prefix, node->name);
+                    cx_fprintf(stream, writef, "</%s:%s>", prefix, node->name);
                 } else {
-                    ucx_fprintf(stream, writef, "</%s>", node->name);
+                    cx_fprintf(stream, writef, "</%s>", node->name);
                 }
             }
+            
+            if(prefix_fr) {
+                free(prefix_fr);
+            }
         } else if(node->type == DAV_XML_TEXT) {
             writef(node->content, 1, node->contentlength, stream);
         }
@@ -241,33 +250,33 @@
 }
 
 DavXmlNode* dav_text_node(DavSession *sn, const char *text) {
-    UcxMempool *mp = sn->mp; 
-    DavXmlNode *newxn = ucx_mempool_calloc(mp, 1, sizeof(DavXmlNode));
+    const CxAllocator *a = sn->mp->allocator; 
+    DavXmlNode *newxn = cxCalloc(a, 1, sizeof(DavXmlNode));
     newxn->type = DAV_XML_TEXT;
-    sstr_t content = scstrdup_a(mp->allocator, scstr(text));
+    cxmutstr content = cx_strdup_a(a, cx_str(text));
     newxn->content = content.ptr;
     newxn->contentlength = content.length;
     return newxn;
 }
 
 DavXmlNode* dav_text_element(DavSession *sn, const char *ns, const char *name, const char *text) {
-    UcxMempool *mp = sn->mp; 
-    DavXmlNode *newelm = ucx_mempool_calloc(mp, 1, sizeof(DavXmlNode));
+    const CxAllocator *a = sn->mp->allocator; 
+    DavXmlNode *newelm = cxCalloc(a, 1, sizeof(DavXmlNode));
     newelm->type = DAV_XML_ELEMENT;
-    newelm->namespace = scstrdup_a(mp->allocator, scstr(ns)).ptr;
-    newelm->name = scstrdup_a(mp->allocator, scstr(name)).ptr;
+    newelm->namespace = cx_strdup_a(a, cx_str(ns)).ptr;
+    newelm->name = cx_strdup_a(a, cx_str(name)).ptr;
     newelm->children = dav_text_node(sn, text);
     return newelm;
 }
 
-static void dav_free_xml_node_a(UcxAllocator *a, DavXmlNode *node) {
-    if(node->name) alfree(a, node->name);
-    if(node->namespace) alfree(a, node->namespace);
-    if(node->content) alfree(a, node->content);
+static void dav_free_xml_node_a(const CxAllocator *a, DavXmlNode *node) {
+    if(node->name) cxFree(a, node->name);
+    if(node->namespace) cxFree(a, node->namespace);
+    if(node->content) cxFree(a, node->content);
     DavXmlAttr *attr = node->attributes;
     while(attr) {
-        if(attr->name) alfree(a, attr->name);
-        if(attr->value) alfree(a, attr->value);
+        if(attr->name) cxFree(a, attr->name);
+        if(attr->value) cxFree(a, attr->value);
         attr = attr->next;
     }
     DavXmlNode *children = node->children;
@@ -276,7 +285,7 @@
         dav_free_xml_node_a(a, children);
         children = next_ch;
     }
-    alfree(a, node);
+    cxFree(a, node);
 }
 
 void dav_free_xml_node_sn(DavSession *sn, DavXmlNode *node) {
@@ -284,7 +293,7 @@
 }
 
 void dav_free_xml_node(DavXmlNode *node) {
-    dav_free_xml_node_a(ucx_default_allocator(), node);
+    dav_free_xml_node_a(cxDefaultAllocator, node);
 }
 
 DavXmlAttr* dav_copy_xml_attr(DavXmlAttr *attr) {
@@ -362,7 +371,7 @@
 DavXmlNode* dav_xml_createtextnode(const char *text) {
     DavXmlNode *node = calloc(1, sizeof(DavXmlNode));
     node->type = DAV_XML_TEXT;
-    sstr_t content = sstrdup(sstr((char*)text));
+    cxmutstr content = cx_strdup(cx_str((char*)text));
     node->content = content.ptr;
     node->contentlength = content.length;
     return node;
--- a/libidav/xml.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/libidav/xml.h	Fri Apr 21 21:25:32 2023 +0200
@@ -38,7 +38,7 @@
 
 void dav_print_xml(DavXmlNode *node);
 
-void dav_print_node(void *stream, write_func writef, UcxMap *nsmap, DavXmlNode *node);
+void dav_print_node(void *stream, cx_write_func writef, CxMap *nsmap, DavXmlNode *node);
 
 
 #ifdef __cplusplus
--- a/test/Makefile	Sun Apr 16 14:12:24 2023 +0200
+++ b/test/Makefile	Fri Apr 21 21:25:32 2023 +0200
@@ -31,6 +31,7 @@
 include ../config.mk
 
 TEST_SRC  = main.c
+TEST_SRC += test.c
 TEST_SRC += base64.c
 TEST_SRC += crypto.c
 
@@ -41,9 +42,9 @@
 
 all: $(TEST_TARGET)
 
-$(TEST_TARGET): $(TEST_OBJ) ../build/libidav$(LIB_EXT)
+$(TEST_TARGET): $(TEST_OBJ) ../build/lib/libidav$(LIB_EXT)
 	$(LD) -o $(TEST_TARGET) $(TEST_OBJ) \
-		../build/libidav$(LIB_EXT) ../build/libucx$(LIB_EXT)  \
+		../build/lib/libidav$(LIB_EXT) ../build/lib/libucx$(LIB_EXT)  \
 		$(LDFLAGS) $(DAV_LDFLAGS)
 
 ../build/test/%$(OBJ_EXT): %.c 
--- a/test/base64.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/test/base64.c	Fri Apr 21 21:25:32 2023 +0200
@@ -32,7 +32,7 @@
 
 #include "base64.h"
 
-#include <ucx/string.h>
+#include <cx/string.h>
 #include <libidav/utils.h>
 
 UCX_TEST(test_util_base64decode) {
--- a/test/base64.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/test/base64.h	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #ifndef BASE64_H
 #define BASE64_H
 
-#include <ucx/test.h>
+#include "test.h"
 
 #ifdef __cplusplus
 extern "C" {
--- a/test/crypto.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/test/crypto.c	Fri Apr 21 21:25:32 2023 +0200
@@ -33,9 +33,9 @@
 
 #include "crypto.h"
 
-#include <ucx/string.h>
-#include <ucx/utils.h>
-#include <ucx/buffer.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/buffer.h>
 #include <libidav/utils.h>
 #include <libidav/crypto.h>
 
@@ -254,11 +254,11 @@
         DavKey *key = i < 16 ? &keys256[i] : &keys128[i%16];
         
         for(int j=0;j<20;j++) {
-            UcxBuffer *content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
-            ucx_buffer_puts(content, strings[j]);
+            CxBuffer *content = cxBufferCreate(NULL, 256, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+            cxBufferPutString(content, strings[j]);
             content->pos = 0;
             
-            UcxBuffer *enc = aes_encrypt_buffer(content, key);
+            CxBuffer *enc = aes_encrypt_buffer(content, key);
             UCX_TEST_ASSERT(enc->size >= content->size + 16, "aes_encrypt_buffer failed");
             
             char *base64 = util_base64encode(enc->space, enc->size);
@@ -269,14 +269,14 @@
             UCX_TEST_ASSERT(plainlen == content->size, "aes_decrypt: wrong length");
             UCX_TEST_ASSERT(!memcmp(plain, content->space, plainlen), "aes_decrypt: wrong content");
             
-            UcxBuffer *dec = aes_decrypt_buffer(enc, key);
+            CxBuffer *dec = aes_decrypt_buffer(enc, key);
             UCX_TEST_ASSERT(dec->size == content->size, "aes_decrypt_buffer failed");
             
             UCX_TEST_ASSERT(!memcmp(content->space, dec->space, dec->size), "decrypted buffer has wrong content");
             
-            ucx_buffer_free(content);
-            ucx_buffer_free(enc);
-            ucx_buffer_free(dec);
+            cxBufferFree(content);
+            cxBufferFree(enc);
+            cxBufferFree(dec);
             free(base64);
             free(plain);
         }
@@ -286,9 +286,9 @@
 }
 
 UCX_TEST(test_crypto_stream) {
-    UcxBuffer *data = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
-    UcxBuffer *cbuf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
-    UcxBuffer *pbuf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *data = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *cbuf = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
+    CxBuffer *pbuf = cxBufferCreate(NULL, 1024, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
     
     UCX_TEST_BEGIN;
     
@@ -298,23 +298,23 @@
             data->pos = 0;
             data->size = 0;
             size_t slen = strlen(strings[j]);
-            ucx_buffer_write(strings[j], 1, slen, data);
-            ucx_buffer_seek(data, 0, SEEK_SET);
+            cxBufferWrite(strings[j], 1, slen, data);
+            cxBufferSeek(data, 0, SEEK_SET);
 
             cbuf->pos = 0;
             cbuf->size = 0;
             pbuf->pos = 0;
             pbuf->size = 0;
 
-            AESEncrypter *enc = aes_encrypter_new(key, data, (dav_read_func)ucx_buffer_read, NULL);
+            AESEncrypter *enc = aes_encrypter_new(key, data, (dav_read_func)cxBufferRead, NULL);
             char buf[1024];
             size_t r = 0;
             while((r = aes_read(buf, 1, 1024, enc)) != 0) {
-                ucx_buffer_write(buf, 1, r, cbuf);
+                cxBufferWrite(buf, 1, r, cbuf);
             }
             aes_encrypter_close(enc);
 
-            AESDecrypter *dec = aes_decrypter_new(key, pbuf, (dav_write_func)ucx_buffer_write);
+            AESDecrypter *dec = aes_decrypter_new(key, pbuf, (dav_write_func)cxBufferWrite);
             aes_write(cbuf->space, 1, cbuf->pos, dec);
             aes_decrypter_shutdown(dec);
             aes_decrypter_close(dec);
@@ -323,8 +323,8 @@
             UCX_TEST_ASSERT(!memcmp(strings[j], pbuf->space, slen), "wrong content after enc-dec");
             
             data->pos = 0;
-            UcxBuffer *enc2 = aes_encrypt_buffer(data, key);
-            UcxBuffer *dec2 = aes_decrypt_buffer(enc2, key);
+            CxBuffer *enc2 = aes_encrypt_buffer(data, key);
+            CxBuffer *dec2 = aes_decrypt_buffer(enc2, key);
             
             UCX_TEST_ASSERT(dec2->size == data->size, "dec2 has wrong size");
             UCX_TEST_ASSERT(!memcmp(strings[j], dec2->space, dec2->size), "dec2 has wrong content");
--- a/test/crypto.h	Sun Apr 16 14:12:24 2023 +0200
+++ b/test/crypto.h	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #ifndef CRYPTO_H
 #define CRYPTO_H
 
-#include <ucx/test.h>
+#include "test.h"
 
 #ifdef __cplusplus
 extern "C" {
--- a/test/main.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/test/main.c	Fri Apr 21 21:25:32 2023 +0200
@@ -29,7 +29,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <ucx/test.h>
+#include "test.h"
 
 #include "base64.h"
 #include "crypto.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,91 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 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.
+ */
+
+#include "test.h"
+
+UcxTestSuite* ucx_test_suite_new() {
+    UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite));
+    if (suite != NULL) {
+        suite->success = 0;
+        suite->failure = 0;
+        suite->tests = NULL;
+    }
+
+    return suite;
+}
+
+void ucx_test_suite_free(UcxTestSuite* suite) {
+    UcxTestList *l = suite->tests;
+    while (l != NULL) {
+        UcxTestList *e = l;
+        l = l->next;
+        free(e);
+    }
+    free(suite);
+}
+
+int ucx_test_register(UcxTestSuite* suite, UcxTest test) {
+    if (suite->tests) {
+        UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList));
+        if (newelem) {
+            newelem->test = test;
+            newelem->next = NULL;
+            
+            UcxTestList *last = suite->tests;
+            while (last->next) {
+                last = last->next;
+            }
+            last->next = newelem;
+            
+            return EXIT_SUCCESS;
+        } else {
+            return EXIT_FAILURE;
+        }
+    } else {
+        suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList));
+        if (suite->tests) {
+            suite->tests->test = test;
+            suite->tests->next = NULL;
+            
+            return EXIT_SUCCESS;
+        } else {
+            return EXIT_FAILURE;
+        }
+    }
+}
+
+void ucx_test_run(UcxTestSuite* suite, FILE* output) {
+    suite->success = 0;
+    suite->failure = 0;
+    for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) {
+        elem->test(suite, output);
+    }
+    fwrite("\nAll test completed.\n", 1, 21, output);
+    fprintf(output, "  Total:   %u\n  Success: %u\n  Failure: %u\n",
+            suite->success+suite->failure, suite->success, suite->failure);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/test.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,241 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 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: test.h
+ * 
+ * UCX Test Framework.
+ * 
+ * Usage of this test framework:
+ *
+ * **** IN HEADER FILE: ****
+ *
+ * <pre>
+ * UCX_TEST(function_name);
+ * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
+ * </pre>
+ *
+ * **** IN SOURCE FILE: ****
+ * <pre>
+ * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
+ *   // tests with UCX_TEST_ASSERT()
+ * }
+ * 
+ * UCX_TEST(function_name) {
+ *   // memory allocation and other stuff here
+ *   #UCX_TEST_BEGIN
+ *   // tests with UCX_TEST_ASSERT() and/or
+ *   // calls with UCX_TEST_CALL_SUBROUTINE() here
+ *   #UCX_TEST_END
+ *   // cleanup of memory here
+ * }
+ * </pre>
+ *
+ * <b>Note:</b> if a test fails, a longjump is performed
+ * back to the #UCX_TEST_BEGIN macro!
+ * 
+ * <b>Attention:</b> Do not call own functions within a test, that use
+ * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE().
+ * 
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ *
+ */
+
+#ifndef UCX_TEST_H
+#define	UCX_TEST_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#ifndef __FUNCTION__
+
+/**
+ * Alias for the <code>__func__</code> preprocessor macro.
+ * Some compilers use <code>__func__</code> and others use __FUNCTION__.
+ * We use __FUNCTION__ so we define it for those compilers which use
+ * <code>__func__</code>.
+ */
+#define __FUNCTION__ __func__
+#endif
+
+/** Type for the UcxTestSuite. */
+typedef struct UcxTestSuite UcxTestSuite;
+
+/** Pointer to a test function. */
+typedef void(*UcxTest)(UcxTestSuite*,FILE*);
+
+/** Type for the internal list of test cases. */
+typedef struct UcxTestList UcxTestList;
+
+/** Structure for the internal list of test cases. */
+struct UcxTestList {
+    
+    /** Test case. */
+    UcxTest test;
+    
+    /** Pointer to the next list element. */
+    UcxTestList *next;
+};
+
+/**
+ * A test suite containing multiple test cases.
+ */
+struct UcxTestSuite {
+    
+    /** The number of successful tests after the suite has been run. */
+    unsigned int success;
+    
+    /** The number of failed tests after the suite has been run. */
+    unsigned int failure;
+    
+    /**
+     * Internal list of test cases.
+     * Use ucx_test_register() to add tests to this list.
+     */
+    UcxTestList *tests;
+};
+
+/**
+ * Creates a new test suite.
+ * @return a new test suite
+ */
+UcxTestSuite* ucx_test_suite_new();
+
+/**
+ * Destroys a test suite.
+ * @param suite the test suite to destroy
+ */
+void ucx_test_suite_free(UcxTestSuite* suite);
+
+/**
+ * Registers a test function with the specified test suite.
+ * 
+ * @param suite the suite, the test function shall be added to
+ * @param test the test function to register
+ * @return <code>EXIT_SUCCESS</code> on success or
+ * <code>EXIT_FAILURE</code> on failure
+ */
+int ucx_test_register(UcxTestSuite* suite, UcxTest test);
+
+/**
+ * Runs a test suite and writes the test log to the specified stream.
+ * @param suite the test suite to run
+ * @param outstream the stream the log shall be written to
+ */
+void ucx_test_run(UcxTestSuite* suite, FILE* outstream);
+
+/**
+ * Macro for a #UcxTest function header.
+ * 
+ * Use this macro to declare and/or define a #UcxTest function.
+ * 
+ * @param name the name of the test function
+ */
+#define UCX_TEST(name) void name(UcxTestSuite* _suite_,FILE *_output_)
+
+/**
+ * Marks the begin of a test.
+ * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>after</b>
+ * #UCX_TEST_BEGIN.
+ * 
+ * @see #UCX_TEST_END
+ */
+#define UCX_TEST_BEGIN fwrite("Running ", 1, 8, _output_);\
+        fwrite(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\
+        fwrite("... ", 1, 4, _output_);\
+        jmp_buf _env_; \
+        if (!setjmp(_env_)) {
+
+/**
+ * Checks a test assertion.
+ * If the assertion is correct, the test carries on. If the assertion is not
+ * correct, the specified message (terminated by a dot and a line break) is
+ * written to the test suites output stream.
+ * @param condition the condition to check
+ * @param message the message that shall be printed out on failure
+ */
+#define UCX_TEST_ASSERT(condition,message) if (!(condition)) { \
+        fwrite(message".\n", 1, 2+strlen(message), _output_); \
+        _suite_->failure++; \
+        longjmp(_env_, 1);\
+    }
+
+/**
+ * Macro for a test subroutine function header.
+ * 
+ * Use this to declare and/or define a subroutine that can be called by using
+ * UCX_TEST_CALL_SUBROUTINE().
+ * 
+ * @param name the name of the subroutine
+ * @param ... the parameter list
+ * 
+ * @see UCX_TEST_CALL_SUBROUTINE()
+ */
+#define UCX_TEST_SUBROUTINE(name,...) void name(UcxTestSuite* _suite_,\
+        FILE *_output_, jmp_buf _env_, __VA_ARGS__)
+
+/**
+ * Macro for calling a test subroutine.
+ * 
+ * Subroutines declared with UCX_TEST_SUBROUTINE() can be called by using this
+ * macro.
+ * 
+ * <b>Note:</b> You may <b>only</b> call subroutines within a #UCX_TEST_BEGIN-
+ * #UCX_TEST_END-block.
+ * 
+ * @param name the name of the subroutine
+ * @param ... the argument list
+ * 
+ * @see UCX_TEST_SUBROUTINE()
+ */
+#define UCX_TEST_CALL_SUBROUTINE(name,...) \
+        name(_suite_,_output_,_env_,__VA_ARGS__);
+
+/**
+ * Marks the end of a test.
+ * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>before</b>
+ * #UCX_TEST_END.
+ * 
+ * @see #UCX_TEST_BEGIN
+ */
+#define UCX_TEST_END fwrite("success.\n", 1, 9, _output_); _suite_->success++;}
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_TEST_H */
+
--- a/ucx/Makefile	Sun Apr 16 14:12:24 2023 +0200
+++ b/ucx/Makefile	Fri Apr 21 21:25:32 2023 +0200
@@ -1,7 +1,7 @@
 #
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 #
-# Copyright 2016 Olaf Wintermann. All rights reserved.
+# Copyright 2013 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:
@@ -26,34 +26,35 @@
 # POSSIBILITY OF SUCH DAMAGE.
 #
 
-BUILD_ROOT = ..
-
+BUILD_ROOT = ../
 include ../config.mk
 
 # list of source files
-SRC  = utils.c
+SRC  = allocator.c
+SRC += array_list.c
+SRC += basic_mempool.c
+SRC += buffer.c
+SRC += compare.c
+SRC += hash_key.c
+SRC += hash_map.c
+SRC += linked_list.c
 SRC += list.c
-SRC += map.c
-SRC += avl.c
-SRC += properties.c
-SRC += mempool.c
+SRC += printf.c
 SRC += string.c
-SRC += test.c
-SRC += allocator.c
-SRC += logging.c
-SRC += buffer.c
-SRC += stack.c
-SRC += ucx.c
+SRC += utils.c
+
+OBJ   = $(SRC:%.c=../build/ucx/%$(OBJ_EXT))
+
+UCX_LIB = ../build/lib/libucx$(LIB_EXT)
 
-OBJ = $(SRC:%.c=../build/ucx/%$(OBJ_EXT))
+all: ../build/ucx $(UCX_LIB)
 
-all: ../build/ucx ../build/libucx$(LIB_EXT)
+$(UCX_LIB): $(OBJ)
+	$(AR) $(ARFLAGS) $(UCX_LIB) $(OBJ)
 
-../build/libucx$(LIB_EXT): $(OBJ)
-	$(AR) $(ARFLAGS) $(AOFLAGS)../build/libucx$(LIB_EXT) $(OBJ)
+../build/ucx:
+	mkdir -p ../build/ucx
 
 ../build/ucx/%$(OBJ_EXT): %.c
-	$(CC) $(CFLAGS) -c -o $@ $<
+	$(CC) $(CFLAGS) -o $@ -c $<
 
-../build/ucx:
-	$(MKDIR) $(MKDIRFLAGS) ../build/ucx
--- a/ucx/README	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-UCX is a library for common data structures, algorithms and string functions.
-
-More informations at: https://develop.uap-core.de/ucx/
-
--- a/ucx/allocator.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/ucx/allocator.c	Fri Apr 21 21:25:32 2023 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * 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:
@@ -26,35 +26,97 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx/allocator.h"
-
-#include <stdlib.h>
+#include "cx/allocator.h"
 
-static UcxAllocator default_allocator = {
-    NULL,
-    ucx_default_malloc,
-    ucx_default_calloc,
-    ucx_default_realloc,
-    ucx_default_free
-};
-
-UcxAllocator *ucx_default_allocator() {
-    UcxAllocator *allocator = &default_allocator;
-    return allocator;
-}
-
-void *ucx_default_malloc(void *ignore, size_t n) {
+__attribute__((__malloc__, __alloc_size__(2)))
+static void *cx_malloc_stdlib(
+        __attribute__((__unused__)) void *d,
+        size_t n
+) {
     return malloc(n);
 }
 
-void *ucx_default_calloc(void *ignore, size_t n, size_t size) {
-    return calloc(n, size);
+__attribute__((__warn_unused_result__, __alloc_size__(3)))
+static void *cx_realloc_stdlib(
+        __attribute__((__unused__)) void *d,
+        void *mem,
+        size_t n
+) {
+    return realloc(mem, n);
+}
+
+__attribute__((__malloc__, __alloc_size__(2, 3)))
+static void *cx_calloc_stdlib(
+        __attribute__((__unused__)) void *d,
+        size_t nelem,
+        size_t n
+) {
+    return calloc(nelem, n);
+}
+
+__attribute__((__nonnull__))
+static void cx_free_stdlib(
+        __attribute__((__unused__)) void *d,
+        void *mem
+) {
+    free(mem);
 }
 
-void *ucx_default_realloc(void *ignore, void *data, size_t n) {
-    return realloc(data, n);
+static cx_allocator_class cx_default_allocator_class = {
+        cx_malloc_stdlib,
+        cx_realloc_stdlib,
+        cx_calloc_stdlib,
+        cx_free_stdlib
+};
+
+struct cx_allocator_s cx_default_allocator = {
+        &cx_default_allocator_class,
+        NULL
+};
+CxAllocator *cxDefaultAllocator = &cx_default_allocator;
+
+// IMPLEMENTATION OF HIGH LEVEL API
+
+void *cxMalloc(
+        CxAllocator const *allocator,
+        size_t n
+) {
+    return allocator->cl->malloc(allocator->data, n);
 }
 
-void ucx_default_free(void *ignore, void *data) {
-    free(data);
+void *cxRealloc(
+        CxAllocator const *allocator,
+        void *mem,
+        size_t n
+) {
+    return allocator->cl->realloc(allocator->data, mem, n);
 }
+
+int cxReallocate(
+        CxAllocator const *allocator,
+        void **mem,
+        size_t n
+) {
+    void *nmem = allocator->cl->realloc(allocator->data, *mem, n);
+    if (nmem == NULL) {
+        return 1;
+    } else {
+        *mem = nmem;
+        return 0;
+    }
+}
+
+void *cxCalloc(
+        CxAllocator const *allocator,
+        size_t nelem,
+        size_t n
+) {
+    return allocator->cl->calloc(allocator->data, nelem, n);
+}
+
+void cxFree(
+        CxAllocator const *allocator,
+        void *mem
+) {
+    allocator->cl->free(allocator->data, mem);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/array_list.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,529 @@
+/*
+ * 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.
+ */
+
+#include "cx/array_list.h"
+#include <assert.h>
+#include <string.h>
+
+// LOW LEVEL ARRAY LIST FUNCTIONS
+
+enum cx_array_copy_result cx_array_copy(
+        void **target,
+        size_t *size,
+        size_t *capacity,
+        size_t index,
+        void const *src,
+        size_t elem_size,
+        size_t elem_count,
+        struct cx_array_reallocator_s *reallocator
+) {
+    // assert pointers
+    assert(target != NULL);
+    assert(size != NULL);
+    assert(src != NULL);
+
+    // determine capacity
+    size_t cap = capacity == NULL ? *size : *capacity;
+
+    // check if resize is required
+    size_t minsize = index + elem_count;
+    size_t newsize = *size < minsize ? minsize : *size;
+    bool needrealloc = newsize > cap;
+
+    // reallocate if possible
+    if (needrealloc) {
+        // a reallocator and a capacity variable must be available
+        if (reallocator == NULL || capacity == NULL) {
+            return CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED;
+        }
+
+        // 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 + cap * elem_size;
+
+        // calculate new capacity (next number divisible by 16)
+        cap = newsize - (newsize % 16) + 16;
+        assert(cap > newsize);
+
+        // perform reallocation
+        void *newmem = reallocator->realloc(
+                *target, cap, elem_size, reallocator
+        );
+        if (newmem == NULL) {
+            return CX_ARRAY_COPY_REALLOC_FAILED;
+        }
+
+        // repair src pointer, if necessary
+        if (repairsrc) {
+            src = ((char *) newmem) + (srcaddr - targetaddr);
+        }
+
+        // store new pointer and capacity
+        *target = newmem;
+        *capacity = cap;
+    }
+
+    // determine target pointer
+    char *start = *target;
+    start += index * elem_size;
+
+    // copy elements and set new size
+    memmove(start, src, elem_count * elem_size);
+    *size = newsize;
+
+    // return successfully
+    return CX_ARRAY_COPY_SUCCESS;
+}
+
+#ifndef CX_ARRAY_SWAP_SBO_SIZE
+#define CX_ARRAY_SWAP_SBO_SIZE 512
+#endif
+
+void cx_array_swap(
+        void *arr,
+        size_t elem_size,
+        size_t idx1,
+        size_t idx2
+) {
+    assert(arr != NULL);
+
+    // short circuit
+    if (idx1 == idx2) return;
+
+    char sbo_mem[CX_ARRAY_SWAP_SBO_SIZE];
+    void *tmp;
+
+    // decide if we can use the local buffer
+    if (elem_size > CX_ARRAY_SWAP_SBO_SIZE) {
+        tmp = malloc(elem_size);
+        // we don't want to enforce error handling
+        if (tmp == NULL) abort();
+    } else {
+        tmp = sbo_mem;
+    }
+
+    // calculate memory locations
+    char *left = arr, *right = arr;
+    left += idx1 * elem_size;
+    right += idx2 * elem_size;
+
+    // three-way swap
+    memcpy(tmp, left, elem_size);
+    memcpy(left, right, elem_size);
+    memcpy(right, tmp, elem_size);
+
+    // free dynamic memory, if it was needed
+    if (tmp != sbo_mem) {
+        free(tmp);
+    }
+}
+
+// HIGH LEVEL ARRAY LIST FUNCTIONS
+
+typedef struct {
+    struct cx_list_s base;
+    void *data;
+    size_t capacity;
+    struct cx_array_reallocator_s reallocator;
+} cx_array_list;
+
+static void *cx_arl_realloc(
+        void *array,
+        size_t capacity,
+        size_t elem_size,
+        struct cx_array_reallocator_s *alloc
+) {
+    // retrieve the pointer to the list allocator
+    CxAllocator const *al = alloc->ptr1;
+
+    // use the list allocator to reallocate the memory
+    return cxRealloc(al, array, capacity * elem_size);
+}
+
+static void cx_arl_destructor(struct cx_list_s *list) {
+    cx_array_list *arl = (cx_array_list *) list;
+    cxFree(list->allocator, arl->data);
+}
+
+static size_t cx_arl_insert_array(
+        struct cx_list_s *list,
+        size_t index,
+        void const *array,
+        size_t n
+) {
+    // out of bounds and special case check
+    if (index > list->size || n == 0) return 0;
+
+    // get a correctly typed pointer to the list
+    cx_array_list *arl = (cx_array_list *) list;
+
+    // do we need to move some elements?
+    if (index < list->size) {
+        char const *first_to_move = (char const *) arl->data;
+        first_to_move += index * list->item_size;
+        size_t elems_to_move = list->size - index;
+        size_t start_of_moved = index + n;
+
+        if (CX_ARRAY_COPY_SUCCESS != cx_array_copy(
+                &arl->data,
+                &list->size,
+                &arl->capacity,
+                start_of_moved,
+                first_to_move,
+                list->item_size,
+                elems_to_move,
+                &arl->reallocator
+        )) {
+            // if moving existing elems is unsuccessful, abort
+            return 0;
+        }
+    }
+
+    // note that if we had to move the elements, the following operation
+    // is guaranteed to succeed, because we have the memory already allocated
+    // therefore, it is impossible to leave this function with an invalid array
+
+    // place the new elements
+    if (CX_ARRAY_COPY_SUCCESS == cx_array_copy(
+            &arl->data,
+            &list->size,
+            &arl->capacity,
+            index,
+            array,
+            list->item_size,
+            n,
+            &arl->reallocator
+    )) {
+        return n;
+    } else {
+        // array list implementation is "all or nothing"
+        return 0;
+    }
+}
+
+static int cx_arl_insert_element(
+        struct cx_list_s *list,
+        size_t index,
+        void const *element
+) {
+    return 1 != cx_arl_insert_array(list, index, element, 1);
+}
+
+static int cx_arl_insert_iter(
+        struct cx_mut_iterator_s *iter,
+        void const *elem,
+        int prepend
+) {
+    struct cx_list_s *list = iter->src_handle;
+    if (iter->index < list->size) {
+        int result = cx_arl_insert_element(
+                list,
+                iter->index + 1 - prepend,
+                elem
+        );
+        if (result == 0 && prepend != 0) {
+            iter->index++;
+            iter->elem_handle = ((char *) iter->elem_handle) + list->item_size;
+        }
+        return result;
+    } else {
+        int result = cx_arl_insert_element(list, list->size, elem);
+        iter->index = list->size;
+        return result;
+    }
+}
+
+static int cx_arl_remove(
+        struct cx_list_s *list,
+        size_t index
+) {
+    cx_array_list *arl = (cx_array_list *) list;
+
+    // out-of-bounds check
+    if (index >= list->size) {
+        return 1;
+    }
+
+    // content destruction
+    cx_invoke_destructor(list, ((char *) arl->data) + index * list->item_size);
+
+    // short-circuit removal of last element
+    if (index == list->size - 1) {
+        list->size--;
+        return 0;
+    }
+
+    // just move the elements starting at index to the left
+    int result = cx_array_copy(
+            &arl->data,
+            &list->size,
+            &arl->capacity,
+            index,
+            ((char *) arl->data) + (index + 1) * list->item_size,
+            list->item_size,
+            list->size - index - 1,
+            &arl->reallocator
+    );
+    if (result == 0) {
+        // decrease the size
+        list->size--;
+    }
+    return result;
+}
+
+static void cx_arl_clear(struct cx_list_s *list) {
+    if (list->size == 0) return;
+
+    cx_array_list *arl = (cx_array_list *) list;
+    char *ptr = arl->data;
+
+    if (list->simple_destructor) {
+        for (size_t i = 0; i < list->size; i++) {
+            cx_invoke_simple_destructor(list, ptr);
+            ptr += list->item_size;
+        }
+    }
+    if (list->advanced_destructor) {
+        for (size_t i = 0; i < list->size; i++) {
+            cx_invoke_advanced_destructor(list, ptr);
+            ptr += list->item_size;
+        }
+    }
+
+    memset(arl->data, 0, list->size * list->item_size);
+    list->size = 0;
+}
+
+static int cx_arl_swap(
+        struct cx_list_s *list,
+        size_t i,
+        size_t j
+) {
+    if (i >= list->size || j >= list->size) return 1;
+    cx_array_list *arl = (cx_array_list *) list;
+    cx_array_swap(arl->data, list->item_size, i, j);
+    return 0;
+}
+
+static void *cx_arl_at(
+        struct cx_list_s const *list,
+        size_t index
+) {
+    if (index < list->size) {
+        cx_array_list const *arl = (cx_array_list const *) list;
+        char *space = arl->data;
+        return space + index * list->item_size;
+    } else {
+        return NULL;
+    }
+}
+
+static size_t cx_arl_find(
+        struct cx_list_s const *list,
+        void const *elem
+) {
+    assert(list->cmpfunc != NULL);
+    char *cur = ((cx_array_list const *) list)->data;
+
+    for (size_t i = 0; i < list->size; i++) {
+        if (0 == list->cmpfunc(elem, cur)) {
+            return i;
+        }
+        cur += list->item_size;
+    }
+
+    return list->size;
+}
+
+static void cx_arl_sort(struct cx_list_s *list) {
+    assert(list->cmpfunc != NULL);
+    qsort(((cx_array_list *) list)->data,
+          list->size,
+          list->item_size,
+          list->cmpfunc
+    );
+}
+
+static int cx_arl_compare(
+        struct cx_list_s const *list,
+        struct cx_list_s const *other
+) {
+    assert(list->cmpfunc != NULL);
+    if (list->size == other->size) {
+        char const *left = ((cx_array_list const *) list)->data;
+        char const *right = ((cx_array_list const *) other)->data;
+        for (size_t i = 0; i < list->size; i++) {
+            int d = list->cmpfunc(left, right);
+            if (d != 0) {
+                return d;
+            }
+            left += list->item_size;
+            right += other->item_size;
+        }
+        return 0;
+    } else {
+        return list->size < other->size ? -1 : 1;
+    }
+}
+
+static void cx_arl_reverse(struct cx_list_s *list) {
+    if (list->size < 2) return;
+    void *data = ((cx_array_list const *) list)->data;
+    size_t half = list->size / 2;
+    for (size_t i = 0; i < half; i++) {
+        cx_array_swap(data, list->item_size, i, list->size - 1 - i);
+    }
+}
+
+static bool cx_arl_iter_valid(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    struct cx_list_s const *list = iter->src_handle;
+    return iter->index < list->size;
+}
+
+static void *cx_arl_iter_current(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    return iter->elem_handle;
+}
+
+static void cx_arl_iter_next(void *it) {
+    struct cx_iterator_base_s *itbase = it;
+    if (itbase->remove) {
+        struct cx_mut_iterator_s *iter = it;
+        itbase->remove = false;
+        cx_arl_remove(iter->src_handle, iter->index);
+    } else {
+        struct cx_iterator_s *iter = it;
+        iter->index++;
+        iter->elem_handle =
+                ((char *) iter->elem_handle)
+                + ((struct cx_list_s const *) iter->src_handle)->item_size;
+    }
+}
+
+static void cx_arl_iter_prev(void *it) {
+    struct cx_iterator_base_s *itbase = it;
+    struct cx_mut_iterator_s *iter = it;
+    cx_array_list *const list = iter->src_handle;
+    if (itbase->remove) {
+        itbase->remove = false;
+        cx_arl_remove(iter->src_handle, iter->index);
+    }
+    iter->index--;
+    if (iter->index < list->base.size) {
+        iter->elem_handle = ((char *) list->data)
+                            + iter->index * list->base.item_size;
+    }
+}
+
+static bool cx_arl_iter_flag_rm(void *it) {
+    struct cx_iterator_base_s *iter = it;
+    if (iter->mutating) {
+        iter->remove = true;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static struct cx_iterator_s cx_arl_iterator(
+        struct cx_list_s const *list,
+        size_t index,
+        bool backwards
+) {
+    struct cx_iterator_s iter;
+
+    iter.index = index;
+    iter.src_handle = list;
+    iter.elem_handle = cx_arl_at(list, index);
+    iter.base.valid = cx_arl_iter_valid;
+    iter.base.current = cx_arl_iter_current;
+    iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
+    iter.base.flag_removal = cx_arl_iter_flag_rm;
+    iter.base.remove = false;
+    iter.base.mutating = false;
+
+    return iter;
+}
+
+static cx_list_class cx_array_list_class = {
+        cx_arl_destructor,
+        cx_arl_insert_element,
+        cx_arl_insert_array,
+        cx_arl_insert_iter,
+        cx_arl_remove,
+        cx_arl_clear,
+        cx_arl_swap,
+        cx_arl_at,
+        cx_arl_find,
+        cx_arl_sort,
+        cx_arl_compare,
+        cx_arl_reverse,
+        cx_arl_iterator,
+};
+
+CxList *cxArrayListCreate(
+        CxAllocator const *allocator,
+        cx_compare_func comparator,
+        size_t item_size,
+        size_t initial_capacity
+) {
+    if (allocator == NULL) {
+        allocator = cxDefaultAllocator;
+    }
+
+    cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list));
+    if (list == NULL) return NULL;
+
+    list->base.cl = &cx_array_list_class;
+    list->base.allocator = allocator;
+    list->base.cmpfunc = comparator;
+    list->capacity = initial_capacity;
+
+    if (item_size > 0) {
+        list->base.item_size = item_size;
+    } else {
+        item_size = sizeof(void *);
+        cxListStorePointers((CxList *) list);
+    }
+
+    // allocate the array after the real item_size is known
+    list->data = cxCalloc(allocator, initial_capacity, item_size);
+    if (list->data == NULL) {
+        cxFree(allocator, list);
+        return NULL;
+    }
+
+    // configure the reallocator
+    list->reallocator.realloc = cx_arl_realloc;
+    list->reallocator.ptr1 = (void *) allocator;
+
+    return (CxList *) list;
+}
--- a/ucx/avl.c	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,373 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-
-#include "ucx/avl.h"
-
-#include <limits.h>
-
-#define ptrcast(ptr) ((void*)(ptr))
-#define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree))
-#define alloc_node(al) (UcxAVLNode*) almalloc((al), sizeof(UcxAVLNode))
-
-static void ucx_avl_connect(UcxAVLTree *tree,
-        UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) {
-    if (child) {
-        child->parent = node;
-    }
-    // if child is NULL, nullkey decides if left or right pointer is cleared
-    if (tree->cmpfunc(
-        ptrcast(child ? child->key : nullkey),
-        ptrcast(node->key), tree->userdata) > 0) {
-      node->right = child;
-    } else {
-      node->left = child;
-    }
-    size_t lh = node->left ? node->left->height : 0;
-    size_t rh = node->right ? node->right->height : 0;
-    node->height = 1 + (lh > rh ? lh : rh);
-}
-
-#define avlheight(node) ((node) ? (node)->height : 0)
-
-static UcxAVLNode* avl_rotright(UcxAVLTree *tree, UcxAVLNode *l0) {
-    UcxAVLNode *p = l0->parent;
-    UcxAVLNode *l1 = l0->left;
-    if (p) {
-        ucx_avl_connect(tree, p, l1, 0);
-    } else {
-        l1->parent = NULL;
-    }
-    ucx_avl_connect(tree, l0, l1->right, l1->key);
-    ucx_avl_connect(tree, l1, l0, 0);
-    return l1;
-}
-
-static UcxAVLNode* avl_rotleft(UcxAVLTree *tree, UcxAVLNode *l0) {
-    UcxAVLNode *p = l0->parent;
-    UcxAVLNode *l1 = l0->right;
-    if (p) {
-        ucx_avl_connect(tree, p, l1, 0);
-    } else {
-        l1->parent = NULL;
-    }
-    ucx_avl_connect(tree, l0, l1->left, l1->key);
-    ucx_avl_connect(tree, l1, l0, 0);
-    return l1;
-}
-
-static void ucx_avl_balance(UcxAVLTree *tree, UcxAVLNode *n) {
-    int lh = avlheight(n->left);
-    int rh = avlheight(n->right);
-    n->height = 1 + (lh > rh ? lh : rh);
-    
-    if (lh - rh == 2) {
-      UcxAVLNode *c = n->left;
-      if (avlheight(c->right) - avlheight(c->left) == 1) {
-        avl_rotleft(tree, c);
-      }
-      n = avl_rotright(tree, n);
-    } else if (rh - lh == 2) {  
-      UcxAVLNode *c = n->right;
-      if (avlheight(c->left) - avlheight(c->right) == 1) {
-        avl_rotright(tree, c);
-      }
-      n = avl_rotleft(tree, n);
-    }
-
-    if (n->parent) {
-      ucx_avl_balance(tree, n->parent);
-    } else {
-      tree->root = n;
-    }
-}
-
-UcxAVLTree *ucx_avl_new(cmp_func cmpfunc) {
-    return ucx_avl_new_a(cmpfunc, ucx_default_allocator());
-}
-
-UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) {
-    UcxAVLTree* tree = alloc_tree(allocator);
-    if (tree) {
-        tree->allocator = allocator;
-        tree->cmpfunc = cmpfunc;
-        tree->root = NULL;
-        tree->userdata = NULL;
-    }
-    
-    return tree;
-}
-
-static void ucx_avl_free_node(UcxAllocator *al, UcxAVLNode *node) {
-    if (node) {
-        ucx_avl_free_node(al, node->left);
-        ucx_avl_free_node(al, node->right);
-        alfree(al, node);
-    }
-}
-
-void ucx_avl_free(UcxAVLTree *tree) {
-    UcxAllocator *al = tree->allocator;
-    ucx_avl_free_node(al, tree->root);
-    alfree(al, tree);
-}
-
-static void ucx_avl_free_content_node(UcxAllocator *al, UcxAVLNode *node,
-        ucx_destructor destr) {
-    if (node) {
-        ucx_avl_free_content_node(al, node->left, destr);
-        ucx_avl_free_content_node(al, node->right, destr);
-        if (destr) {
-            destr(node->value);
-        } else {
-            alfree(al, node->value);
-        }
-    }
-}
-
-void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr) {
-    ucx_avl_free_content_node(tree->allocator, tree->root, destr);
-}
-
-UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) {
-    UcxAVLNode *n = tree->root;
-    int cmpresult;
-    while (n && (cmpresult = tree->cmpfunc(
-            ptrcast(key), ptrcast(n->key), tree->userdata))) {
-        n = cmpresult > 0 ? n->right : n->left;
-    }
-    return n;
-}
-
-void *ucx_avl_get(UcxAVLTree *tree, intptr_t key) {
-    UcxAVLNode *n = ucx_avl_get_node(tree, key);
-    return n ? n->value : NULL;
-}
-
-UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key,
-        distance_func dfnc, int mode) {
-    UcxAVLNode *n = tree->root;
-    UcxAVLNode *closest = NULL;
-
-    intmax_t cmpresult;
-    intmax_t closest_dist;
-    closest_dist = mode == UCX_AVL_FIND_LOWER_BOUNDED ? INTMAX_MIN : INTMAX_MAX;
-    
-    while (n && (cmpresult = dfnc(
-            ptrcast(key), ptrcast(n->key), tree->userdata))) {
-        if (mode == UCX_AVL_FIND_CLOSEST) {
-            intmax_t dist = cmpresult;
-            if (dist < 0) dist *= -1;
-            if (dist < closest_dist) {
-                closest_dist = dist;
-                closest = n;
-            }
-        } else if (mode == UCX_AVL_FIND_LOWER_BOUNDED && cmpresult <= 0) {
-            if (cmpresult > closest_dist) {
-                closest_dist = cmpresult;
-                closest = n;
-            }
-        } else if (mode == UCX_AVL_FIND_UPPER_BOUNDED && cmpresult >= 0) {
-            if (cmpresult < closest_dist) {
-                closest_dist = cmpresult;
-                closest = n;
-            }
-        }
-        n = cmpresult > 0 ? n->right : n->left;
-    }
-    return n ? n : closest;
-}
-
-void *ucx_avl_find(UcxAVLTree *tree, intptr_t key,
-        distance_func dfnc, int mode) {
-    UcxAVLNode *n = ucx_avl_find_node(tree, key, dfnc, mode);
-    return n ? n->value : NULL;
-}
-
-int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value) {
-    return ucx_avl_put_s(tree, key, value, NULL);
-}
-
-int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value,
-        void **oldvalue) {
-    if (tree->root) {
-        UcxAVLNode *n = tree->root;
-        int cmpresult;
-        while ((cmpresult = tree->cmpfunc(
-                ptrcast(key), ptrcast(n->key), tree->userdata))) {
-            UcxAVLNode *m = cmpresult > 0 ? n->right : n->left;
-            if (m) {
-                n = m;
-            } else {
-                break;
-            }
-        }
-
-        if (cmpresult) {
-            UcxAVLNode* e = alloc_node(tree->allocator);
-            if (e) {
-                e->key = key; e->value = value; e->height = 1;
-                e->parent = e->left = e->right = NULL;
-                ucx_avl_connect(tree, n, e, 0);
-                ucx_avl_balance(tree, n);
-                return 0;
-            } else {
-                return 1;
-            }
-        } else {
-            if (oldvalue) {
-                *oldvalue = n->value;
-            }
-            n->value = value;
-            return 0;
-        }
-    } else {
-        tree->root = alloc_node(tree->allocator);
-        if (tree->root) {
-            tree->root->key = key; tree->root->value = value;
-            tree->root->height = 1;
-            tree->root->parent = tree->root->left = tree->root->right = NULL;
-            
-            if (oldvalue) {
-                *oldvalue = NULL;
-            }
-            
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-}
-
-int ucx_avl_remove(UcxAVLTree *tree, intptr_t key) {
-    return ucx_avl_remove_s(tree, key, NULL, NULL);
-}
-    
-int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node) {
-    return ucx_avl_remove_s(tree, node->key, NULL, NULL);
-}
-
-int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key,
-        intptr_t *oldkey, void **oldvalue) {
-    
-    UcxAVLNode *n = tree->root;
-    int cmpresult;
-    while (n && (cmpresult = tree->cmpfunc(
-            ptrcast(key), ptrcast(n->key), tree->userdata))) {
-        n = cmpresult > 0 ? n->right : n->left;
-    }
-    if (n) {
-        if (oldkey) {
-            *oldkey = n->key;
-        }
-        if (oldvalue) {
-            *oldvalue = n->value;
-        }
-        
-        UcxAVLNode *p = n->parent;
-        if (n->left && n->right) {
-            UcxAVLNode *s = n->right;
-            while (s->left) {
-                s = s->left;
-            }
-            ucx_avl_connect(tree, s->parent, s->right, s->key);
-            n->key = s->key; n->value = s->value;
-            p = s->parent;
-            alfree(tree->allocator, s);
-        } else {
-            if (p) {
-                ucx_avl_connect(tree, p, n->right ? n->right:n->left, n->key);
-            } else {
-                tree->root = n->right ? n->right : n->left;
-                if (tree->root) {
-                    tree->root->parent = NULL;
-                }
-            }
-            alfree(tree->allocator, n);
-        }
-
-        if (p) {
-            ucx_avl_balance(tree, p);
-        }
-        
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-static size_t ucx_avl_countn(UcxAVLNode *node) {
-    if (node) {
-        return 1 + ucx_avl_countn(node->left) + ucx_avl_countn(node->right);
-    } else {
-        return 0;
-    }
-}
-
-size_t ucx_avl_count(UcxAVLTree *tree) {
-    return ucx_avl_countn(tree->root);
-}
-
-UcxAVLNode* ucx_avl_pred(UcxAVLNode* node) {
-    if (node->left) {
-        UcxAVLNode* n = node->left;
-        while (n->right) {
-            n = n->right;
-        }
-        return n;
-    } else {
-        UcxAVLNode* n = node;
-        while (n->parent) {
-            if (n->parent->right == n) {
-                return n->parent;
-            } else {
-                n = n->parent;
-            }
-        }
-        return NULL;
-    }
-}
-
-UcxAVLNode* ucx_avl_succ(UcxAVLNode* node) {
-    if (node->right) {
-        UcxAVLNode* n = node->right;
-        while (n->left) {
-            n = n->left;
-        }
-        return n;
-    } else {
-        UcxAVLNode* n = node;
-        while (n->parent) {
-            if (n->parent->left == n) {
-                return n->parent;
-            } else {
-                n = n->parent;
-            }
-        }
-        return NULL;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/basic_mempool.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,235 @@
+/*
+ * 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.
+ */
+
+#include "cx/basic_mempool.h"
+#include "cx/utils.h"
+#include <string.h>
+
+#define of_chk_(n) if (SIZE_MAX - sizeof(cx_destructor_func) < (n)) return NULL
+
+/** Internal structure for denoting pooled memory. */
+typedef struct {
+    /** The destructor. */
+    cx_destructor_func destructor;
+    /**
+     * Access to the first byte of the polled memory.
+     */
+    char c;
+} cx_basic_mempool_memory;
+
+static int cx_basic_mempool_chcap(
+        struct cx_basic_mempool_s *pool,
+        size_t newcap
+) {
+    if (newcap < pool->ndata) {
+        return 1;
+    }
+
+    size_t newcapsz;
+    if (cx_szmul(newcap, sizeof(void *), &newcapsz)) {
+        return 1;
+    }
+
+    void **data = realloc(pool->data, newcapsz);
+    if (data) {
+        pool->data = data;
+        pool->size = newcap;
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+void *cx_malloc_basic_mempool(
+        void *data,
+        size_t n
+) {
+    of_chk_(n);
+    struct cx_basic_mempool_s *pool = data;
+
+    if (pool->ndata >= pool->size) {
+        size_t newcap = pool->size * 2;
+        if (newcap < pool->size || cx_basic_mempool_chcap(pool, newcap)) {
+            return NULL;
+        }
+    }
+
+    cx_basic_mempool_memory *mem = malloc(sizeof(cx_destructor_func) + n);
+    if (mem == NULL) {
+        return NULL;
+    }
+
+    mem->destructor = NULL;
+    pool->data[pool->ndata] = mem;
+    pool->ndata++;
+
+    return &(mem->c);
+}
+
+void *cx_calloc_basic_mempool(
+        void *data,
+        size_t nelem,
+        size_t elsize
+) {
+    size_t msz;
+    if (cx_szmul(nelem, elsize, &msz)) {
+        return NULL;
+    }
+    void *ptr = cx_malloc_basic_mempool(data, msz);
+    if (ptr == NULL) {
+        return NULL;
+    }
+    memset(ptr, 0, nelem * elsize);
+    return ptr;
+}
+
+void *cx_realloc_basic_mempool(
+        void *data,
+        void *ptr,
+        size_t n
+) {
+    of_chk_(n);
+    struct cx_basic_mempool_s *pool = data;
+
+    char *mem = ((char *) ptr) - sizeof(cx_destructor_func);
+    char *newm = (char *) realloc(mem, n + sizeof(cx_destructor_func));
+    if (newm == NULL) {
+        return NULL;
+    }
+    if (mem != newm) {
+        cx_for_n(i, pool->ndata) {
+            if (pool->data[i] == mem) {
+                pool->data[i] = newm;
+                return newm + sizeof(cx_destructor_func);
+            }
+        }
+        abort();
+    } else {
+        return newm + sizeof(cx_destructor_func);
+    }
+}
+
+void cx_free_basic_mempool(
+        void *data,
+        void *ptr
+) {
+    struct cx_basic_mempool_s *pool = data;
+
+    cx_basic_mempool_memory *mem = (cx_basic_mempool_memory *)
+            ((char *) ptr - sizeof(cx_destructor_func));
+    cx_for_n(i, pool->ndata) {
+        if (mem == pool->data[i]) {
+            if (mem->destructor != NULL) {
+                mem->destructor(&(mem->c));
+            }
+            free(mem);
+            size_t last_index = pool->ndata - 1;
+            if (i != last_index) {
+                pool->data[i] = pool->data[last_index];
+                pool->data[last_index] = NULL;
+            }
+            pool->ndata--;
+            return;
+        }
+    }
+    abort();
+}
+
+void cx_basic_mempool_destroy(CxMempool *p) {
+    struct cx_basic_mempool_s *pool = (struct cx_basic_mempool_s *) p;
+    cx_basic_mempool_memory *mem;
+    cx_for_n(i, pool->ndata) {
+        mem = (cx_basic_mempool_memory *) pool->data[i];
+        if (mem) {
+            if (mem->destructor) {
+                mem->destructor(&(mem->c));
+            }
+            free(mem);
+        }
+    }
+    free(pool->data);
+    free((void *) p->allocator);
+    free(pool);
+}
+
+void cx_basic_mempool_set_destr(
+        __attribute__((__unused__)) CxMempool *pool,
+        void *ptr,
+        cx_destructor_func func
+) {
+    *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func;
+}
+
+static cx_allocator_class cx_basic_mempool_allocator_class = {
+        cx_malloc_basic_mempool,
+        cx_realloc_basic_mempool,
+        cx_calloc_basic_mempool,
+        cx_free_basic_mempool
+};
+
+static cx_mempool_class cx_basic_mempool_class = {
+        cx_basic_mempool_destroy,
+        cx_basic_mempool_set_destr,
+};
+
+CxMempool *cxBasicMempoolCreate(size_t capacity) {
+    size_t poolsize;
+    if (cx_szmul(capacity, sizeof(void *), &poolsize)) {
+        return NULL;
+    }
+
+    struct cx_basic_mempool_s *pool =
+            malloc(sizeof(struct cx_basic_mempool_s));
+    if (pool == NULL) {
+        return NULL;
+    }
+
+
+    CxAllocator *provided_allocator = malloc(sizeof(CxAllocator));
+    if (!provided_allocator) {
+        free(pool);
+        return NULL;
+    }
+    provided_allocator->cl = &cx_basic_mempool_allocator_class;
+    provided_allocator->data = pool;
+
+    pool->base.cl = &cx_basic_mempool_class;
+    pool->base.allocator = provided_allocator;
+
+    pool->data = malloc(poolsize);
+    if (pool->data == NULL) {
+        free(provided_allocator);
+        free(pool);
+        return NULL;
+    }
+
+    pool->ndata = 0;
+    pool->size = capacity;
+
+    return (CxMempool *) pool;
+}
--- a/ucx/buffer.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/ucx/buffer.c	Fri Apr 21 21:25:32 2023 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * 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:
@@ -26,90 +26,100 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx/buffer.h"
+#include "cx/buffer.h"
+#include "cx/utils.h"
 
-#include <stdarg.h>
-#include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 
-UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags) {
-    UcxBuffer *buffer = (UcxBuffer*) malloc(sizeof(UcxBuffer));
-    if (buffer) {
-        buffer->flags = flags;
-        if (!space) {
-            buffer->space = (char*)malloc(capacity);
-            if (!buffer->space) {
-                free(buffer);
-                return NULL;
-            }
-            memset(buffer->space, 0, capacity);
-            buffer->flags |= UCX_BUFFER_AUTOFREE;
-        } else {
-            buffer->space = (char*)space;
+int cxBufferInit(
+        CxBuffer *buffer,
+        void *space,
+        size_t capacity,
+        CxAllocator const *allocator,
+        int flags
+) {
+    if (allocator == NULL) allocator = cxDefaultAllocator;
+    buffer->allocator = allocator;
+    buffer->flags = flags;
+    if (!space) {
+        buffer->bytes = cxMalloc(allocator, capacity);
+        if (buffer->bytes == NULL) {
+            return 1;
         }
-        buffer->capacity = capacity;
-        buffer->size = 0;
-
-        buffer->pos = 0;
+        buffer->flags |= CX_BUFFER_FREE_CONTENTS;
+    } else {
+        buffer->bytes = space;
     }
+    buffer->capacity = capacity;
+    buffer->size = 0;
+    buffer->pos = 0;
 
-    return buffer;
-}
+    buffer->flush_func = NULL;
+    buffer->flush_target = NULL;
+    buffer->flush_blkmax = 0;
+    buffer->flush_blksize = 4096;
+    buffer->flush_threshold = SIZE_MAX;
 
-void ucx_buffer_free(UcxBuffer *buffer) {
-    if ((buffer->flags & UCX_BUFFER_AUTOFREE) == UCX_BUFFER_AUTOFREE) {
-        free(buffer->space);
-    }
-    free(buffer);
+    return 0;
 }
 
-UcxBuffer* ucx_buffer_extract(
-        UcxBuffer *src, size_t start, size_t length, int flags) {
-    if (src->size == 0 || length == 0 ||
-        ((size_t)-1) - start < length || start+length > src->capacity)
-    {
+void cxBufferDestroy(CxBuffer *buffer) {
+    if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) {
+        cxFree(buffer->allocator, buffer->bytes);
+    }
+}
+
+CxBuffer *cxBufferCreate(
+        void *space,
+        size_t capacity,
+        CxAllocator const *allocator,
+        int flags
+) {
+    CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer));
+    if (buf == NULL) return NULL;
+    if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) {
+        return buf;
+    } else {
+        cxFree(allocator, buf);
         return NULL;
     }
+}
 
-    UcxBuffer *dst = (UcxBuffer*) malloc(sizeof(UcxBuffer));
-    if (dst) {
-        dst->space = (char*)malloc(length);
-        if (!dst->space) {
-            free(dst);
-            return NULL;
-        }
-        dst->capacity = length;
-        dst->size = length;
-        dst->flags = flags | UCX_BUFFER_AUTOFREE;
-        dst->pos = 0;
-        memcpy(dst->space, src->space+start, length);
+void cxBufferFree(CxBuffer *buffer) {
+    if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) {
+        cxFree(buffer->allocator, buffer->bytes);
     }
-    return dst;
+    cxFree(buffer->allocator, buffer);
 }
 
-int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence) {
+int cxBufferSeek(
+        CxBuffer *buffer,
+        off_t offset,
+        int whence
+) {
     size_t npos;
     switch (whence) {
-    case SEEK_CUR:
-        npos = buffer->pos;
-        break;
-    case SEEK_END:
-        npos = buffer->size;
-        break;
-    case SEEK_SET:
-        npos = 0;
-        break;
-    default:
-        return -1;
+        case SEEK_CUR:
+            npos = buffer->pos;
+            break;
+        case SEEK_END:
+            npos = buffer->size;
+            break;
+        case SEEK_SET:
+            npos = 0;
+            break;
+        default:
+            return -1;
     }
 
     size_t opos = npos;
     npos += offset;
-    
+
     if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
         return -1;
     }
-    
+
     if (npos >= buffer->size) {
         return -1;
     } else {
@@ -119,135 +129,237 @@
 
 }
 
-int ucx_buffer_eof(UcxBuffer *buffer) {
+void cxBufferClear(CxBuffer *buffer) {
+    memset(buffer->bytes, 0, buffer->size);
+    buffer->size = 0;
+    buffer->pos = 0;
+}
+
+int cxBufferEof(CxBuffer const *buffer) {
     return buffer->pos >= buffer->size;
 }
 
-int ucx_buffer_extend(UcxBuffer *buffer, size_t len) {
-    size_t newcap = buffer->capacity;
-    
-    if (buffer->capacity + len < buffer->capacity) {
-        return -1;
+int cxBufferMinimumCapacity(
+        CxBuffer *buffer,
+        size_t newcap
+) {
+    if (newcap <= buffer->capacity) {
+        return 0;
     }
-    
-    while (buffer->capacity + len > newcap) {
-        newcap <<= 1;
-        if (newcap < buffer->capacity) {
-            return -1;
-        }
-    }
-    
-    char *newspace = (char*)realloc(buffer->space, newcap);
-    if (newspace) {
-        memset(newspace+buffer->size, 0, newcap-buffer->size);
-        buffer->space = newspace;
+
+    if (cxReallocate(buffer->allocator,
+                     (void **) &buffer->bytes, newcap) == 0) {
         buffer->capacity = newcap;
+        return 0;
     } else {
         return -1;
     }
-    
-    return 0;
 }
 
-size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
-        UcxBuffer *buffer) {
+/**
+ * Helps flushing data to the flush target of a buffer.
+ *
+ * @param buffer the buffer containing the config
+ * @param space the data to flush
+ * @param size the element size
+ * @param nitems the number of items
+ * @return the number of items flushed
+ */
+static size_t cx_buffer_write_flush_helper(
+        CxBuffer *buffer,
+        unsigned char const *space,
+        size_t size,
+        size_t nitems
+) {
+    size_t pos = 0;
+    size_t remaining = nitems;
+    size_t max_items = buffer->flush_blksize / size;
+    while (remaining > 0) {
+        size_t items = remaining > max_items ? max_items : remaining;
+        size_t flushed = buffer->flush_func(
+                space + pos,
+                size, items,
+                buffer->flush_target);
+        if (flushed > 0) {
+            pos += (flushed * size);
+            remaining -= flushed;
+        } else {
+            // if no bytes can be flushed out anymore, we give up
+            break;
+        }
+    }
+    return nitems - remaining;
+}
+
+size_t cxBufferWrite(
+        void const *ptr,
+        size_t size,
+        size_t nitems,
+        CxBuffer *buffer
+) {
+    // optimize for easy case
+    if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
+        memcpy(buffer->bytes + buffer->pos, ptr, nitems);
+        buffer->pos += nitems;
+        if (buffer->pos > buffer->size) {
+            buffer->size = buffer->pos;
+        }
+        return nitems;
+    }
+
     size_t len;
-    if(ucx_szmul(size, nitems, &len)) {
+    size_t nitems_out = nitems;
+    if (cx_szmul(size, nitems, &len)) {
         return 0;
     }
     size_t required = buffer->pos + len;
     if (buffer->pos > required) {
         return 0;
     }
-    
+
+    bool perform_flush = false;
     if (required > buffer->capacity) {
-        if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
-            if (ucx_buffer_extend(buffer, required - buffer->capacity)) {
-                return 0;
+        if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) {
+            if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) {
+                perform_flush = true;
+            } else {
+                if (cxBufferMinimumCapacity(buffer, required)) {
+                    return 0;
+                }
             }
         } else {
-            len = buffer->capacity - buffer->pos;
-            if (size > 1) {
-                len -= len%size;
+            if (buffer->flush_blkmax > 0) {
+                perform_flush = true;
+            } else {
+                // truncate data to be written, if we can neither extend nor flush
+                len = buffer->capacity - buffer->pos;
+                if (size > 1) {
+                    len -= len % size;
+                }
+                nitems_out = len / size;
             }
         }
     }
-    
+
     if (len == 0) {
         return len;
     }
-    
-    memcpy(buffer->space + buffer->pos, ptr, len);
-    buffer->pos += len;
-    if(buffer->pos > buffer->size) {
-        buffer->size = buffer->pos;
+
+    if (perform_flush) {
+        size_t flush_max;
+        if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) {
+            return 0;
+        }
+        size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL
+                           ? buffer->pos
+                           : cx_buffer_write_flush_helper(buffer, buffer->bytes, 1, buffer->pos);
+        if (flush_pos == buffer->pos) {
+            // entire buffer has been flushed, we can reset
+            buffer->size = buffer->pos = 0;
+
+            size_t items_flush; // how many items can also be directly flushed
+            size_t items_keep; // how many items have to be written to the buffer
+
+            items_flush = flush_max >= required ? nitems : (flush_max - flush_pos) / size;
+            if (items_flush > 0) {
+                items_flush = cx_buffer_write_flush_helper(buffer, ptr, size, items_flush / size);
+                // in case we could not flush everything, keep the rest
+            }
+            items_keep = nitems - items_flush;
+            if (items_keep > 0) {
+                // try again with the remaining stuff
+                unsigned char const *new_ptr = ptr;
+                new_ptr += items_flush * size;
+                // report the directly flushed items as written plus the remaining stuff
+                return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer);
+            } else {
+                // all items have been flushed - report them as written
+                return nitems;
+            }
+        } else if (flush_pos == 0) {
+            // nothing could be flushed at all, we immediately give up without writing any data
+            return 0;
+        } else {
+            // we were partially successful, we shift left and try again
+            cxBufferShiftLeft(buffer, flush_pos);
+            return cxBufferWrite(ptr, size, nitems, buffer);
+        }
+    } else {
+        memcpy(buffer->bytes + buffer->pos, ptr, len);
+        buffer->pos += len;
+        if (buffer->pos > buffer->size) {
+            buffer->size = buffer->pos;
+        }
+        return nitems_out;
     }
-    
-    return len / size;
+
 }
 
-size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
-        UcxBuffer *buffer) {
+int cxBufferPut(
+        CxBuffer *buffer,
+        int c
+) {
+    c &= 0xFF;
+    unsigned char const ch = c;
+    if (cxBufferWrite(&ch, 1, 1, buffer) == 1) {
+        return c;
+    } else {
+        return EOF;
+    }
+}
+
+size_t cxBufferPutString(
+        CxBuffer *buffer,
+        const char *str
+) {
+    return cxBufferWrite(str, 1, strlen(str), buffer);
+}
+
+size_t cxBufferRead(
+        void *ptr,
+        size_t size,
+        size_t nitems,
+        CxBuffer *buffer
+) {
     size_t len;
-    if(ucx_szmul(size, nitems, &len)) {
+    if (cx_szmul(size, nitems, &len)) {
         return 0;
     }
     if (buffer->pos + len > buffer->size) {
         len = buffer->size - buffer->pos;
-        if (size > 1) len -= len%size;
+        if (size > 1) len -= len % size;
     }
-    
+
     if (len <= 0) {
         return len;
     }
-    
-    memcpy(ptr, buffer->space + buffer->pos, len);
+
+    memcpy(ptr, buffer->bytes + buffer->pos, len);
     buffer->pos += len;
-    
+
     return len / size;
 }
 
-int ucx_buffer_putc(UcxBuffer *buffer, int c) {
-    if(buffer->pos >= buffer->capacity) {
-        if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
-            if(ucx_buffer_extend(buffer, 1)) {
-                return EOF;
-            }
-        } else {
-            return EOF;
-        }
-    }
-    
-    c &= 0xFF;
-    buffer->space[buffer->pos] = (char) c;
-    buffer->pos++;
-    if(buffer->pos > buffer->size) {
-        buffer->size = buffer->pos;
-    }
-    return c;
-}
-
-int ucx_buffer_getc(UcxBuffer *buffer) {
-    if (ucx_buffer_eof(buffer)) {
+int cxBufferGet(CxBuffer *buffer) {
+    if (cxBufferEof(buffer)) {
         return EOF;
     } else {
-        int c = ((unsigned char*)buffer->space)[buffer->pos];
+        int c = buffer->bytes[buffer->pos];
         buffer->pos++;
         return c;
     }
 }
 
-size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) {
-    return ucx_buffer_write((const void*)str, 1, strlen(str), buffer);
-}
-
-int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) {
+int cxBufferShiftLeft(
+        CxBuffer *buffer,
+        size_t shift
+) {
     if (shift >= buffer->size) {
         buffer->pos = buffer->size = 0;
     } else {
-        memmove(buffer->space, buffer->space + shift, buffer->size - shift);
+        memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift);
         buffer->size -= shift;
-        
+
         if (buffer->pos >= shift) {
             buffer->pos -= shift;
         } else {
@@ -257,14 +369,17 @@
     return 0;
 }
 
-int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) {
+int cxBufferShiftRight(
+        CxBuffer *buffer,
+        size_t shift
+) {
     size_t req_capacity = buffer->size + shift;
     size_t movebytes;
-    
+
     // auto extend buffer, if required and enabled
     if (buffer->capacity < req_capacity) {
-        if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
-            if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) {
+        if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) {
+            if (cxBufferMinimumCapacity(buffer, req_capacity)) {
                 return 1;
             }
             movebytes = buffer->size;
@@ -274,23 +389,26 @@
     } else {
         movebytes = buffer->size;
     }
-    
-    memmove(buffer->space + shift, buffer->space, movebytes);
-    buffer->size = shift+movebytes;
-    
+
+    memmove(buffer->bytes + shift, buffer->bytes, movebytes);
+    buffer->size = shift + movebytes;
+
     buffer->pos += shift;
     if (buffer->pos > buffer->size) {
         buffer->pos = buffer->size;
     }
-    
+
     return 0;
 }
 
-int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) {
+int cxBufferShift(
+        CxBuffer *buffer,
+        off_t shift
+) {
     if (shift < 0) {
-        return ucx_buffer_shift_left(buffer, (size_t) (-shift));
+        return cxBufferShiftLeft(buffer, (size_t) (-shift));
     } else if (shift > 0) {
-        return ucx_buffer_shift_right(buffer, (size_t) shift);
+        return cxBufferShiftRight(buffer, (size_t) shift);
     } else {
         return 0;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/compare.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+#include "cx/compare.h"
+
+#include <math.h>
+
+int cx_cmp_int(void const *i1, void const *i2) {
+    int a = *((const int *) i1);
+    int b = *((const int *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_longint(void const *i1, void const *i2) {
+    long int a = *((const long int *) i1);
+    long int b = *((const long int *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_longlong(void const *i1, void const *i2) {
+    long long a = *((const long long *) i1);
+    long long b = *((const long long *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_int16(void const *i1, void const *i2) {
+    int16_t a = *((const int16_t *) i1);
+    int16_t b = *((const int16_t *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_int32(void const *i1, void const *i2) {
+    int32_t a = *((const int32_t *) i1);
+    int32_t b = *((const int32_t *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_int64(void const *i1, void const *i2) {
+    int64_t a = *((const int64_t *) i1);
+    int64_t b = *((const int64_t *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_uint(void const *i1, void const *i2) {
+    unsigned int a = *((const unsigned int *) i1);
+    unsigned int b = *((const unsigned int *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_ulongint(void const *i1, void const *i2) {
+    unsigned long int a = *((const unsigned long int *) i1);
+    unsigned long int b = *((const unsigned long int *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_ulonglong(void const *i1, void const *i2) {
+    unsigned long long a = *((const unsigned long long *) i1);
+    unsigned long long b = *((const unsigned long long *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_uint16(void const *i1, void const *i2) {
+    uint16_t a = *((const uint16_t *) i1);
+    uint16_t b = *((const uint16_t *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_uint32(void const *i1, void const *i2) {
+    uint32_t a = *((const uint32_t *) i1);
+    uint32_t b = *((const uint32_t *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_uint64(void const *i1, void const *i2) {
+    uint64_t a = *((const uint64_t *) i1);
+    uint64_t b = *((const uint64_t *) i2);
+    if (a == b) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_float(void const *f1, void const *f2) {
+    float a = *((const float *) f1);
+    float b = *((const float *) f2);
+    if (fabsf(a - b) < 1e-6f) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_double(
+        void const *d1,
+        void const *d2
+) {
+    double a = *((const double *) d1);
+    double b = *((const double *) d2);
+    if (fabs(a - b) < 1e-14) {
+        return 0;
+    } else {
+        return a < b ? -1 : 1;
+    }
+}
+
+int cx_cmp_intptr(
+        void const *ptr1,
+        void const *ptr2
+) {
+    intptr_t p1 = *(const intptr_t *) ptr1;
+    intptr_t p2 = *(const intptr_t *) ptr2;
+    if (p1 == p2) {
+        return 0;
+    } else {
+        return p1 < p2 ? -1 : 1;
+    }
+}
+
+int cx_cmp_uintptr(
+        void const *ptr1,
+        void const *ptr2
+) {
+    uintptr_t p1 = *(const uintptr_t *) ptr1;
+    uintptr_t p2 = *(const uintptr_t *) ptr2;
+    if (p1 == p2) {
+        return 0;
+    } else {
+        return p1 < p2 ? -1 : 1;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/allocator.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,225 @@
+/*
+ * 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 allocator.h
+ * Interface for custom allocators.
+ */
+
+#ifndef UCX_ALLOCATOR_H
+#define UCX_ALLOCATOR_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The class definition for an allocator.
+ */
+typedef struct {
+    /**
+     * The allocator's malloc() implementation.
+     */
+    void *(*malloc)(
+            void *data,
+            size_t n
+    );
+
+    /**
+     * The allocator's realloc() implementation.
+     */
+    void *(*realloc)(
+            void *data,
+            void *mem,
+            size_t n
+    )
+    __attribute__((__warn_unused_result__));
+
+    /**
+     * The allocator's calloc() implementation.
+     */
+    void *(*calloc)(
+            void *data,
+            size_t nelem,
+            size_t n
+    );
+
+    /**
+     * The allocator's free() implementation.
+     */
+    void (*free)(
+            void *data,
+            void *mem
+    )
+    __attribute__((__nonnull__));
+} cx_allocator_class;
+
+/**
+ * Structure holding the data for an allocator.
+ */
+struct cx_allocator_s {
+    /**
+     * A pointer to the instance of the allocator class.
+     */
+    cx_allocator_class *cl;
+    /**
+     * A pointer to the data this allocator uses.
+     */
+    void *data;
+};
+
+/**
+ * High-Level type alias for the allocator type.
+ */
+typedef struct cx_allocator_s CxAllocator;
+
+/**
+ * A default allocator using standard library malloc() etc.
+ */
+extern CxAllocator *cxDefaultAllocator;
+
+/**
+ * Function pointer type for destructor functions.
+ *
+ * A destructor function deallocates possible contents and MAY free the memory
+ * pointed to by \p memory. Read the documentation of the respective function
+ * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that
+ * particular implementation.
+ *
+ * @param memory a pointer to the object to destruct
+  */
+typedef void (*cx_destructor_func)(void *memory) __attribute__((__nonnull__));
+
+/**
+ * Function pointer type for destructor functions.
+ *
+ * A destructor function deallocates possible contents and MAY free the memory
+ * pointed to by \p memory. Read the documentation of the respective function
+ * pointer to learn if a destructor SHALL, MAY, or MUST NOT free the memory in that
+ * particular implementation.
+ *
+ * @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
+) __attribute__((__nonnull__(2)));
+
+/**
+ * Allocate \p n bytes of memory.
+ *
+ * @param allocator the allocator
+ * @param n the number of bytes
+ * @return a pointer to the allocated memory
+ */
+void *cxMalloc(
+        CxAllocator const *allocator,
+        size_t n
+)
+__attribute__((__malloc__))
+__attribute__((__alloc_size__(2)));
+
+/**
+ * Re-allocate the previously allocated block in \p mem, making the new block \p n bytes long.
+ * This function may return the same pointer that was passed to it, if moving the memory
+ * was not necessary.
+ *
+ * \note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * @param allocator the allocator
+ * @param mem pointer to the previously allocated block
+ * @param n the new size in bytes
+ * @return a pointer to the re-allocated memory
+ */
+void *cxRealloc(
+        CxAllocator const *allocator,
+        void *mem,
+        size_t n
+)
+__attribute__((__warn_unused_result__))
+__attribute__((__alloc_size__(3)));
+
+/**
+ * Re-allocate a previously allocated block and changes the pointer in-place, if necessary.
+ * This function acts like cxRealloc() using the pointer pointed to by \p mem.
+ * On success, the pointer is changed to the new location (in case the
+ *
+ * \note Re-allocating a block allocated by a different allocator is undefined.
+ *
+ * \par Error handling
+ * \c errno will be set, if the underlying realloc function does so.
+ *
+ * @param allocator the allocator
+ * @param mem pointer to the pointer to allocated block
+ * @param n the new size in bytes
+ * @return zero on success, non-zero on failure
+ */
+int cxReallocate(
+        CxAllocator const *allocator,
+        void **mem,
+        size_t n
+)
+__attribute__((__nonnull__));
+
+/**
+ * Allocate \p nelem elements of \p n bytes each, all initialized to zero.
+ *
+ * @param allocator the allocator
+ * @param nelem the number of elements
+ * @param n the size of each element in bytes
+ * @return a pointer to the allocated memory
+ */
+void *cxCalloc(
+        CxAllocator const *allocator,
+        size_t nelem,
+        size_t n
+)
+__attribute__((__malloc__))
+__attribute__((__alloc_size__(2, 3)));
+
+/**
+ * Free a block allocated by this allocator.
+ *
+ * \note Freeing a block of a different allocator is undefined.
+ *
+ * @param allocator the allocator
+ * @param mem a pointer to the block to free
+ */
+void cxFree(
+        CxAllocator const *allocator,
+        void *mem
+)
+__attribute__((__nonnull__));
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_ALLOCATOR_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/array_list.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,194 @@
+/*
+ * 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 array_list.h
+ * \brief Array list implementation.
+ * \details Also provides several low-level functions for custom array list implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+
+#ifndef UCX_ARRAY_LIST_H
+#define UCX_ARRAY_LIST_H
+
+#include "list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Defines a reallocation mechanism for arrays.
+ */
+struct cx_array_reallocator_s {
+    /**
+     * Re-allocates space for the given array.
+     *
+     * Implementations are not required to free the original array.
+     * This allows re-allocation of static memory by allocating heap memory
+     * and copying the array contents. The information in \p data can keep
+     * track of the state of the memory or other additional allocator info.
+     *
+     * @param array the array to reallocate
+     * @param capacity the new capacity (number of elements)
+     * @param elem_size the size of each element
+     * @param alloc a reference to this allocator
+     * @return a pointer to the reallocated memory or \c NULL on failure
+     */
+    void *(*realloc)(
+            void *array,
+            size_t capacity,
+            size_t elem_size,
+            struct cx_array_reallocator_s *alloc
+    );
+
+    /**
+     * Custom data pointer.
+     */
+    void *ptr1;
+    /**
+     * Custom data pointer.
+     */
+    void *ptr2;
+    /**
+     * Custom data integer.
+     */
+    size_t int1;
+    /**
+     * Custom data integer.
+     */
+    size_t int2;
+};
+
+/**
+ * Return codes for cx_array_copy().
+ */
+enum cx_array_copy_result {
+    CX_ARRAY_COPY_SUCCESS,
+    CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED,
+    CX_ARRAY_COPY_REALLOC_FAILED,
+};
+
+/**
+ * Copies elements from one array to another.
+ *
+ * The elements are copied to the \p target array at the specified \p index,
+ * overwriting possible elements. The \p index does not need to be in range of
+ * the current array \p size. If the new index plus the number of elements added
+ * would extend the array's size, and \p capacity is not \c NULL, the remaining
+ * capacity is used.
+ *
+ * If the capacity is insufficient to hold the new data, a reallocation
+ * attempt is made, unless the allocator is set to \c NULL, in which case
+ * this function ultimately returns a failure.
+ *
+ * @param target the target array
+ * @param size a pointer to the size of the target array
+ * @param capacity a pointer to the target array's capacity -
+ * \c NULL if only the size shall be used to bound the array
+ * @param index the index where the copied elements shall be placed
+ * @param src the source array
+ * @param elem_size the size of one element
+ * @param elem_count the number of elements to copy
+ * @param reallocator the array re-allocator to use, or \c NULL
+ * if re-allocation shall not happen
+ * @return zero on success, non-zero error code on failure
+ */
+enum cx_array_copy_result cx_array_copy(
+        void **target,
+        size_t *size,
+        size_t *capacity,
+        size_t index,
+        void const *src,
+        size_t elem_size,
+        size_t elem_count,
+        struct cx_array_reallocator_s *reallocator
+) __attribute__((__nonnull__(1, 2, 5)));
+
+
+/**
+ * Swaps two array elements.
+ *
+ * @param arr the array
+ * @param elem_size the element size
+ * @param idx1 index of first element
+ * @param idx2 index of second element
+ */
+void cx_array_swap(
+        void *arr,
+        size_t elem_size,
+        size_t idx1,
+        size_t idx2
+) __attribute__((__nonnull__));
+
+/**
+ * Allocates an array list for storing elements with \p item_size bytes each.
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
+ * cxListStorePointers() was called immediately after creation.
+ *
+ * @param allocator the allocator for allocating the list memory
+ * (if \c NULL the cxDefaultAllocator will be used)
+ * @param comparator the comparator for the elements
+ * (if \c NULL sort and find functions will not work)
+ * @param item_size the size of each element in bytes
+ * @param initial_capacity the initial number of elements the array can store
+ * @return the created list
+ */
+CxList *cxArrayListCreate(
+        CxAllocator const *allocator,
+        cx_compare_func comparator,
+        size_t item_size,
+        size_t initial_capacity
+);
+
+/**
+ * Allocates an array list for storing elements with \p item_size bytes each.
+ *
+ * The list will use the cxDefaultAllocator and \em NO compare function.
+ * If you want to call functions that need a compare function, you have to
+ * set it immediately after creation or use cxArrayListCreate().
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
+ * cxListStorePointers() was called immediately after creation.
+ *
+ * @param item_size the size of each element in bytes
+ * @param initial_capacity the initial number of elements the array can store
+ * @return the created list
+ */
+#define cxArrayListCreateSimple(item_size, initial_capacity) \
+    cxArrayListCreate(NULL, NULL, item_size, initial_capacity)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_ARRAY_LIST_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/basic_mempool.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,76 @@
+/*
+ * 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 basic_mempool.h
+ * \brief Implementation of a basic memory pool.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_BASIC_MEMPOOL_H
+#define UCX_BASIC_MEMPOOL_H
+
+#include "mempool.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Basic array-based memory pool.
+ */
+struct cx_basic_mempool_s {
+    /** Inherit base structure members. */
+    CxMempool base;
+
+    /** List of pointers to pooled memory. */
+    void **data;
+
+    /** Number of pooled memory items. */
+    size_t ndata;
+
+    /** Memory pool size. */
+    size_t size;
+};
+
+/**
+ * Creates a basic array-based memory pool.
+ *
+ * @param capacity the initial capacity of the pool
+ * @return the created memory pool or \c NULL if allocation failed
+ */
+__attribute__((__warn_unused_result__))
+CxMempool *cxBasicMempoolCreate(size_t capacity);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_BASIC_MEMPOOL_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/buffer.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,451 @@
+/*
+ * 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 buffer.h
+ *
+ * \brief Advanced buffer implementation.
+ *
+ * Instances of CxBuffer can be used to read from or to write to like one
+ * would do with a stream.
+ *
+ * Some features for convenient use of the buffer
+ * can be enabled. See the documentation of the macro constants for more
+ * information.
+ *
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_BUFFER_H
+#define UCX_BUFFER_H
+
+#include "common.h"
+#include "allocator.h"
+
+#ifdef    __cplusplus
+extern "C" {
+#endif
+
+/**
+ * No buffer features enabled (all flags cleared).
+ */
+#define CX_BUFFER_DEFAULT 0x00
+
+/**
+ * If this flag is enabled, the buffer will automatically free its contents when destroyed.
+ */
+#define CX_BUFFER_FREE_CONTENTS 0x01
+
+/**
+ * If this flag is enabled, the buffer will automatically extends its capacity.
+ */
+#define CX_BUFFER_AUTO_EXTEND 0x02
+
+/** Structure for the UCX buffer data. */
+typedef struct {
+    /** A pointer to the buffer contents. */
+    union {
+        /**
+         * Data is interpreted as text.
+         */
+        char *space;
+        /**
+         * Data is interpreted as binary.
+         */
+        unsigned char *bytes;
+    };
+    /** The allocator to use for automatic memory management. */
+    CxAllocator const *allocator;
+    /** Current position of the buffer. */
+    size_t pos;
+    /** Current capacity (i.e. maximum size) of the buffer. */
+    size_t capacity;
+    /** Current size of the buffer content. */
+    size_t size;
+    /**
+     * The buffer may not extend beyond this threshold before starting to flush.
+     * Default is \c SIZE_MAX (flushing disabled when auto extension is enabled).
+     */
+    size_t flush_threshold;
+    /**
+     * The block size for the elements to flush.
+     * Default is 4096 bytes.
+     */
+    size_t flush_blksize;
+    /**
+     * The maximum number of blocks to flush in one cycle.
+     * Zero disables flushing entirely (this is the default).
+     * Set this to \c SIZE_MAX to flush the entire buffer.
+     *
+     * @attention if the maximum number of blocks multiplied with the block size
+     * is smaller than the expected contents written to this buffer within one write
+     * operation, multiple flush cycles are performed after that write.
+     * That means the total number of blocks flushed after one write to this buffer may
+     * be larger than \c flush_blkmax.
+     */
+    size_t flush_blkmax;
+
+    /**
+     * The write function used for flushing.
+     * If NULL, the flushed content gets discarded.
+     */
+    cx_write_func flush_func;
+
+    /**
+     * The target for \c flush_func.
+     */
+    void *flush_target;
+
+    /**
+     * Flag register for buffer features.
+     * @see #CX_BUFFER_DEFAULT
+     * @see #CX_BUFFER_FREE_CONTENTS
+     * @see #CX_BUFFER_AUTO_EXTEND
+     */
+    int flags;
+} cx_buffer_s;
+
+/**
+ * UCX buffer.
+ */
+typedef cx_buffer_s CxBuffer;
+
+/**
+ * Initializes a fresh buffer.
+ *
+ * \note You may provide \c NULL as argument for \p space.
+ * Then this function will allocate the space and enforce
+ * the #CX_BUFFER_FREE_CONTENTS flag.
+ *
+ * @param buffer the buffer to initialize
+ * @param space pointer to the memory area, or \c NULL to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
+ * @param allocator the allocator this buffer shall use for automatic
+ * memory management. If \c NULL, the default heap allocator will be used.
+ * @param flags buffer features (see cx_buffer_s.flags)
+ * @return zero on success, non-zero if a required allocation failed
+ */
+__attribute__((__nonnull__(1)))
+int cxBufferInit(
+        CxBuffer *buffer,
+        void *space,
+        size_t capacity,
+        CxAllocator const *allocator,
+        int flags
+);
+
+/**
+ * Allocates and initializes a fresh buffer.
+ *
+ * \note You may provide \c NULL as argument for \p space.
+ * Then this function will allocate the space and enforce
+ * the #CX_BUFFER_FREE_CONTENTS flag.
+ *
+ * @param space pointer to the memory area, or \c NULL to allocate
+ * new memory
+ * @param capacity the capacity of the buffer
+ * @param allocator the allocator to use for allocating the structure and the automatic
+ * memory management within the buffer. If \c NULL, the default heap allocator will be used.
+ * @param flags buffer features (see cx_buffer_s.flags)
+ * @return a pointer to the buffer on success, \c NULL if a required allocation failed
+ */
+CxBuffer *cxBufferCreate(
+        void *space,
+        size_t capacity,
+        CxAllocator const *allocator,
+        int flags
+);
+
+/**
+ * Destroys the buffer contents.
+ *
+ * Has no effect if the #CX_BUFFER_FREE_CONTENTS feature is not enabled.
+ * If you want to free the memory of the entire buffer, use cxBufferFree().
+ *
+ * @param buffer the buffer which contents shall be destroyed
+ * @see cxBufferInit()
+ */
+__attribute__((__nonnull__))
+void cxBufferDestroy(CxBuffer *buffer);
+
+/**
+ * Deallocates the buffer.
+ *
+ * If the #CX_BUFFER_FREE_CONTENTS feature is enabled, this function also destroys
+ * the contents. If you \em only want to destroy the contents, use cxBufferDestroy().
+ *
+ * @param buffer the buffer to deallocate
+ * @see cxBufferCreate()
+ */
+__attribute__((__nonnull__))
+void cxBufferFree(CxBuffer *buffer);
+
+/**
+ * Shifts the contents of the buffer by the given offset.
+ *
+ * If the offset is positive, the contents are shifted to the right.
+ * If auto extension is enabled, the buffer grows, if necessary.
+ * In case the auto extension fails, this function returns a non-zero value and
+ * no contents are changed.
+ * If auto extension is disabled, the contents that do not fit into the buffer
+ * are discarded.
+ *
+ * If the offset is negative, the contents are shifted to the left where the
+ * first \p shift bytes are discarded.
+ * The new size of the buffer is the old size minus the absolute shift value.
+ * If this value is larger than the buffer size, the buffer is emptied (but
+ * not cleared, see the security note below).
+ *
+ * The buffer position gets shifted alongside with the content but is kept
+ * within the boundaries of the buffer.
+ *
+ * \note For situations where \c off_t is not large enough, there are specialized cxBufferShiftLeft() and
+ * cxBufferShiftRight() functions using a \c size_t as parameter type.
+ *
+ * \attention
+ * Security Note: The shifting operation does \em not erase the previously occupied memory cells.
+ * But you can easily do that manually, e.g. by calling
+ * <code>memset(buffer->bytes, 0, shift)</code> for a right shift or
+ * <code>memset(buffer->bytes + buffer->size, 0, buffer->capacity - buffer->size)</code>
+ * for a left shift.
+ *
+ * @param buffer the buffer
+ * @param shift the shift offset (negative means left shift)
+ * @return 0 on success, non-zero if a required auto-extension fails
+ */
+__attribute__((__nonnull__))
+int cxBufferShift(
+        CxBuffer *buffer,
+        off_t shift
+);
+
+/**
+ * Shifts the buffer to the right.
+ * See cxBufferShift() for details.
+ *
+ * @param buffer the buffer
+ * @param shift the shift offset
+ * @return 0 on success, non-zero if a required auto-extension fails
+ * @see cxBufferShift()
+ */
+__attribute__((__nonnull__))
+int cxBufferShiftRight(
+        CxBuffer *buffer,
+        size_t shift
+);
+
+/**
+ * Shifts the buffer to the left.
+ * See cxBufferShift() for details.
+ *
+ * \note Since a left shift cannot fail due to memory allocation problems, this
+ * function always returns zero.
+ *
+ * @param buffer the buffer
+ * @param shift the positive shift offset
+ * @return always zero
+ * @see cxBufferShift()
+ */
+__attribute__((__nonnull__))
+int cxBufferShiftLeft(
+        CxBuffer *buffer,
+        size_t shift
+);
+
+
+/**
+ * Moves the position of the buffer.
+ *
+ * The new position is relative to the \p whence argument.
+ *
+ * \li \c SEEK_SET marks the start of the buffer.
+ * \li \c SEEK_CUR marks the current position.
+ * \li \c SEEK_END marks the end of the buffer.
+ *
+ * With an offset of zero, this function sets the buffer position to zero
+ * (\c SEEK_SET), the buffer size (\c SEEK_END) or leaves the buffer position
+ * unchanged (\c SEEK_CUR).
+ *
+ * @param buffer the buffer
+ * @param offset position offset relative to \p whence
+ * @param whence one of \c SEEK_SET, \c SEEK_CUR or \c SEEK_END
+ * @return 0 on success, non-zero if the position is invalid
+ *
+ */
+__attribute__((__nonnull__))
+int cxBufferSeek(
+        CxBuffer *buffer,
+        off_t offset,
+        int whence
+);
+
+/**
+ * Clears the buffer by resetting the position and deleting the data.
+ *
+ * The data is deleted by zeroing it with a call to memset().
+ *
+ * @param buffer the buffer to be cleared
+ */
+__attribute__((__nonnull__))
+void cxBufferClear(CxBuffer *buffer);
+
+/**
+ * Tests, if the buffer position has exceeded the buffer capacity.
+ *
+ * @param buffer the buffer to test
+ * @return non-zero, if the current buffer position has exceeded the last
+ * available byte of the buffer.
+ */
+__attribute__((__nonnull__))
+int cxBufferEof(CxBuffer const *buffer);
+
+
+/**
+ * Ensures that the buffer has a minimum capacity.
+ *
+ * If the current capacity is not sufficient, the buffer will be extended.
+ *
+ * @param buffer the buffer
+ * @param capacity the minimum required capacity for this buffer
+ * @return 0 on success or a non-zero value on failure
+ */
+__attribute__((__nonnull__))
+int cxBufferMinimumCapacity(
+        CxBuffer *buffer,
+        size_t capacity
+);
+
+/**
+ * Writes data to a CxBuffer.
+ *
+ * If flushing is enabled and the buffer needs to flush, the data is flushed to
+ * the target until the target signals that it cannot take more data by
+ * returning zero via the respective write function. In that case, the remaining
+ * data in this buffer is shifted to the beginning of this buffer so that the
+ * newly available space can be used to append as much data as possible. This
+ * function only stops writing more elements, when the flush target and this
+ * buffer are both incapable of taking more data or all data has been written.
+ * The number returned by this function is the total number of elements that
+ * could be written during the process. It does not necessarily mean that those
+ * elements are still in this buffer, because some of them could have also be
+ * flushed already.
+ *
+ * If automatic flushing is not enabled, the position of the buffer is increased
+ * by the number of bytes written.
+ *
+ * \note The signature is compatible with the fwrite() family of functions.
+ *
+ * @param ptr a pointer to the memory area containing the bytes to be written
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the CxBuffer to write to
+ * @return the total count of elements written
+ */
+__attribute__((__nonnull__))
+size_t cxBufferWrite(
+        void const *ptr,
+        size_t size,
+        size_t nitems,
+        CxBuffer *buffer
+);
+
+/**
+ * Reads data from a CxBuffer.
+ *
+ * The position of the buffer is increased by the number of bytes read.
+ *
+ * \note The signature is compatible with the fread() family of functions.
+ *
+ * @param ptr a pointer to the memory area where to store the read data
+ * @param size the length of one element
+ * @param nitems the element count
+ * @param buffer the CxBuffer to read from
+ * @return the total number of elements read
+ */
+__attribute__((__nonnull__))
+size_t cxBufferRead(
+        void *ptr,
+        size_t size,
+        size_t nitems,
+        CxBuffer *buffer
+);
+
+/**
+ * Writes a character to a buffer.
+ *
+ * The least significant byte of the argument is written to the buffer. If the
+ * end of the buffer is reached and #CX_BUFFER_AUTO_EXTEND feature is enabled,
+ * the buffer capacity is extended by cxBufferMinimumCapacity(). If the feature is
+ * disabled or buffer extension fails, \c EOF is returned.
+ *
+ * On successful write, the position of the buffer is increased.
+ *
+ * @param buffer the buffer to write to
+ * @param c the character to write
+ * @return the byte that has bean written or \c EOF when the end of the stream is
+ * reached and automatic extension is not enabled or not possible
+ */
+__attribute__((__nonnull__))
+int cxBufferPut(
+        CxBuffer *buffer,
+        int c
+);
+
+/**
+ * Writes a string to a buffer.
+ *
+ * @param buffer the buffer
+ * @param str the zero-terminated string
+ * @return the number of bytes written
+ */
+__attribute__((__nonnull__))
+size_t cxBufferPutString(
+        CxBuffer *buffer,
+        const char *str
+);
+
+/**
+ * Gets a character from a buffer.
+ *
+ * The current position of the buffer is increased after a successful read.
+ *
+ * @param buffer the buffer to read from
+ * @return the character or \c EOF, if the end of the buffer is reached
+ */
+__attribute__((__nonnull__))
+int cxBufferGet(CxBuffer *buffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // UCX_BUFFER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/collection.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,138 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2023 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 collection.h
+ * \brief Common definitions for various collection implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_COLLECTION_H
+#define UCX_COLLECTION_H
+
+#include "allocator.h"
+#include "iterator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Special constant used for creating collections that are storing pointers.
+ */
+#define CX_STORE_POINTERS 0
+
+/**
+ * A comparator function comparing two collection elements.
+ */
+typedef int(*cx_compare_func)(
+        void const *left,
+        void const *right
+);
+
+/**
+ * Use this macro to declare common members for a collection structure.
+ */
+#define CX_COLLECTION_MEMBERS \
+    /** \
+     * The allocator to use. \
+     */ \
+    CxAllocator const *allocator; \
+    /** \
+     * The comparator function for the elements. \
+     */ \
+    cx_compare_func cmpfunc; \
+    /** \
+     * The size of each element. \
+     */ \
+    size_t item_size; \
+    /** \
+     * The number of currently stored elements. \
+     */ \
+    size_t size; \
+    /** \
+     * An optional simple destructor for the collection's elements. \
+     * \
+     * @attention Read the documentation of the particular collection implementation \
+     * whether this destructor shall only destroy the contents or also free the memory. \
+     */ \
+    cx_destructor_func simple_destructor; \
+    /** \
+     * An optional advanced destructor for the collection's elements. \
+     * \
+     * @attention Read the documentation of the particular collection implementation \
+     * whether this destructor shall only destroy the contents or also free the memory. \
+     */ \
+    cx_destructor_func2 advanced_destructor; \
+    /** \
+     * The pointer to additional data that is passed to the advanced destructor. \
+     */ \
+    void *destructor_data; \
+    /** \
+     * Indicates if this instance of a collection is supposed to store pointers \
+     * instead of copies of the actual objects. \
+     */ \
+    bool store_pointer;
+
+/**
+ * Invokes the simple destructor function for a specific element.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c the collection
+ * @param e the element
+ */
+#define cx_invoke_simple_destructor(c, e) \
+    (c)->simple_destructor((c)->store_pointer ? (*((void **) (e))) : (e))
+
+/**
+ * Invokes the advanced destructor function for a specific element.
+ *
+ * Usually only used by collection implementations. There should be no need
+ * to invoke this macro manually.
+ *
+ * @param c the collection
+ * @param e the element
+ */
+#define cx_invoke_advanced_destructor(c, e) \
+    (c)->advanced_destructor((c)->destructor_data, \
+    (c)->store_pointer ? (*((void **) (e))) : (e))
+
+
+#define cx_invoke_destructor(c, e) \
+    if ((c)->simple_destructor) cx_invoke_simple_destructor(c,e); \
+    if ((c)->advanced_destructor) cx_invoke_advanced_destructor(c,e)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_COLLECTION_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/common.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,141 @@
+/*
+ * 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>
+ * -&nbsp;or&nbsp;-
+ * <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)
+
+// Common Includes
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.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 *
+);
+
+
+// Compiler specific stuff
+
+#ifndef __GNUC__
+/**
+ * Removes GNU C attributes where they are not supported.
+ */
+#define __attribute__(x)
+#endif
+
+#ifdef _MSC_VER
+
+// fix missing ssize_t definition
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+
+// fix missing _Thread_local support
+#define _Thread_local __declspec(thread)
+
+#endif
+
+#endif // UCX_COMMON_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/compare.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,220 @@
+/*
+ * 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 compare.h
+ * \brief A collection of simple compare functions.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_COMPARE_H
+#define UCX_COMPARE_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Compares two integers of type int.
+ *
+ * @param i1 pointer to integer one
+ * @param i2 pointer to integer two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_int(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type long int.
+ *
+ * @param i1 pointer to long integer one
+ * @param i2 pointer to long integer two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_longint(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type long long.
+ *
+ * @param i1 pointer to long long one
+ * @param i2 pointer to long long two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_longlong(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type int16_t.
+ *
+ * @param i1 pointer to int16_t one
+ * @param i2 pointer to int16_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_int16(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type int32_t.
+ *
+ * @param i1 pointer to int32_t one
+ * @param i2 pointer to int32_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_int32(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type int64_t.
+ *
+ * @param i1 pointer to int64_t one
+ * @param i2 pointer to int64_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_int64(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type unsigned int.
+ *
+ * @param i1 pointer to unsigned integer one
+ * @param i2 pointer to unsigned integer two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_uint(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type unsigned long int.
+ *
+ * @param i1 pointer to unsigned long integer one
+ * @param i2 pointer to unsigned long integer two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_ulongint(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type unsigned long long.
+ *
+ * @param i1 pointer to unsigned long long one
+ * @param i2 pointer to unsigned long long two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_ulonglong(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type uint16_t.
+ *
+ * @param i1 pointer to uint16_t one
+ * @param i2 pointer to uint16_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_uint16(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type uint32_t.
+ *
+ * @param i1 pointer to uint32_t one
+ * @param i2 pointer to uint32_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_uint32(void const *i1, void const *i2);
+
+/**
+ * Compares two integers of type uint64_t.
+ *
+ * @param i1 pointer to uint64_t one
+ * @param i2 pointer to uint64_t two
+ * @return -1, if *i1 is less than *i2, 0 if both are equal,
+ * 1 if *i1 is greater than *i2
+ */
+int cx_cmp_uint64(void const *i1, void const *i2);
+
+/**
+ * Compares two real numbers of type float with precision 1e-6f.
+ *
+ * @param f1 pointer to float one
+ * @param f2 pointer to float two
+ * @return -1, if *f1 is less than *f2, 0 if both are equal,
+ * 1 if *f1 is greater than *f2
+ */
+
+int cx_cmp_float(void const *f1, void const *f2);
+
+/**
+ * Compares two real numbers of type double with precision 1e-14.
+ *
+ * @param d1 pointer to double one
+ * @param d2 pointer to double two
+ * @return -1, if *d1 is less than *d2, 0 if both are equal,
+ * 1 if *d1 is greater than *d2
+ */
+int cx_cmp_double(
+        void const *d1,
+        void const *d2
+);
+
+/**
+ * Compares the integer representation of two pointers.
+ *
+ * @param ptr1 pointer to pointer one (intptr_t const*)
+ * @param ptr2 pointer to pointer two (intptr_t const*)
+ * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal,
+ * 1 if *ptr1 is greater than *ptr2
+ */
+int cx_cmp_intptr(
+        void const *ptr1,
+        void const *ptr2
+);
+
+/**
+ * Compares the unsigned integer representation of two pointers.
+ *
+ * @param ptr1 pointer to pointer one (uintptr_t const*)
+ * @param ptr2 pointer to pointer two (uintptr_t const*)
+ * @return -1 if *ptr1 is less than *ptr2, 0 if both are equal,
+ * 1 if *ptr1 is greater than *ptr2
+ */
+int cx_cmp_uintptr(
+        void const *ptr1,
+        void const *ptr2
+);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //UCX_COMPARE_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/hash_key.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,129 @@
+/*
+ * 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 hash_key.h
+ * \brief Interface for map implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+
+#ifndef UCX_HASH_KEY_H
+#define UCX_HASH_KEY_H
+
+#include "common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal structure for a key within a hash map. */
+struct cx_hash_key_s {
+    /** The key data. */
+    void const *data;
+    /**
+     * The key data length.
+     */
+    size_t len;
+    /** The hash value of the key data. */
+    unsigned hash;
+};
+
+/**
+ * Type for a hash key.
+ */
+typedef struct cx_hash_key_s CxHashKey;
+
+/**
+ * Computes a murmur2 32 bit hash.
+ *
+ * You need to initialize \c data and \c len in the key struct.
+ * The hash is then directly written to that struct.
+ *
+ * \note If \c data is \c NULL, the hash is defined as 1574210520.
+ *
+ * @param key the key, the hash shall be computed for
+ */
+void cx_hash_murmur(CxHashKey *key);
+
+/**
+ * Computes a hash key from a string.
+ *
+ * The string needs to be zero-terminated.
+ *
+ * @param str the string
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key_str(char const *str);
+
+/**
+ * Computes a hash key from a byte array.
+ *
+ * @param bytes the array
+ * @param len the length
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key_bytes(
+        unsigned char const *bytes,
+        size_t len
+);
+
+/**
+ * Computes a hash key for an arbitrary object.
+ *
+ * The computation uses the in-memory representation that might not be
+ * the same on different platforms. Therefore, this hash should not be
+ * used for data exchange with different machines.
+ *
+ * @param obj a pointer to an arbitrary object
+ * @param len the length of object in memory
+ * @return the hash key
+ */
+__attribute__((__warn_unused_result__))
+CxHashKey cx_hash_key(
+        void const *obj,
+        size_t len
+);
+
+/**
+ * Computes a hash key from a UCX string.
+ *
+ * @param str the string
+ * @return the hash key
+ */
+#define cx_hash_key_cxstr(str) cx_hash_key((void*)(str).ptr, (str).length)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_HASH_KEY_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/hash_map.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,118 @@
+/*
+ * 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 hash_map.h
+ * \brief Hash map implementation.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_HASH_MAP_H
+#define UCX_HASH_MAP_H
+
+#include "map.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal structure for an element of a hash map. */
+struct cx_hash_map_element_s;
+
+/**
+ * Internal structure for a hash map.
+ */
+struct cx_hash_map_s {
+    /**
+     * Base structure for maps.
+     */
+    struct cx_map_s base;
+    /**
+     * The buckets of this map, each containing a linked list of elements.
+     */
+    struct cx_hash_map_element_s **buckets;
+    /**
+     * The number of buckets.
+     */
+    size_t bucket_count;
+};
+
+
+/**
+ * Creates a new hash map with the specified number of buckets.
+ *
+ * If \p buckets is zero, an implementation defined default will be used.
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created map will be created as if
+ * cxMapStorePointers() was called immediately after creation.
+ *
+ * @note Iterators provided by this hash map implementation provide the remove operation.
+ * The index value of an iterator is the incremented when the iterator advanced without removal.
+ * In other words, when the iterator is finished, \c index==size .
+ *
+ * @param allocator the allocator to use
+ * @param itemsize the size of one element
+ * @param buckets the initial number of buckets in this hash map
+ * @return a pointer to the new hash map
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+CxMap *cxHashMapCreate(
+        CxAllocator const *allocator,
+        size_t itemsize,
+        size_t buckets
+);
+
+/**
+ * Increases the number of buckets, if necessary.
+ *
+ * The load threshold is \c 0.75*buckets. If the element count exceeds the load
+ * threshold, the map will be rehashed. Otherwise, no action is performed and
+ * this function simply returns 0.
+ *
+ * The rehashing process ensures, that the number of buckets is at least
+ * 2.5 times the element count. So there is enough room for additional
+ * elements without the need of another soon rehashing.
+ *
+ * You can use this function after filling a map to increase access performance.
+ *
+ * @note If the specified map is not a hash map, the behavior is undefined.
+ *
+ * @param map the map to rehash
+ * @return zero on success, non-zero if a memory allocation error occurred
+ */
+__attribute__((__nonnull__))
+int cxMapRehash(CxMap *map);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_HASH_MAP_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/iterator.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,257 @@
+/*
+ * 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 iterator.h
+ * \brief Interface for iterator implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_ITERATOR_H
+#define UCX_ITERATOR_H
+
+#include "common.h"
+
+/**
+ * The base of mutating and non-mutating iterators.
+ */
+struct cx_iterator_base_s {
+    /**
+     * True iff the iterator points to valid data.
+     */
+    __attribute__ ((__nonnull__))
+    bool (*valid)(void const *);
+
+    /**
+     * Returns a pointer to the current element.
+     */
+    __attribute__ ((__nonnull__))
+    void *(*current)(void const *);
+
+    /**
+     * Original implementation in case the function needs to be wrapped.
+     */
+    __attribute__ ((__nonnull__))
+    void *(*current_impl)(void const *);
+
+    /**
+     * Advances the iterator.
+     */
+    __attribute__ ((__nonnull__))
+    void (*next)(void *);
+
+    /**
+     * Flag current element for removal, if possible.
+     */
+    __attribute__ ((__nonnull__))
+    bool (*flag_removal)(void *);
+
+    /**
+     * Indicates whether this iterator is muting.
+     */
+    bool mutating;
+
+    /**
+     * Internal flag for removing the current element when advancing.
+     */
+    bool remove;
+};
+
+/**
+ * Internal iterator struct - use CxMutIterator.
+ */
+struct cx_mut_iterator_s {
+
+    /**
+     * The base properties of this iterator.
+     */
+    struct cx_iterator_base_s base;
+
+    /**
+     * Handle for the current element, if required.
+     */
+    void *elem_handle;
+
+    /**
+     * Handle for the source collection, if any.
+     */
+    void *src_handle;
+
+    /**
+     * Field for storing a key-value pair.
+     * May be used by iterators that iterate over k/v-collections.
+     */
+    struct {
+        /**
+         * A pointer to the key.
+         */
+        void const *key;
+        /**
+         * A pointer to the value.
+         */
+        void *value;
+    } kv_data;
+
+    /**
+     * Field for storing a slot number.
+     * May be used by iterators that iterate over multi-bucket collections.
+     */
+    size_t slot;
+
+    /**
+     * If the iterator is position-aware, contains the index of the element in the underlying collection.
+     * Otherwise, this field is usually uninitialized.
+     */
+    size_t index;
+};
+
+/**
+ * Mutating iterator value type.
+ *
+ * An iterator points to a certain element in an (possibly unbounded) chain of elements.
+ * Iterators that are based on collections (which have a defined "first" element), are supposed
+ * 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 mutable through that iterator. However, if the
+ * iterator is based on a collection and the underlying collection is mutated by other means than this iterator
+ * (e.g. elements added or removed), the iterator becomes invalid (regardless of what cxIteratorValid() returns)
+ * and MUST be re-obtained from the collection.
+ *
+ * @see CxIterator
+ */
+typedef struct cx_mut_iterator_s CxMutIterator;
+
+/**
+ * Internal iterator struct - use CxIterator.
+ */
+struct cx_iterator_s {
+
+    /**
+     * The base properties of this iterator.
+     */
+    struct cx_iterator_base_s base;
+
+    /**
+     * Handle for the current element, if required.
+     */
+    void *elem_handle;
+
+    /**
+     * Handle for the source collection, if any.
+     */
+    void const *src_handle;
+
+    /**
+     * Field for storing a key-value pair.
+     * May be used by iterators that iterate over k/v-collections.
+     */
+    struct {
+        /**
+         * A pointer to the key.
+         */
+        void const *key;
+        /**
+         * A pointer to the value.
+         */
+        void *value;
+    } kv_data;
+
+    /**
+     * Field for storing a slot number.
+     * May be used by iterators that iterate over multi-bucket collections.
+     */
+    size_t slot;
+
+    /**
+     * If the iterator is position-aware, contains the index of the element in the underlying collection.
+     * Otherwise, this field is usually uninitialized.
+     */
+    size_t index;
+};
+
+/**
+ * Iterator value type.
+ * An iterator points to a certain element in an (possibly unbounded) chain of elements.
+ * Iterators that are based on collections (which have a defined "first" element), are supposed
+ * 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,
+ * this iterator cannot mutate the collection itself (add or remove elements) and any mutation of the
+ * collection by other means make this iterator invalid (regardless of what cxIteratorValid() returns).
+ *
+ * @see CxMutIterator
+ */
+typedef struct cx_iterator_s CxIterator;
+
+/**
+ * Checks if the iterator points to valid data.
+ *
+ * This is especially false for past-the-end iterators.
+ *
+ * @param iter the iterator
+ * @return true iff the iterator points to valid data
+ */
+#define cxIteratorValid(iter) (iter).base.valid(&(iter))
+
+/**
+ * Returns a pointer to the current element.
+ *
+ * The behavior is undefined if this iterator is invalid.
+ *
+ * @param iter the iterator
+ * @return a pointer to the current element
+ */
+#define cxIteratorCurrent(iter) (iter).base.current(&iter)
+
+/**
+ * Advances the iterator to the next element.
+ *
+ * @param iter the iterator
+ */
+#define cxIteratorNext(iter) (iter).base.next(&iter)
+
+/**
+ * Flags the current element for removal.
+ *
+ * @param iter the iterator
+ * @return false if this iterator cannot remove the element
+ */
+#define cxIteratorFlagRemoval(iter) (iter).base.flag_removal(&iter)
+
+/**
+ * Loops over an iterator.
+ * @param type the type of the elements
+ * @param elem the name of the iteration variable
+ * @param iter the iterator
+ */
+#define cx_foreach(type, elem, iter) \
+for (type elem; cxIteratorValid(iter) && (elem = (type)cxIteratorCurrent(iter)) != NULL ; cxIteratorNext(iter))
+
+#endif // UCX_ITERATOR_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/linked_list.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,415 @@
+/*
+ * 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 linked_list.h
+ * \brief Linked list implementation.
+ * \details Also provides several low-level functions for custom linked list implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_LINKED_LIST_H
+#define UCX_LINKED_LIST_H
+
+#include "common.h"
+#include "list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Set this flag to true, if you want to disable the use of SBO for
+ * linked list swap operations.
+ */
+extern bool CX_DISABLE_LINKED_LIST_SWAP_SBO;
+
+/**
+ * Allocates a linked list for storing elements with \p item_size bytes each.
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
+ * cxListStorePointers() was called immediately after creation.
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * (if \c NULL the cxDefaultAllocator will be used)
+ * @param comparator the comparator for the elements
+ * (if \c NULL sort and find functions will not work)
+ * @param item_size the size of each element in bytes
+ * @return the created list
+ */
+CxList *cxLinkedListCreate(
+        CxAllocator const *allocator,
+        cx_compare_func comparator,
+        size_t item_size
+);
+
+/**
+ * Allocates a linked list for storing elements with \p item_size bytes each.
+ *
+ * The list will use cxDefaultAllocator and no comparator function. If you want
+ * to call functions that need a comparator, you must either set one immediately
+ * after list creation or use cxLinkedListCreate().
+ *
+ * If \p item_size is CX_STORE_POINTERS, the created list will be created as if
+ * cxListStorePointers() was called immediately after creation.
+ *
+ * @param item_size the size of each element in bytes
+ * @return the created list
+ */
+#define cxLinkedListCreateSimple(item_size) \
+    cxLinkedListCreate(NULL, NULL, item_size)
+
+/**
+ * Finds the node at a certain index.
+ *
+ * This function can be used to start at an arbitrary position within the list.
+ * If the search index is large than the start index, \p loc_advance must denote
+ * the location of some sort of \c next pointer (i.e. a pointer to the next node).
+ * But it is also possible that the search index is smaller than the start index
+ * (e.g. in cases where traversing a list backwards is faster) in which case
+ * \p loc_advance must denote the location of some sort of \c prev pointer
+ * (i.e. a pointer to the previous node).
+ *
+ * @param start a pointer to the start node
+ * @param start_index the start index
+ * @param loc_advance the location of the pointer to advance
+ * @param index the search index
+ * @return the node found at the specified index
+ */
+void *cx_linked_list_at(
+        void const *start,
+        size_t start_index,
+        ptrdiff_t loc_advance,
+        size_t index
+) __attribute__((__nonnull__));
+
+/**
+ * Finds the index of an element within a linked list.
+ *
+ * @param start a pointer to the start node
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the \c data pointer within your node struct
+ * @param cmp_func a compare function to compare \p elem against the node data
+ * @param elem a pointer to the element to find
+ * @return the index of the element or a past-one index if the element could not be found
+ */
+size_t cx_linked_list_find(
+        void const *start,
+        ptrdiff_t loc_advance,
+        ptrdiff_t loc_data,
+        cx_compare_func cmp_func,
+        void const *elem
+) __attribute__((__nonnull__));
+
+/**
+ * Finds the first node in a linked list.
+ *
+ * The function starts with the pointer denoted by \p node and traverses the list
+ * along a prev pointer whose location within the node struct is
+ * denoted by \p loc_prev.
+ *
+ * @param node a pointer to a node in the list
+ * @param loc_prev the location of the \c prev pointer
+ * @return a pointer to the first node
+ */
+void *cx_linked_list_first(
+        void const *node,
+        ptrdiff_t loc_prev
+) __attribute__((__nonnull__));
+
+/**
+ * Finds the last node in a linked list.
+ *
+ * The function starts with the pointer denoted by \p node and traverses the list
+ * along a next pointer whose location within the node struct is
+ * denoted by \p loc_next.
+ *
+ * @param node a pointer to a node in the list
+ * @param loc_next the location of the \c next pointer
+ * @return a pointer to the last node
+ */
+void *cx_linked_list_last(
+        void const *node,
+        ptrdiff_t loc_next
+) __attribute__((__nonnull__));
+
+/**
+ * Finds the predecessor of a node in case it is not linked.
+ *
+ * \remark If \p node is not contained in the list starting with \p begin, the behavior is undefined.
+ *
+ * @param begin the node where to start the search
+ * @param loc_next the location of the \c next pointer
+ * @param node the successor of the node to find
+ * @return the node or \c NULL if \p node has no predecessor
+ */
+void *cx_linked_list_prev(
+        void const *begin,
+        ptrdiff_t loc_next,
+        void const *node
+) __attribute__((__nonnull__));
+
+/**
+ * Adds a new node to a linked list.
+ * The node must not be part of any list already.
+ *
+ * \remark One of the pointers \p begin or \p end may be \c NULL, but not both.
+ *
+ * @param begin a pointer to the begin node pointer (if your list has one)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be appended
+ */
+void cx_linked_list_add(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *new_node
+) __attribute__((__nonnull__(5)));
+
+/**
+ * Prepends a new node to a linked list.
+ * The node must not be part of any list already.
+ *
+ * \remark One of the pointers \p begin or \p end may be \c NULL, but not both.
+ *
+ * @param begin a pointer to the begin node pointer (if your list has one)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param new_node a pointer to the node that shall be prepended
+ */
+void cx_linked_list_prepend(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *new_node
+) __attribute__((__nonnull__(5)));
+
+/**
+ * Links two nodes.
+ *
+ * @param left the new predecessor of \p right
+ * @param right the new successor of \p left
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ */
+void cx_linked_list_link(
+        void *left,
+        void *right,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) __attribute__((__nonnull__));
+
+/**
+ * Unlinks two nodes.
+ *
+ * If right is not the successor of left, the behavior is undefined.
+ *
+ * @param left the predecessor of \p right
+ * @param right the successor of \p left
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ */
+void cx_linked_list_unlink(
+        void *left,
+        void *right,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) __attribute__((__nonnull__));
+
+/**
+ * Inserts a new node after a given node of a linked list.
+ * The new node must not be part of any list already.
+ *
+ * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or
+ * the \p end pointer to determine the start of the list. Then the new node will be prepended to the list.
+ *
+ * @param begin a pointer to the begin node pointer (if your list has one)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param node the node after which to insert (\c NULL if you want to prepend the node to the list)
+ * @param new_node a pointer to the node that shall be prepended
+ */
+void cx_linked_list_insert(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *node,
+        void *new_node
+) __attribute__((__nonnull__(6)));
+
+/**
+ * Inserts a chain of nodes after a given node of a linked list.
+ * The chain must not be part of any list already.
+ *
+ * If you do not explicitly specify the end of the chain, it will be determined by traversing
+ * the \c next pointer.
+ *
+ * \note If you specify \c NULL as the \p node to insert after, this function needs either the \p begin or
+ * the \p end pointer to determine the start of the list. If only the \p end pointer is specified, you also need
+ * to provide a valid \p loc_prev location.
+ * Then the chain will be prepended to the list.
+ *
+ * @param begin a pointer to the begin node pointer (if your list has one)
+ * @param end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param node the node after which to insert (\c NULL to prepend the chain to the list)
+ * @param insert_begin a pointer to the first node of the chain that shall be inserted
+ * @param insert_end a pointer to the last node of the chain (or NULL if the last node shall be determined)
+ */
+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
+) __attribute__((__nonnull__(6)));
+
+/**
+ * Removes a node from the linked list.
+ *
+ * If the node to remove is the begin (resp. end) node of the list and if \p begin (resp. \p end)
+ * addresses are provided, the pointers are adjusted accordingly.
+ *
+ * The following combinations of arguments are valid (more arguments are optional):
+ * \li \p loc_next and \p loc_prev (ancestor node is determined by using the prev pointer, overall O(1) performance)
+ * \li \p loc_next and \p begin (ancestor node is determined by list traversal, overall O(n) performance)
+ *
+ * \remark The \c next and \c prev pointers of the removed node are not cleared by this function and may still be used
+ * to traverse to a former adjacent node in the list.
+ *
+ * @param begin a pointer to the begin node pointer (optional)
+ * @param end a pointer to the end node pointer (optional)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param node the node to remove
+ */
+void cx_linked_list_remove(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *node
+) __attribute__((__nonnull__(5)));
+
+
+/**
+ * Determines the size of a linked list starting with \p node.
+ * @param node the first node
+ * @param loc_next the location of the \c next pointer within the node struct
+ * @return the size of the list or zero if \p node is \c NULL
+ */
+size_t cx_linked_list_size(
+        void const *node,
+        ptrdiff_t loc_next
+);
+
+/**
+ * Sorts a linked list based on a comparison function.
+ *
+ * This function can work with linked lists of the following structure:
+ * \code
+ * typedef struct node node;
+ * struct node {
+ *   node* prev;
+ *   node* next;
+ *   my_payload data;
+ * }
+ * \endcode
+ *
+ * @note This is a recursive function with at most logarithmic recursion depth.
+ *
+ * @param begin a pointer to the begin node pointer (required)
+ * @param end a pointer to the end node pointer (optional)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if not present)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ * @param loc_data the location of the \c data pointer within your node struct
+ * @param cmp_func the compare function defining the sort order
+ */
+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
+) __attribute__((__nonnull__(1, 6)));
+
+
+/**
+ * Compares two lists element wise.
+ *
+ * \note Both list must have the same structure.
+ *
+ * @param begin_left the begin of the left list (\c NULL denotes an empty list)
+ * @param begin_right the begin of the right list  (\c NULL denotes an empty list)
+ * @param loc_advance the location of the pointer to advance
+ * @param loc_data the location of the \c data pointer within your node struct
+ * @param cmp_func the function to compare the elements
+ * @return the first non-zero result of invoking \p cmp_func or: negative if the left list is smaller than the
+ * right list, positive if the left list is larger than the right list, zero if both lists are equal.
+ */
+int cx_linked_list_compare(
+        void const *begin_left,
+        void const *begin_right,
+        ptrdiff_t loc_advance,
+        ptrdiff_t loc_data,
+        cx_compare_func cmp_func
+) __attribute__((__nonnull__(5)));
+
+/**
+ * Reverses the order of the nodes in a linked list.
+ *
+ * @param begin a pointer to the begin node pointer (required)
+ * @param end a pointer to the end node pointer (optional)
+ * @param loc_prev the location of a \c prev pointer within your node struct (negative if your struct does not have one)
+ * @param loc_next the location of a \c next pointer within your node struct (required)
+ */
+void cx_linked_list_reverse(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) __attribute__((__nonnull__(1)));
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_LINKED_LIST_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/list.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,638 @@
+/*
+ * 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 list.h
+ * \brief Interface for list implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_LIST_H
+#define UCX_LIST_H
+
+#include "common.h"
+#include "collection.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * List class type.
+ */
+typedef struct cx_list_class_s cx_list_class;
+
+/**
+ * Structure for holding the base data of a list.
+ */
+struct cx_list_s {
+    CX_COLLECTION_MEMBERS
+    /**
+     * The list class definition.
+     */
+    cx_list_class const *cl;
+    /**
+     * The actual implementation in case the list class is delegating.
+     */
+    cx_list_class const *climpl;
+};
+
+/**
+ * The class definition for arbitrary lists.
+ */
+struct cx_list_class_s {
+    /**
+     * Destructor function.
+     */
+    void (*destructor)(struct cx_list_s *list);
+
+    /**
+     * Member function for inserting a single elements.
+     * Implementors SHOULD see to performant implementations for corner cases.
+     */
+    int (*insert_element)(
+            struct cx_list_s *list,
+            size_t index,
+            void const *data
+    );
+
+    /**
+     * Member function for inserting multiple elements.
+     * Implementors SHOULD see to performant implementations for corner cases.
+     */
+    size_t (*insert_array)(
+            struct cx_list_s *list,
+            size_t index,
+            void const *data,
+            size_t n
+    );
+
+    /**
+     * Member function for inserting an element relative to an iterator position.
+     */
+    int (*insert_iter)(
+            struct cx_mut_iterator_s *iter,
+            void const *elem,
+            int prepend
+    );
+
+    /**
+     * Member function for removing an element.
+     */
+    int (*remove)(
+            struct cx_list_s *list,
+            size_t index
+    );
+
+    /**
+     * Member function for removing all elements.
+     */
+    void (*clear)(struct cx_list_s *list);
+
+    /**
+     * Member function for swapping two elements.
+     */
+    int (*swap)(
+            struct cx_list_s *list,
+            size_t i,
+            size_t j
+    );
+
+    /**
+     * Member function for element lookup.
+     */
+    void *(*at)(
+            struct cx_list_s const *list,
+            size_t index
+    );
+
+    /**
+     * Member function for finding an element.
+     */
+    size_t (*find)(
+            struct cx_list_s const *list,
+            void const *elem
+    );
+
+    /**
+     * Member function for sorting the list in place.
+     */
+    void (*sort)(struct cx_list_s *list);
+
+    /**
+     * Member function for comparing this list to another list of the same type.
+     */
+    int (*compare)(
+            struct cx_list_s const *list,
+            struct cx_list_s const *other
+    );
+
+    /**
+     * Member function for reversing the order of the items.
+     */
+    void (*reverse)(struct cx_list_s *list);
+
+    /**
+     * Member function for returning an iterator pointing to the specified index.
+     */
+    struct cx_iterator_s (*iterator)(
+            struct cx_list_s const *list,
+            size_t index,
+            bool backward
+    );
+};
+
+/**
+ * Common type for all list implementations.
+ */
+typedef struct cx_list_s CxList;
+
+/**
+ * Advises the list to store copies of the objects (default mode of operation).
+ *
+ * Retrieving objects from this list will yield pointers to the copies stored
+ * within this list.
+ *
+ * @param list the list
+ * @see cxListStorePointers()
+ */
+__attribute__((__nonnull__))
+void cxListStoreObjects(CxList *list);
+
+/**
+ * Advises the list to only store pointers to the objects.
+ *
+ * Retrieving objects from this list will yield the original pointers stored.
+ *
+ * @note This function forcibly sets the element size to the size of a pointer.
+ * Invoking this function on a non-empty list that already stores copies of
+ * objects is undefined.
+ *
+ * @param list the list
+ * @see cxListStoreObjects()
+ */
+__attribute__((__nonnull__))
+void cxListStorePointers(CxList *list);
+
+/**
+ * Returns true, if this list is storing pointers instead of the actual data.
+ *
+ * @param list
+ * @return true, if this list is storing pointers
+ * @see cxListStorePointers()
+ */
+__attribute__((__nonnull__))
+static inline bool cxListIsStoringPointers(CxList const *list) {
+    return list->store_pointer;
+}
+
+/**
+ * Returns the number of elements currently stored in the list.
+ *
+ * @param list the list
+ * @return the number of currently stored elements
+ */
+__attribute__((__nonnull__))
+static inline size_t cxListSize(CxList const *list) {
+    return list->size;
+}
+
+/**
+ * Adds an item to the end of the list.
+ *
+ * @param list the list
+ * @param elem a pointer to the element to add
+ * @return zero on success, non-zero on memory allocation failure
+ * @see cxListAddArray()
+ */
+__attribute__((__nonnull__))
+static inline int cxListAdd(
+        CxList *list,
+        void const *elem
+) {
+    return list->cl->insert_element(list, list->size, elem);
+}
+
+/**
+ * Adds multiple items to the end of the list.
+ *
+ * This method is more efficient than invoking cxListAdd() multiple times.
+ *
+ * If there is not enough memory to add all elements, the returned value is
+ * less than \p n.
+ *
+ * If this list is storing pointers instead of objects \p array is expected to
+ * be an array of pointers.
+ *
+ * @param list the list
+ * @param array a pointer to the elements to add
+ * @param n the number of elements to add
+ * @return the number of added elements
+ */
+__attribute__((__nonnull__))
+static inline size_t cxListAddArray(
+        CxList *list,
+        void const *array,
+        size_t n
+) {
+    return list->cl->insert_array(list, list->size, array, n);
+}
+
+/**
+ * Inserts an item at the specified index.
+ *
+ * If \p index equals the list \c size, this is effectively cxListAdd().
+ *
+ * @param list the list
+ * @param index the index the element shall have
+ * @param elem a pointer to the element to add
+ * @return zero on success, non-zero on memory allocation failure
+ * or when the index is out of bounds
+ * @see cxListInsertAfter()
+ * @see cxListInsertBefore()
+ */
+__attribute__((__nonnull__))
+static inline int cxListInsert(
+        CxList *list,
+        size_t index,
+        void const *elem
+) {
+    return list->cl->insert_element(list, index, elem);
+}
+
+/**
+ * Inserts multiple items to the list at the specified index.
+ * If \p index equals the list size, this is effectively cxListAddArray().
+ *
+ * This method is usually more efficient than invoking cxListInsert()
+ * multiple times.
+ *
+ * If there is not enough memory to add all elements, the returned value is
+ * less than \p n.
+ *
+ * If this list is storing pointers instead of objects \p array is expected to
+ * be an array of pointers.
+ *
+ * @param list the list
+ * @param index the index where to add the elements
+ * @param array a pointer to the elements to add
+ * @param n the number of elements to add
+ * @return the number of added elements
+ */
+__attribute__((__nonnull__))
+static inline size_t cxListInsertArray(
+        CxList *list,
+        size_t index,
+        void const *array,
+        size_t n
+) {
+    return list->cl->insert_array(list, index, array, n);
+}
+
+/**
+ * Inserts an element after the current location of the specified iterator.
+ *
+ * The used iterator remains operational, but all other active iterators should
+ * be considered invalidated.
+ *
+ * If \p iter is not a list iterator, the behavior is undefined.
+ * If \p iter is a past-the-end iterator, the new element gets appended to the list.
+ *
+ * @param iter an iterator
+ * @param elem the element to insert
+ * @return zero on success, non-zero on memory allocation failure
+ * @see cxListInsert()
+ * @see cxListInsertBefore()
+ */
+__attribute__((__nonnull__))
+static inline int cxListInsertAfter(
+        CxMutIterator *iter,
+        void const *elem
+) {
+    return ((struct cx_list_s *) iter->src_handle)->cl->insert_iter(iter, elem, 0);
+}
+
+/**
+ * Inserts an element before the current location of the specified iterator.
+ *
+ * The used iterator remains operational, but all other active iterators should
+ * be considered invalidated.
+ *
+ * If \p iter is not a list iterator, the behavior is undefined.
+ * If \p iter is a past-the-end iterator, the new element gets appended to the list.
+ *
+ * @param iter an iterator
+ * @param elem the element to insert
+ * @return zero on success, non-zero on memory allocation failure
+ * @see cxListInsert()
+ * @see cxListInsertAfter()
+ */
+__attribute__((__nonnull__))
+static inline int cxListInsertBefore(
+        CxMutIterator *iter,
+        void const *elem
+) {
+    return ((struct cx_list_s *) iter->src_handle)->cl->insert_iter(iter, elem, 1);
+}
+
+/**
+ * Removes the element at the specified index.
+ *
+ * If an element destructor function is specified, it is called before
+ * removing the element.
+ *
+ * @param list the list
+ * @param index the index of the element
+ * @return zero on success, non-zero if the index is out of bounds
+ */
+__attribute__((__nonnull__))
+static inline int cxListRemove(
+        CxList *list,
+        size_t index
+) {
+    return list->cl->remove(list, index);
+}
+
+/**
+ * Removes all elements from this list.
+ *
+ * If an element destructor function is specified, it is called for each
+ * element before removing them.
+ *
+ * @param list the list
+ */
+__attribute__((__nonnull__))
+static inline void cxListClear(CxList *list) {
+    list->cl->clear(list);
+}
+
+/**
+ * Swaps two items in the list.
+ *
+ * Implementations should only allocate temporary memory for the swap, if
+ * it is necessary.
+ *
+ * @param list the list
+ * @param i the index of the first element
+ * @param j the index of the second element
+ * @return zero on success, non-zero if one of the indices is out of bounds
+ */
+__attribute__((__nonnull__))
+static inline int cxListSwap(
+        CxList *list,
+        size_t i,
+        size_t j
+) {
+    return list->cl->swap(list, i, j);
+}
+
+/**
+ * Returns a pointer to the element at the specified index.
+ *
+ * @param list the list
+ * @param index the index of the element
+ * @return a pointer to the element or \c NULL if the index is out of bounds
+ */
+__attribute__((__nonnull__))
+static inline void *cxListAt(
+        CxList *list,
+        size_t index
+) {
+    return list->cl->at(list, index);
+}
+
+/**
+ * Returns an iterator pointing to the item at the specified index.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the index is out of range, 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
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxListIteratorAt(
+        CxList const *list,
+        size_t index
+) {
+    return list->cl->iterator(list, index, false);
+}
+
+/**
+ * Returns a backwards iterator pointing to the item at the specified index.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the index is out of range, 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
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxListBackwardsIteratorAt(
+        CxList const *list,
+        size_t index
+) {
+    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, 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
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+CxMutIterator 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, 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
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+CxMutIterator cxListMutBackwardsIteratorAt(
+        CxList *list,
+        size_t index
+);
+
+/**
+ * Returns an iterator pointing to the first item of the list.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the list is empty, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxListIterator(CxList const *list) {
+    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, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxListMutIterator(CxList *list) {
+    return cxListMutIteratorAt(list, 0);
+}
+
+
+/**
+ * Returns a backwards iterator pointing to the last item of the list.
+ *
+ * The returned iterator is position-aware.
+ *
+ * If the list is empty, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxListBackwardsIterator(CxList const *list) {
+    return list->cl->iterator(list, list->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, a past-the-end iterator will be returned.
+ *
+ * @param list the list
+ * @return a new iterator
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxListMutBackwardsIterator(CxList *list) {
+    return cxListMutBackwardsIteratorAt(list, list->size - 1);
+}
+
+/**
+ * Returns the index of the first element that equals \p elem.
+ *
+ * Determining equality is performed by the list's comparator function.
+ *
+ * @param list the list
+ * @param elem the element to find
+ * @return the index of the element or \c (size+1) if the element is not found
+ */
+__attribute__((__nonnull__))
+static inline size_t cxListFind(
+        CxList const *list,
+        void const *elem
+) {
+    return list->cl->find(list, elem);
+}
+
+/**
+ * Sorts the list in place.
+ *
+ * \remark The underlying sort algorithm is implementation defined.
+ *
+ * @param list the list
+ */
+__attribute__((__nonnull__))
+static inline void cxListSort(CxList *list) {
+    list->cl->sort(list);
+}
+
+/**
+ * Reverses the order of the items.
+ *
+ * @param list the list
+ */
+__attribute__((__nonnull__))
+static inline void cxListReverse(CxList *list) {
+    list->cl->reverse(list);
+}
+
+/**
+ * Compares a list to another list of the same type.
+ *
+ * First, the list sizes are compared.
+ * If they match, the lists are compared element-wise.
+ *
+ * @param list the list
+ * @param other the list to compare to
+ * @return zero, if both lists are equal element wise,
+ * negative if the first list is smaller, positive if the first list is larger
+ */
+__attribute__((__nonnull__))
+int cxListCompare(
+        CxList const *list,
+        CxList const *other
+);
+
+/**
+ * Deallocates the memory of the specified list structure.
+ *
+ * Also calls content a destructor function, depending on the configuration
+ * in CxList.content_destructor_type.
+ *
+ * This function itself is a destructor function for the CxList.
+ *
+ * @param list the list which shall be destroyed
+ */
+__attribute__((__nonnull__))
+void cxListDestroy(CxList *list);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_LIST_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/map.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,1145 @@
+/*
+ * 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 map.h
+ * \brief Interface for map implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_MAP_H
+#define UCX_MAP_H
+
+#include "common.h"
+#include "collection.h"
+#include "string.h"
+#include "hash_key.h"
+
+#ifdef    __cplusplus
+extern "C" {
+#endif
+
+/** Type for the UCX map. */
+typedef struct cx_map_s CxMap;
+
+/** Type for a map entry. */
+typedef struct cx_map_entry_s CxMapEntry;
+
+/** Type for map class definitions. */
+typedef struct cx_map_class_s cx_map_class;
+
+/** Structure for the UCX map. */
+struct cx_map_s {
+    CX_COLLECTION_MEMBERS
+    /** The map class definition. */
+    cx_map_class *cl;
+};
+
+/**
+ * The class definition for arbitrary maps.
+ */
+struct cx_map_class_s {
+    /**
+     * Deallocates the entire memory.
+     */
+    __attribute__((__nonnull__))
+    void (*destructor)(struct cx_map_s *map);
+
+    /**
+     * Removes all elements.
+     */
+    __attribute__((__nonnull__))
+    void (*clear)(struct cx_map_s *map);
+
+    /**
+     * Add or overwrite an element.
+     */
+    __attribute__((__nonnull__))
+    int (*put)(
+            CxMap *map,
+            CxHashKey key,
+            void *value
+    );
+
+    /**
+     * Returns an element.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    void *(*get)(
+            CxMap const *map,
+            CxHashKey key
+    );
+
+    /**
+     * Removes an element.
+     */
+    __attribute__((__nonnull__))
+    void *(*remove)(
+            CxMap *map,
+            CxHashKey key,
+            bool destroy
+    );
+
+    /**
+     * Iterator over the key/value pairs.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxIterator (*iterator)(CxMap const *map);
+
+    /**
+     * Iterator over the keys.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxIterator (*iterator_keys)(CxMap const *map);
+
+    /**
+     * Iterator over the values.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxIterator (*iterator_values)(CxMap const *map);
+
+    /**
+     * Mutating iterator over the key/value pairs.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxMutIterator (*mut_iterator)(CxMap *map);
+
+    /**
+     * Mutating iterator over the keys.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxMutIterator (*mut_iterator_keys)(CxMap *map);
+
+    /**
+     * Mutating iterator over the values.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxMutIterator (*mut_iterator_values)(CxMap *map);
+};
+
+/**
+ * A map entry.
+ */
+struct cx_map_entry_s {
+    /**
+     * A pointer to the key.
+     */
+    CxHashKey const *key;
+    /**
+     * A pointer to the value.
+     */
+    void *value;
+};
+
+/**
+ * Advises the map to store copies of the objects (default mode of operation).
+ *
+ * Retrieving objects from this map will yield pointers to the copies stored
+ * within this list.
+ *
+ * @param map the map
+ * @see cxMapStorePointers()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapStoreObjects(CxMap *map) {
+    map->store_pointer = false;
+}
+
+/**
+ * Advises the map to only store pointers to the objects.
+ *
+ * Retrieving objects from this list will yield the original pointers stored.
+ *
+ * @note This function forcibly sets the element size to the size of a pointer.
+ * Invoking this function on a non-empty map that already stores copies of
+ * objects is undefined.
+ *
+ * @param map the map
+ * @see cxMapStoreObjects()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapStorePointers(CxMap *map) {
+    map->store_pointer = true;
+    map->item_size = sizeof(void *);
+}
+
+
+/**
+ * Deallocates the memory of the specified map.
+ *
+ * @param map the map to be destroyed
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDestroy(CxMap *map) {
+    map->cl->destructor(map);
+}
+
+
+/**
+ * Clears a map by removing all elements.
+ *
+ * @param map the map to be cleared
+ */
+__attribute__((__nonnull__))
+static inline void cxMapClear(CxMap *map) {
+    map->cl->clear(map);
+}
+
+
+// TODO: set-like map operations (union, intersect, difference)
+
+/**
+ * Creates a value iterator for a map.
+ *
+ * \note An iterator iterates over all elements successively. Therefore the order
+ * highly depends on the map implementation and may change arbitrarily when the contents change.
+ *
+ * @param map the map to create the iterator for
+ * @return an iterator for the currently stored values
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxMapIteratorValues(CxMap *map) {
+    return map->cl->iterator_values(map);
+}
+
+/**
+ * Creates a key iterator for a map.
+ *
+ * The elements of the iterator are keys of type 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
+ * @return an iterator for the currently stored keys
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxMapIteratorKeys(CxMap *map) {
+    return map->cl->iterator_keys(map);
+}
+
+/**
+ * Creates an iterator for a map.
+ *
+ * The elements of the iterator are key/value pairs of type 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
+ * @return an iterator for the currently stored entries
+ * @see cxMapIteratorKeys()
+ * @see cxMapIteratorValues()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxIterator cxMapIterator(CxMap *map) {
+    return map->cl->iterator(map);
+}
+
+
+/**
+ * Creates a mutating iterator over the values of a map.
+ *
+ * \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
+ * @return an iterator for the currently stored values
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxMapMutIteratorValues(CxMap *map) {
+    return map->cl->mut_iterator_values(map);
+}
+
+/**
+ * Creates a mutating iterator over the keys of a map.
+ *
+ * The elements of the iterator are keys of type 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
+ * @return an iterator for the currently stored keys
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxMapMutIteratorKeys(CxMap *map) {
+    return map->cl->mut_iterator_keys(map);
+}
+
+/**
+ * Creates a mutating iterator for a map.
+ *
+ * The elements of the iterator are key/value pairs of type 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
+ * @return an iterator for the currently stored entries
+ * @see cxMapMutIteratorKeys()
+ * @see cxMapMutIteratorValues()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline CxMutIterator cxMapMutIterator(CxMap *map) {
+    return map->cl->mut_iterator(map);
+}
+
+#ifdef __cplusplus
+} // end the extern "C" block here, because we want to start overloading
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+        CxMap *map,
+        CxHashKey const &key,
+        void *value
+) {
+    return map->cl->put(map, key, value);
+}
+
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+        CxMap *map,
+        cxstring const &key,
+        void *value
+) {
+    return map->cl->put(map, cx_hash_key_cxstr(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+        CxMap *map,
+        cxmutstr const &key,
+        void *value
+) {
+    return map->cl->put(map, cx_hash_key_cxstr(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cxMapPut(
+        CxMap *map,
+        char const *key,
+        void *value
+) {
+    return map->cl->put(map, cx_hash_key_str(key), value);
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+        CxMap const *map,
+        CxHashKey const &key
+) {
+    return map->cl->get(map, key);
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+        CxMap const *map,
+        cxstring const &key
+) {
+    return map->cl->get(map, cx_hash_key_cxstr(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+        CxMap const *map,
+        cxmutstr const &key
+) {
+    return map->cl->get(map, cx_hash_key_cxstr(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapGet(
+        CxMap const *map,
+        char const *key
+) {
+    return map->cl->get(map, cx_hash_key_str(key));
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+        CxMap *map,
+        CxHashKey const &key
+) {
+    (void) map->cl->remove(map, key, true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+        CxMap *map,
+        cxstring const &key
+) {
+    (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+        CxMap *map,
+        cxmutstr const &key
+) {
+    (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapRemove(
+        CxMap *map,
+        char const *key
+) {
+    (void) map->cl->remove(map, cx_hash_key_str(key), true);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDetach(
+        CxMap *map,
+        CxHashKey const &key
+) {
+    (void) map->cl->remove(map, key, false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDetach(
+        CxMap *map,
+        cxstring const &key
+) {
+    (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDetach(
+        CxMap *map,
+        cxmutstr const &key
+) {
+    (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDetach(
+        CxMap *map,
+        char const *key
+) {
+    (void) map->cl->remove(map, cx_hash_key_str(key), false);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+        CxMap *map,
+        CxHashKey key
+) {
+    return map->cl->remove(map, key, !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+        CxMap *map,
+        cxstring key
+) {
+    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+        CxMap *map,
+        cxmutstr key
+) {
+    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemoveAndGet(
+        CxMap *map,
+        char const *key
+) {
+    return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer);
+}
+
+#else // __cplusplus
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cx_map_put(
+        CxMap *map,
+        CxHashKey key,
+        void *value
+) {
+    return map->cl->put(map, key, value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cx_map_put_cxstr(
+        CxMap *map,
+        cxstring key,
+        void *value
+) {
+    return map->cl->put(map, cx_hash_key_cxstr(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cx_map_put_mustr(
+        CxMap *map,
+        cxmutstr key,
+        void *value
+) {
+    return map->cl->put(map, cx_hash_key_cxstr(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+__attribute__((__nonnull__))
+static inline int cx_map_put_str(
+        CxMap *map,
+        char const *key,
+        void *value
+) {
+    return map->cl->put(map, cx_hash_key_str(key), value);
+}
+
+/**
+ * Puts a key/value-pair into the map.
+ *
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+#define cxMapPut(map, key, value) _Generic((key), \
+    CxHashKey: cx_map_put,                        \
+    cxstring: cx_map_put_cxstr,                   \
+    cxmutstr: cx_map_put_mustr,                   \
+    char*: cx_map_put_str,                        \
+    char const*: cx_map_put_str)                  \
+    (map, key, value)
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_get(
+        CxMap const *map,
+        CxHashKey key
+) {
+    return map->cl->get(map, key);
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_get_cxstr(
+        CxMap const *map,
+        cxstring key
+) {
+    return map->cl->get(map, cx_hash_key_cxstr(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_get_mustr(
+        CxMap const *map,
+        cxmutstr key
+) {
+    return map->cl->get(map, cx_hash_key_cxstr(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_get_str(
+        CxMap const *map,
+        char const *key
+) {
+    return map->cl->get(map, cx_hash_key_str(key));
+}
+
+/**
+ * Retrieves a value by using a key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+#define cxMapGet(map, key) _Generic((key), \
+    CxHashKey: cx_map_get,                 \
+    cxstring: cx_map_get_cxstr,            \
+    cxmutstr: cx_map_get_mustr,            \
+    char*: cx_map_get_str,                 \
+    char const*: cx_map_get_str)           \
+    (map, key)
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_remove(
+        CxMap *map,
+        CxHashKey key
+) {
+    (void) map->cl->remove(map, key, true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_remove_cxstr(
+        CxMap *map,
+        cxstring key
+) {
+    (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_remove_mustr(
+        CxMap *map,
+        cxmutstr key
+) {
+    (void) map->cl->remove(map, cx_hash_key_cxstr(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_remove_str(
+        CxMap *map,
+        char const *key
+) {
+    (void) map->cl->remove(map, cx_hash_key_str(key), true);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * Always invokes the destructor function, if any, on the removed element.
+ * If this map is storing pointers and you just want to retrieve the pointer
+ * without invoking the destructor, use cxMapRemoveAndGet().
+ * If you just want to detach the element from the map without invoking the
+ * destructor or returning the element, use cxMapDetach().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemoveAndGet()
+ * @see cxMapDetach()
+ */
+#define cxMapRemove(map, key) _Generic((key), \
+    CxHashKey: cx_map_remove,                 \
+    cxstring: cx_map_remove_cxstr,            \
+    cxmutstr: cx_map_remove_mustr,            \
+    char*: cx_map_remove_str,                 \
+    char const*: cx_map_remove_str)           \
+    (map, key)
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_detach(
+        CxMap *map,
+        CxHashKey key
+) {
+    (void) map->cl->remove(map, key, false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_detach_cxstr(
+        CxMap *map,
+        cxstring key
+) {
+    (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_detach_mustr(
+        CxMap *map,
+        cxmutstr key
+) {
+    (void) map->cl->remove(map, cx_hash_key_cxstr(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * @param map the map
+ * @param key the key
+ */
+__attribute__((__nonnull__))
+static inline void cx_map_detach_str(
+        CxMap *map,
+        char const *key
+) {
+    (void) map->cl->remove(map, cx_hash_key_str(key), false);
+}
+
+/**
+ * Detaches a key/value-pair from the map by using the key
+ * without invoking the destructor.
+ *
+ * In general, you should only use this function if the map does not own
+ * the data and there is a valid reference to the data somewhere else
+ * in the program. In all other cases it is preferable to use
+ * cxMapRemove() or cxMapRemoveAndGet().
+ *
+ * @param map the map
+ * @param key the key
+ * @see cxMapRemove()
+ * @see cxMapRemoveAndGet()
+ */
+#define cxMapDetach(map, key) _Generic((key), \
+    CxHashKey: cx_map_detach,                 \
+    cxstring: cx_map_detach_cxstr,            \
+    cxmutstr: cx_map_detach_mustr,            \
+    char*: cx_map_detach_str,                 \
+    char const*: cx_map_detach_str)           \
+    (map, key)
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_remove_and_get(
+        CxMap *map,
+        CxHashKey key
+) {
+    return map->cl->remove(map, key, !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_remove_and_get_cxstr(
+        CxMap *map,
+        cxstring key
+) {
+    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_remove_and_get_mustr(
+        CxMap *map,
+        cxmutstr key
+) {
+    return map->cl->remove(map, cx_hash_key_cxstr(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cx_map_remove_and_get_str(
+        CxMap *map,
+        char const *key
+) {
+    return map->cl->remove(map, cx_hash_key_str(key), !map->store_pointer);
+}
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ *
+ * This function can be used when the map is storing pointers,
+ * in order to retrieve the pointer from the map without invoking
+ * any destructor function. Sometimes you do not want the pointer
+ * to be returned - in that case (instead of suppressing the "unused
+ * result" warning) you can use cxMapDetach().
+ *
+ * If this map is not storing pointers, this function behaves like
+ * cxMapRemove() and returns \c NULL.
+ *
+ * @param map the map
+ * @param key the key
+ * @return the stored pointer or \c NULL if either the key is not present
+ * in the map or the map is not storing pointers
+ * @see cxMapStorePointers()
+ * @see cxMapDetach()
+ */
+#define cxMapRemoveAndGet(map, key) _Generic((key), \
+    CxHashKey: cx_map_remove_and_get,               \
+    cxstring: cx_map_remove_and_get_cxstr,          \
+    cxmutstr: cx_map_remove_and_get_mustr,          \
+    char*: cx_map_remove_and_get_str,               \
+    char const*: cx_map_remove_and_get_str)         \
+    (map, key)
+
+#endif // __cplusplus
+
+#endif // UCX_MAP_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/mempool.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,122 @@
+/*
+ * 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 mempool.h
+ * \brief Interface for memory pool implementations.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_MEMPOOL_H
+#define UCX_MEMPOOL_H
+
+#include "common.h"
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Memory pool class type.
+ */
+typedef struct cx_mempool_class_s cx_mempool_class;
+
+/**
+ * The basic structure of a memory pool.
+ * Should be the first member of an actual memory pool implementation.
+ */
+struct cx_mempool_s {
+    /**
+     * The pool class definition.
+     */
+    cx_mempool_class *cl;
+    /**
+     * The provided allocator.
+     */
+    CxAllocator const *allocator;
+};
+
+/**
+ * Common type for all memory pool implementations.
+ */
+typedef struct cx_mempool_s CxMempool;
+
+/**
+ * The class definition for a memory pool.
+ */
+struct cx_mempool_class_s {
+    /** Member function for destroying the pool. */
+    __attribute__((__nonnull__))
+    void (*destroy)(CxMempool *pool);
+
+    /** Member function for setting a destructor. */
+    __attribute__((__nonnull__))
+    void (*set_destructor)(
+            CxMempool *pool,
+            void *memory,
+            cx_destructor_func fnc
+    );
+};
+
+
+/**
+ * Destroys a memory pool including their contents.
+ *
+ * @param pool the memory pool to destroy
+ */
+__attribute__((__nonnull__))
+static inline void cxMempoolDestroy(CxMempool *pool) {
+    pool->cl->destroy(pool);
+}
+
+/**
+ * Sets a destructor function for an allocated memory object.
+ *
+ * If the memory is not managed by the pool, the behavior is undefined.
+ *
+ * @param pool the pool
+ * @param memory the objected allocated in the pool
+ * @param fnc the destructor function
+ */
+__attribute__((__nonnull__))
+static inline void cxMempoolSetDestructor(
+        CxMempool *pool,
+        void *memory,
+        cx_destructor_func fnc
+) {
+    pool->cl->set_destructor(pool, memory, fnc);
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_MEMPOOL_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/printf.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,166 @@
+/*
+ * 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 printf.h
+ * \brief Wrapper for write functions with a printf-like interface.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_PRINTF_H
+#define UCX_PRINTF_H
+
+#include "common.h"
+#include "string.h"
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A \c fprintf like function which writes the output to a stream by
+ * using a write_func.
+ *
+ * @param stream the stream the data is written to
+ * @param wfc the write function
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return the total number of bytes written
+ */
+__attribute__((__nonnull__(1, 2, 3), __format__(printf, 3, 4)))
+int cx_fprintf(
+        void *stream,
+        cx_write_func wfc,
+        char const *fmt,
+        ...
+);
+
+/**
+ * A \c vfprintf like function which writes the output to a stream by
+ * using a write_func.
+ *
+ * @param stream the stream the data is written to
+ * @param wfc the write function
+ * @param fmt format string
+ * @param ap argument list
+ * @return the total number of bytes written
+ * @see cx_fprintf()
+ */
+__attribute__((__nonnull__))
+int cx_vfprintf(
+        void *stream,
+        cx_write_func wfc,
+        char const *fmt,
+        va_list ap
+);
+
+/**
+ * A \c asprintf like function which allocates space for a string
+ * the result is written to.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ *
+ * @param allocator the CxAllocator used for allocating the string
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return the formatted string
+ * @see cx_strfree_a()
+ */
+__attribute__((__nonnull__(1, 2), __format__(printf, 2, 3)))
+cxmutstr cx_asprintf_a(
+        CxAllocator const *allocator,
+        char const *fmt,
+        ...
+);
+
+/**
+ * A \c asprintf like function which allocates space for a string
+ * the result is written to.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ *
+ * @param fmt format string
+ * @param ... additional arguments
+ * @return the formatted string
+ * @see cx_strfree()
+ */
+#define cx_asprintf(fmt, ...) \
+    cx_asprintf_a(cxDefaultAllocator, fmt, __VA_ARGS__)
+
+/**
+* A \c vasprintf like function which allocates space for a string
+ * the result is written to.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ *
+ * @param allocator the CxAllocator used for allocating the string
+ * @param fmt format string
+ * @param ap argument list
+ * @return the formatted string
+ * @see cx_asprintf_a()
+ */
+__attribute__((__nonnull__))
+cxmutstr cx_vasprintf_a(
+        CxAllocator const *allocator,
+        char const *fmt,
+        va_list ap
+);
+
+/**
+* A \c vasprintf like function which allocates space for a string
+ * the result is written to.
+ *
+ * \note The resulting string is guaranteed to be zero-terminated.
+ *
+ * @param fmt format string
+ * @param ap argument list
+ * @return the formatted string
+ * @see cx_asprintf()
+ */
+#define cx_vasprintf(fmt, ap) cx_vasprintf_a(cxDefaultAllocator, fmt, ap)
+
+/**
+ * A \c printf like function which writes the output to a CxBuffer.
+ *
+ * @param buffer a pointer to the buffer the data is written to
+ * @param fmt the format string
+ * @param ... additional arguments
+ * @return the total number of bytes written
+ * @see ucx_fprintf()
+ */
+#define cx_bprintf(buffer, fmt, ...) cx_fprintf((CxBuffer*)buffer, \
+    (cx_write_func) cxBufferWrite, fmt, __VA_ARGS__)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //UCX_PRINTF_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/string.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,1007 @@
+/*
+ * 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 string.h
+ * \brief Strings that know their length.
+ * \author Mike Becker
+ * \author Olaf Wintermann
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_STRING_H
+#define UCX_STRING_H
+
+#include "common.h"
+#include "allocator.h"
+
+/**
+ * The UCX string structure.
+ */
+struct cx_mutstr_s {
+    /**
+     * A pointer to the string.
+     * \note The string is not necessarily \c NULL terminated.
+     * Always use the length.
+     */
+    char *ptr;
+    /** The length of the string */
+    size_t length;
+};
+
+/**
+ * A mutable string.
+ */
+typedef struct cx_mutstr_s cxmutstr;
+
+/**
+ * The UCX string structure for immutable (constant) strings.
+ */
+struct cx_string_s {
+    /**
+     * A pointer to the immutable string.
+     * \note The string is not necessarily \c NULL terminated.
+     * Always use the length.
+     */
+    char const *ptr;
+    /** The length of the string */
+    size_t length;
+};
+
+/**
+ * An immutable string.
+ */
+typedef struct cx_string_s cxstring;
+
+/**
+ * Context for string tokenizing.
+ */
+struct cx_strtok_ctx_s {
+    /**
+     * The string to tokenize.
+     */
+    cxstring str;
+    /**
+     * The primary delimiter.
+     */
+    cxstring delim;
+    /**
+     * Optional array of more delimiters.
+     */
+    cxstring const *delim_more;
+    /**
+     * Length of the array containing more delimiters.
+     */
+    size_t delim_more_count;
+    /**
+     * Position of the currently active token in the source string.
+     */
+    size_t pos;
+    /**
+     * Position of next delimiter in the source string.
+     *
+     * If the tokenizer has not yet returned a token, the content of this field
+     * is undefined. If the tokenizer reached the end of the string, this field
+     * contains the length of the source string.
+     */
+    size_t delim_pos;
+    /**
+     * The position of the next token in the source string.
+     */
+    size_t next_pos;
+    /**
+     * The number of already found tokens.
+     */
+    size_t found;
+    /**
+     * The maximum number of tokens that shall be returned.
+     */
+    size_t limit;
+};
+
+/**
+ * A string tokenizing context.
+ */
+typedef struct cx_strtok_ctx_s CxStrtokCtx;
+
+#ifdef __cplusplus
+extern "C" {
+
+/**
+ * A literal initializer for an UCX string structure.
+ *
+ * @param literal the string literal
+ */
+#define CX_STR(literal) cxstring{literal, sizeof(literal) - 1}
+
+#else // __cplusplus
+
+/**
+ * A literal initializer for an UCX string structure.
+ *
+ * The argument MUST be a string (const char*) \em literal.
+ *
+ * @param literal the string literal
+ */
+#define CX_STR(literal) (cxstring){literal, sizeof(literal) - 1}
+
+#endif
+
+
+/**
+ * Wraps a mutable string that must be zero-terminated.
+ *
+ * The length is implicitly inferred by using a call to \c strlen().
+ *
+ * \note the wrapped string will share the specified pointer to the string.
+ * If you do want a copy, use cx_strdup() on the return value of this function.
+ *
+ * If you need to wrap a constant string, use cx_str().
+ *
+ * @param cstring the string to wrap, must be zero-terminated
+ * @return the wrapped string
+ *
+ * @see cx_mutstrn()
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxmutstr cx_mutstr(char *cstring);
+
+/**
+ * Wraps a string that does not need to be zero-terminated.
+ *
+ * The argument may be \c NULL if the length is zero.
+ *
+ * \note the wrapped string will share the specified pointer to the string.
+ * If you do want a copy, use cx_strdup() on the return value of this function.
+ *
+ * If you need to wrap a constant string, use cx_strn().
+ *
+ * @param cstring  the string to wrap (or \c NULL, only if the length is zero)
+ * @param length   the length of the string
+ * @return the wrapped string
+ *
+ * @see cx_mutstr()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_mutstrn(
+        char *cstring,
+        size_t length
+);
+
+/**
+ * Wraps a string that must be zero-terminated.
+ *
+ * The length is implicitly inferred by using a call to \c strlen().
+ *
+ * \note the wrapped string will share the specified pointer to the string.
+ * If you do want a copy, use cx_strdup() on the return value of this function.
+ *
+ * If you need to wrap a non-constant string, use cx_mutstr().
+ *
+ * @param cstring the string to wrap, must be zero-terminated
+ * @return the wrapped string
+ *
+ * @see cx_strn()
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxstring cx_str(char const *cstring);
+
+
+/**
+ * Wraps a string that does not need to be zero-terminated.
+ *
+ * The argument may be \c NULL if the length is zero.
+ *
+ * \note the wrapped string will share the specified pointer to the string.
+ * If you do want a copy, use cx_strdup() on the return value of this function.
+ *
+ * If you need to wrap a non-constant string, use cx_mutstrn().
+ *
+ * @param cstring  the string to wrap (or \c NULL, only if the length is zero)
+ * @param length   the length of the string
+ * @return the wrapped string
+ *
+ * @see cx_str()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strn(
+        char const *cstring,
+        size_t length
+);
+
+/**
+* Casts a mutable string to an immutable string.
+*
+* \note This is not seriously a cast. Instead you get a copy
+* of the struct with the desired pointer type. Both structs still
+* point to the same location, though!
+*
+* @param str the mutable string to cast
+* @return an immutable copy of the string pointer
+*/
+__attribute__((__warn_unused_result__))
+cxstring cx_strcast(cxmutstr str);
+
+/**
+ * Passes the pointer in this string to \c free().
+ *
+ * The pointer in the struct is set to \c NULL and the length is set to zero.
+ *
+ * \note There is no implementation for cxstring, because it is unlikely that
+ * you ever have a \c char \c const* you are really supposed to free. If you
+ * encounter such situation, you should double-check your code.
+ *
+ * @param str the string to free
+ */
+__attribute__((__nonnull__))
+void cx_strfree(cxmutstr *str);
+
+/**
+ * Passes the pointer in this string to the allocators free function.
+ *
+ * The pointer in the struct is set to \c NULL and the length is set to zero.
+ *
+ * \note There is no implementation for cxstring, because it is unlikely that
+ * you ever have a \c char \c const* you are really supposed to free. If you
+ * encounter such situation, you should double-check your code.
+ *
+ * @param alloc the allocator
+ * @param str the string to free
+ */
+__attribute__((__nonnull__))
+void cx_strfree_a(
+        CxAllocator const *alloc,
+        cxmutstr *str
+);
+
+/**
+ * Returns the accumulated length of all specified strings.
+ *
+ * \attention if the count argument is larger than the number of the
+ * specified strings, the behavior is undefined.
+ *
+ * @param count    the total number of specified strings
+ * @param ...      all strings
+ * @return the accumulated length of all strings
+ */
+__attribute__((__warn_unused_result__))
+size_t cx_strlen(
+        size_t count,
+        ...
+);
+
+/**
+ * Concatenates two or more strings.
+ *
+ * The resulting string will be allocated by the specified allocator.
+  * So developers \em must pass the return value to cx_strfree() eventually.
+  *
+  * \note It is guaranteed that there is only one allocation.
+  * It is also guaranteed that the returned string is zero-terminated.
+ *
+ * @param alloc the allocator to use
+ * @param count   the total number of strings to concatenate
+ * @param ...     all strings
+ * @return the concatenated string
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxmutstr cx_strcat_a(
+        CxAllocator const *alloc,
+        size_t count,
+        ...
+);
+
+/**
+ * Concatenates two or more strings.
+ *
+ * The resulting string will be allocated by standard \c malloc().
+ * So developers \em must pass the return value to cx_strfree() eventually.
+ *
+ * \note It is guaranteed that there is only one allocation.
+ * It is also guaranteed that the returned string is zero-terminated.
+ *
+ * @param count   the total number of strings to concatenate
+ * @param ...     all strings
+ * @return the concatenated string
+ */
+#define cx_strcat(count, ...) \
+cx_strcat_a(cxDefaultAllocator, count, __VA_ARGS__)
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * \attention the new string references the same memory area as the
+ * input string and is usually \em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start  start location of the substring
+ * @return a substring of \p string starting at \p start
+ *
+ * @see cx_strsubsl()
+ * @see cx_strsubs_m()
+ * @see cx_strsubsl_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strsubs(
+        cxstring string,
+        size_t start
+);
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * The returned string will be limited to \p length bytes or the number
+ * of bytes available in \p string, whichever is smaller.
+ *
+ * \attention the new string references the same memory area as the
+ * input string and is usually \em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start  start location of the substring
+ * @param length the maximum length of the returned string
+ * @return a substring of \p string starting at \p start
+ *
+ * @see cx_strsubs()
+ * @see cx_strsubs_m()
+ * @see cx_strsubsl_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strsubsl(
+        cxstring string,
+        size_t start,
+        size_t length
+);
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * \attention the new string references the same memory area as the
+ * input string and is usually \em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start  start location of the substring
+ * @return a substring of \p string starting at \p start
+ *
+ * @see cx_strsubsl_m()
+ * @see cx_strsubs()
+ * @see cx_strsubsl()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strsubs_m(
+        cxmutstr string,
+        size_t start
+);
+
+/**
+ * Returns a substring starting at the specified location.
+ *
+ * The returned string will be limited to \p length bytes or the number
+ * of bytes available in \p string, whichever is smaller.
+ *
+ * \attention the new string references the same memory area as the
+ * input string and is usually \em not zero-terminated.
+ * Use cx_strdup() to get a copy.
+ *
+ * @param string input string
+ * @param start  start location of the substring
+ * @param length the maximum length of the returned string
+ * @return a substring of \p string starting at \p start
+ *
+ * @see cx_strsubs_m()
+ * @see cx_strsubs()
+ * @see cx_strsubsl()
+ */
+__attribute__((__warn_unused_result__))
+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
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr    the character to locate
+ * @return       a substring starting at the first location of \p chr
+ *
+ * @see cx_strchr_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strchr(
+        cxstring string,
+        int chr
+);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr    the character to locate
+ * @return       a substring starting at the first location of \p chr
+ *
+ * @see cx_strchr()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strchr_m(
+        cxmutstr string,
+        int chr
+);
+
+/**
+ * Returns a substring starting at the location of the last occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr    the character to locate
+ * @return       a substring starting at the last location of \p chr
+ *
+ * @see cx_strrchr_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strrchr(
+        cxstring string,
+        int chr
+);
+
+/**
+ * Returns a substring starting at the location of the last occurrence of the
+ * specified character.
+ *
+ * If the string does not contain the character, an empty string is returned.
+ *
+ * @param string the string where to locate the character
+ * @param chr    the character to locate
+ * @return       a substring starting at the last location of \p chr
+ *
+ * @see cx_strrchr()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strrchr_m(
+        cxmutstr string,
+        int chr
+);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified string.
+ *
+ * If \p haystack does not contain \p needle, an empty string is returned.
+ *
+ * If \p needle is an empty string, the complete \p haystack is
+ * returned.
+ *
+ * @param haystack the string to be scanned
+ * @param needle  string containing the sequence of characters to match
+ * @return       a substring starting at the first occurrence of
+ *               \p needle, or an empty string, if the sequence is not
+ *               contained
+ * @see cx_strstr_m()
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strstr(
+        cxstring haystack,
+        cxstring needle
+);
+
+/**
+ * Returns a substring starting at the location of the first occurrence of the
+ * specified string.
+ *
+ * If \p haystack does not contain \p needle, an empty string is returned.
+ *
+ * If \p needle is an empty string, the complete \p haystack is
+ * returned.
+ *
+ * @param haystack the string to be scanned
+ * @param needle  string containing the sequence of characters to match
+ * @return       a substring starting at the first occurrence of
+ *               \p needle, or an empty string, if the sequence is not
+ *               contained
+ * @see cx_strstr()
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strstr_m(
+        cxmutstr haystack,
+        cxstring needle
+);
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * \note The resulting array contains strings that point to the source
+ * \p string. Use cx_strdup() to get copies.
+ *
+ * @param string the string to split
+ * @param delim  the delimiter
+ * @param limit the maximum number of split items
+ * @param output a pre-allocated array of at least \p limit length
+ * @return the actual number of split items
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+size_t cx_strsplit(
+        cxstring string,
+        cxstring delim,
+        size_t limit,
+        cxstring *output
+);
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * The array pointed to by \p output will be allocated by \p allocator.
+ *
+ * \note The resulting array contains strings that point to the source
+ * \p string. Use cx_strdup() to get copies.
+ *
+ * \attention If allocation fails, the \c NULL pointer will be written to
+ * \p output and the number returned will be zero.
+ *
+ * @param allocator the allocator to use for allocating the resulting array
+ * @param string the string to split
+ * @param delim  the delimiter
+ * @param limit the maximum number of split items
+ * @param output a pointer where the address of the allocated array shall be
+ * written to
+ * @return the actual number of split items
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+size_t cx_strsplit_a(
+        CxAllocator const *allocator,
+        cxstring string,
+        cxstring delim,
+        size_t limit,
+        cxstring **output
+);
+
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * \note The resulting array contains strings that point to the source
+ * \p string. Use cx_strdup() to get copies.
+ *
+ * @param string the string to split
+ * @param delim  the delimiter
+ * @param limit the maximum number of split items
+ * @param output a pre-allocated array of at least \p limit length
+ * @return the actual number of split items
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+size_t cx_strsplit_m(
+        cxmutstr string,
+        cxstring delim,
+        size_t limit,
+        cxmutstr *output
+);
+
+/**
+ * Splits a given string using a delimiter string.
+ *
+ * The array pointed to by \p output will be allocated by \p allocator.
+ *
+ * \note The resulting array contains strings that point to the source
+ * \p string. Use cx_strdup() to get copies.
+ *
+ * \attention If allocation fails, the \c NULL pointer will be written to
+ * \p output and the number returned will be zero.
+ *
+ * @param allocator the allocator to use for allocating the resulting array
+ * @param string the string to split
+ * @param delim  the delimiter
+ * @param limit the maximum number of split items
+ * @param output a pointer where the address of the allocated array shall be
+ * written to
+ * @return the actual number of split items
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+size_t cx_strsplit_ma(
+        CxAllocator const *allocator,
+        cxmutstr string,
+        cxstring delim,
+        size_t limit,
+        cxmutstr **output
+);
+
+/**
+ * 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
+ */
+__attribute__((__warn_unused_result__))
+int cx_strcmp(
+        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
+ */
+__attribute__((__warn_unused_result__))
+int cx_strcasecmp(
+        cxstring s1,
+        cxstring s2
+);
+
+/**
+ * Compares two strings.
+ *
+ * This function has a compatible signature for the use as a cx_compare_func.
+ *
+ * @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
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+int cx_strcmp_p(
+        void const *s1,
+        void const *s2
+);
+
+/**
+ * Compares two strings ignoring case.
+ *
+ * This function has a compatible signature for the use as a cx_compare_func.
+ *
+ * @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
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+int cx_strcasecmp_p(
+        void const *s1,
+        void const *s2
+);
+
+
+/**
+ * Creates a duplicate of the specified string.
+ *
+ * The new string will contain a copy allocated by \p allocator.
+ *
+ * \note The returned string is guaranteed to be zero-terminated.
+ *
+ * @param allocator the allocator to use
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see cx_strdup()
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxmutstr cx_strdup_a(
+        CxAllocator const *allocator,
+        cxstring string
+);
+
+/**
+ * Creates a duplicate of the specified string.
+ *
+ * The new string will contain a copy allocated by standard
+ * \c malloc(). So developers \em must pass the return value to cx_strfree().
+ *
+ * \note The returned string is guaranteed to be zero-terminated.
+ *
+ * @param string the string to duplicate
+ * @return a duplicate of the string
+ * @see cx_strdup_a()
+ */
+#define cx_strdup(string) cx_strdup_a(cxDefaultAllocator, string)
+
+/**
+ * Omits leading and trailing spaces.
+ *
+ * \note the returned string references the same memory, thus you
+ * must \em not free the returned memory.
+ *
+ * @param string the string that shall be trimmed
+ * @return the trimmed string
+ */
+__attribute__((__warn_unused_result__))
+cxstring cx_strtrim(cxstring string);
+
+/**
+ * Omits leading and trailing spaces.
+ *
+ * \note the returned string references the same memory, thus you
+ * must \em not free the returned memory.
+ *
+ * @param string the string that shall be trimmed
+ * @return the trimmed string
+ */
+__attribute__((__warn_unused_result__))
+cxmutstr cx_strtrim_m(cxmutstr string);
+
+/**
+ * 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
+ */
+__attribute__((__warn_unused_result__))
+bool cx_strprefix(
+        cxstring string,
+        cxstring prefix
+);
+
+/**
+ * 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
+ */
+__attribute__((__warn_unused_result__))
+bool cx_strsuffix(
+        cxstring string,
+        cxstring suffix
+);
+
+/**
+ * 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
+ */
+__attribute__((__warn_unused_result__))
+bool cx_strcaseprefix(
+        cxstring string,
+        cxstring prefix
+);
+
+/**
+ * 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
+ */
+__attribute__((__warn_unused_result__))
+bool cx_strcasesuffix(
+        cxstring string,
+        cxstring suffix
+);
+
+/**
+ * Converts the string to lower case.
+ *
+ * The change is made in-place. If you want a copy, use cx_strdup(), first.
+ *
+ * @param string the string to modify
+ * @see cx_strdup()
+ */
+void cx_strlower(cxmutstr string);
+
+/**
+ * Converts the string to upper case.
+ *
+ * The change is made in-place. If you want a copy, use cx_strdup(), first.
+ *
+ * @param string the string to modify
+ * @see cx_strdup()
+ */
+void cx_strupper(cxmutstr string);
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most \p replmax occurrences.
+ *
+ * The returned string will be allocated by \p allocator and is guaranteed
+ * to be zero-terminated.
+ *
+ * If allocation fails, or the input string is empty,
+ * the returned string will be empty.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+cxmutstr cx_strreplacen_a(
+        CxAllocator const *allocator,
+        cxstring str,
+        cxstring pattern,
+        cxstring replacement,
+        size_t replmax
+);
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most \p replmax occurrences.
+ *
+ * The returned string will be allocated by \c malloc() and is guaranteed
+ * to be zero-terminated.
+ *
+ * If allocation fails, or the input string is empty,
+ * the returned string will be empty.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+#define cx_strreplacen(str, pattern, replacement, replmax) \
+cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, replmax)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ *
+ * The returned string will be allocated by \p allocator and is guaranteed
+ * to be zero-terminated.
+ *
+ * If allocation fails, or the input string is empty,
+ * the returned string will be empty.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @return the resulting string after applying the replacements
+ */
+#define cx_strreplace_a(allocator, str, pattern, replacement) \
+cx_strreplacen_a(allocator, str, pattern, replacement, SIZE_MAX)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most \p replmax occurrences.
+ *
+ * The returned string will be allocated by \c malloc() and is guaranteed
+ * to be zero-terminated.
+ *
+ * If allocation fails, or the input string is empty,
+ * the returned string will be empty.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @return the resulting string after applying the replacements
+ */
+#define cx_strreplace(str, pattern, replacement) \
+cx_strreplacen_a(cxDefaultAllocator, str, pattern, replacement, SIZE_MAX)
+
+/**
+ * Creates a string tokenization context.
+ *
+ * @param str the string to tokenize
+ * @param delim the delimiter (must not be empty)
+ * @param limit the maximum number of tokens that shall be returned
+ * @return a new string tokenization context
+ */
+__attribute__((__warn_unused_result__))
+CxStrtokCtx cx_strtok(
+        cxstring str,
+        cxstring delim,
+        size_t limit
+);
+
+/**
+* Creates a string tokenization context for a mutable string.
+*
+* @param str the string to tokenize
+* @param delim the delimiter (must not be empty)
+* @param limit the maximum number of tokens that shall be returned
+* @return a new string tokenization context
+*/
+__attribute__((__warn_unused_result__))
+CxStrtokCtx cx_strtok_m(
+        cxmutstr str,
+        cxstring delim,
+        size_t limit
+);
+
+/**
+ * Returns the next token.
+ *
+ * The token will point to the source string.
+ *
+ * @param ctx the tokenization context
+ * @param token a pointer to memory where the next token shall be stored
+ * @return true if successful, false if the limit or the end of the string
+ * has been reached
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+bool cx_strtok_next(
+        CxStrtokCtx *ctx,
+        cxstring *token
+);
+
+/**
+ * Returns the next token of a mutable string.
+ *
+ * The token will point to the source string.
+ * If the context was not initialized over a mutable string, modifying
+ * the data of the returned token is undefined behavior.
+ *
+ * @param ctx the tokenization context
+ * @param token a pointer to memory where the next token shall be stored
+ * @return true if successful, false if the limit or the end of the string
+ * has been reached
+ */
+__attribute__((__warn_unused_result__, __nonnull__))
+bool cx_strtok_next_m(
+        CxStrtokCtx *ctx,
+        cxmutstr *token
+);
+
+/**
+ * Defines an array of more delimiters for the specified tokenization context.
+ *
+ * @param ctx the tokenization context
+ * @param delim array of more delimiters
+ * @param count number of elements in the array
+ */
+__attribute__((__nonnull__))
+void cx_strtok_delim(
+        CxStrtokCtx *ctx,
+        cxstring const *delim,
+        size_t count
+);
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //UCX_STRING_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/tree.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,136 @@
+/*
+ * 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 tree.h
+ * \brief Interface for tree implementations.
+ * \author Olaf Wintermann
+ * \author Mike Becker
+ * \version 3.0
+ * \copyright 2-Clause BSD License
+ */
+
+#ifndef UCX_TREE_H
+#define UCX_TREE_H
+
+#include "common.h"
+#include "allocator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Adds a sibling to the current tree node.
+ *
+ * In case your struct does not have a \p prev or a \p parent pointer,
+ * specify a negative location. The location of the \p next pointer is
+ * mandatory.
+ *
+ * \attention Do not use this function to add siblings in a tree where the
+ * nodes store a pointer to the last sibling because that would not be modified by this function.
+ *
+ * \remark If yo do not provide a location to the parent pointer, a call to this function is
+ * effectively the same as a call to cx_linked_list_add().
+ *
+ * @param node a pointer to the node
+ * @param loc_prev the location of a \c prev pointer within your node struct
+ * @param loc_next the location of a \c next pointer within your node struct
+ * @param loc_parent the location of a \c parent pointer within your node struct
+ * @param new_node the new node that shall be added as a sibling
+ */
+void cx_tree_add_sibling(void *node,
+                         ptrdiff_t loc_prev, ptrdiff_t loc_next,
+                         ptrdiff_t loc_parent,
+                         void *new_node)
+__attribute__((__nonnull__));
+
+/**
+ * Adds a node to the list of children.
+ *
+ * \par Example with a full structure
+ * A full tree node structure may look like this:
+ * \code
+ * typedef struct MyTreeNode MyTreeNode;
+ * struct MyTreeNode {
+ *   MyTreeNode* parent;
+ *   MyTreeNode* first_child;
+ *   MyTreeNode* last_child;
+ *   MyTreeNode* prev_sibling;
+ *   MyTreeNode* next_sibling;
+ *   // ...contents...
+ * }
+ * \endcode
+ * Adding a new child to a node with the above structure can be performed with the following call:
+ * \code
+ * MyTreeNode *node, *child; // given
+ * cx_tree_add_child(&node->first_child, &node->last_child,
+ *                   offsetof(MyTreeNode, prev_sibling), offsetof(MyTreeNode, next_sibling),
+ *                   child, offsetof(MyTreeNode, parent), node);
+ * \endcode
+ *
+ * \par Example with a reduced structure
+ * The minimal reasonable structure with parent pointer looks like this:
+ * \code
+ * typedef struct MyTreeNode MyTreeNode;
+ * struct MyTreeNode {
+ *   MyTreeNode* parent;
+ *   MyTreeNode* children;
+ *   MyTreeNode* next_sibling;
+ *   // ...contents...
+ * }
+ * \endcode
+ * This simplifies the function call to:
+ * \code
+ * MyTreeNode *node, *child; // given
+ * cx_tree_add_child(&node->children, NULL, -1, offsetof(MyTreeNode, next_sibling),
+ *                   child, offsetof(MyTreeNode, parent), node);
+ * \endcode
+ *
+ * \remark If your tree structure does not possess a parent pointer, a call to this function is
+ * effectively the same as a call to cx_linked_list_add().
+ *
+ * @param children_begin a pointer to the begin node pointer (if your list has one)
+ * @param children_end a pointer to the end node pointer (if your list has one)
+ * @param loc_prev the location of a \c prev pointer within your node struct
+ * @param loc_next the location of a \c next pointer within your node struct
+ * @param new_node a pointer to the node that shall be appended
+ * @param loc_parent the location of a \c parent pointer within your node struct
+ * @param parent the parent node
+ */
+void cx_tree_add_child(void **children_begin, void **children_end,
+                       ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node,
+                       ptrdiff_t loc_parent, void *parent)
+__attribute__((__nonnull__ (5)));
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // UCX_TREE_H
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/cx/utils.h	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,196 @@
+/*
+ * 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
+ * \version 3.0
+ * \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
+ * @param n the maximum number of bytes that shall be copied.
+ * @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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/hash_key.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#include "cx/hash_key.h"
+#include <string.h>
+
+void cx_hash_murmur(CxHashKey *key) {
+    unsigned char const *data = key->data;
+    if (data == NULL) {
+        // extension: special value for NULL
+        key->hash = 1574210520u;
+        return;
+    }
+    size_t len = key->len;
+
+    unsigned m = 0x5bd1e995;
+    unsigned r = 24;
+    unsigned h = 25 ^ len;
+    unsigned i = 0;
+    while (len >= 4) {
+        unsigned k = data[i + 0] & 0xFF;
+        k |= (data[i + 1] & 0xFF) << 8;
+        k |= (data[i + 2] & 0xFF) << 16;
+        k |= (data[i + 3] & 0xFF) << 24;
+
+        k *= m;
+        k ^= k >> r;
+        k *= m;
+
+        h *= m;
+        h ^= k;
+
+        i += 4;
+        len -= 4;
+    }
+
+    switch (len) {
+        case 3:
+            h ^= (data[i + 2] & 0xFF) << 16;
+                    __attribute__((__fallthrough__));
+        case 2:
+            h ^= (data[i + 1] & 0xFF) << 8;
+                    __attribute__((__fallthrough__));
+        case 1:
+            h ^= (data[i + 0] & 0xFF);
+            h *= m;
+                    __attribute__((__fallthrough__));
+        default: // do nothing
+            ;
+    }
+
+    h ^= h >> 13;
+    h *= m;
+    h ^= h >> 15;
+
+    key->hash = h;
+}
+
+CxHashKey cx_hash_key_str(char const *str) {
+    CxHashKey key;
+    key.data = str;
+    key.len = str == NULL ? 0 : strlen(str);
+    cx_hash_murmur(&key);
+    return key;
+}
+
+CxHashKey cx_hash_key_bytes(
+        unsigned char const *bytes,
+        size_t len
+) {
+    CxHashKey key;
+    key.data = bytes;
+    key.len = len;
+    cx_hash_murmur(&key);
+    return key;
+}
+
+CxHashKey cx_hash_key(
+        void const *obj,
+        size_t len
+) {
+    CxHashKey key;
+    key.data = obj;
+    key.len = len;
+    cx_hash_murmur(&key);
+    return key;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/hash_map.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,509 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+#include "cx/hash_map.h"
+#include "cx/utils.h"
+
+struct cx_hash_map_element_s {
+    /** A pointer to the next element in the current bucket. */
+    struct cx_hash_map_element_s *next;
+
+    /** The corresponding key. */
+    CxHashKey key;
+
+    /** The value data. */
+    char data[];
+};
+
+static void cx_hash_map_clear(struct cx_map_s *map) {
+    struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+    cx_for_n(i, hash_map->bucket_count) {
+        struct cx_hash_map_element_s *elem = hash_map->buckets[i];
+        if (elem != NULL) {
+            do {
+                struct cx_hash_map_element_s *next = elem->next;
+                // invoke the destructor
+                cx_invoke_destructor(map, elem->data);
+                // free the key data
+                cxFree(map->allocator, (void *) elem->key.data);
+                // free the node
+                cxFree(map->allocator, elem);
+                // proceed
+                elem = next;
+            } while (elem != NULL);
+
+            // do not leave a dangling pointer
+            hash_map->buckets[i] = NULL;
+        }
+    }
+    map->size = 0;
+}
+
+static void cx_hash_map_destructor(struct cx_map_s *map) {
+    struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+
+    // free the buckets
+    cx_hash_map_clear(map);
+    cxFree(map->allocator, hash_map->buckets);
+
+    // free the map structure
+    cxFree(map->allocator, map);
+}
+
+static int cx_hash_map_put(
+        CxMap *map,
+        CxHashKey key,
+        void *value
+) {
+    struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+    CxAllocator const *allocator = map->allocator;
+
+    unsigned hash = key.hash;
+    if (hash == 0) {
+        cx_hash_murmur(&key);
+        hash = key.hash;
+    }
+
+    size_t slot = hash % hash_map->bucket_count;
+    struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
+    struct cx_hash_map_element_s *prev = NULL;
+
+    while (elm != NULL && elm->key.hash < hash) {
+        prev = elm;
+        elm = elm->next;
+    }
+
+    if (elm != NULL && elm->key.hash == hash && elm->key.len == key.len &&
+        memcmp(elm->key.data, key.data, key.len) == 0) {
+        // overwrite existing element
+        if (map->store_pointer) {
+            memcpy(elm->data, &value, sizeof(void *));
+        } else {
+            memcpy(elm->data, value, map->item_size);
+        }
+    } else {
+        // allocate new element
+        struct cx_hash_map_element_s *e = cxMalloc(
+                allocator,
+                sizeof(struct cx_hash_map_element_s) + map->item_size
+        );
+        if (e == NULL) {
+            return -1;
+        }
+
+        // write the value
+        if (map->store_pointer) {
+            memcpy(e->data, &value, sizeof(void *));
+        } else {
+            memcpy(e->data, value, map->item_size);
+        }
+
+        // copy the key
+        void *kd = cxMalloc(allocator, key.len);
+        if (kd == NULL) {
+            return -1;
+        }
+        memcpy(kd, key.data, key.len);
+        e->key.data = kd;
+        e->key.len = key.len;
+        e->key.hash = hash;
+
+        // insert the element into the linked list
+        if (prev == NULL) {
+            hash_map->buckets[slot] = e;
+        } else {
+            prev->next = e;
+        }
+        e->next = elm;
+
+        // increase the size
+        map->size++;
+    }
+
+    return 0;
+}
+
+static void cx_hash_map_unlink(
+        struct cx_hash_map_s *hash_map,
+        size_t slot,
+        struct cx_hash_map_element_s *prev,
+        struct cx_hash_map_element_s *elm
+) {
+    // unlink
+    if (prev == NULL) {
+        hash_map->buckets[slot] = elm->next;
+    } else {
+        prev->next = elm->next;
+    }
+    // free element
+    cxFree(hash_map->base.allocator, (void *) elm->key.data);
+    cxFree(hash_map->base.allocator, elm);
+    // decrease size
+    hash_map->base.size--;
+}
+
+/**
+ * Helper function to avoid code duplication.
+ *
+ * @param map the map
+ * @param key the key to look up
+ * @param remove flag indicating whether the looked up entry shall be removed
+ * @param destroy flag indicating whether the destructor shall be invoked
+ * @return a pointer to the value corresponding to the key or \c NULL
+ */
+static void *cx_hash_map_get_remove(
+        CxMap *map,
+        CxHashKey key,
+        bool remove,
+        bool destroy
+) {
+    struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+
+    unsigned hash = key.hash;
+    if (hash == 0) {
+        cx_hash_murmur(&key);
+        hash = key.hash;
+    }
+
+    size_t slot = hash % hash_map->bucket_count;
+    struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
+    struct cx_hash_map_element_s *prev = NULL;
+    while (elm && elm->key.hash <= hash) {
+        if (elm->key.hash == hash && elm->key.len == key.len) {
+            if (memcmp(elm->key.data, key.data, key.len) == 0) {
+                void *data = NULL;
+                if (destroy) {
+                    cx_invoke_destructor(map, elm->data);
+                } else {
+                    if (map->store_pointer) {
+                        data = *(void **) elm->data;
+                    } else {
+                        data = elm->data;
+                    }
+                }
+                if (remove) {
+                    cx_hash_map_unlink(hash_map, slot, prev, elm);
+                }
+                return data;
+            }
+        }
+        prev = elm;
+        elm = prev->next;
+    }
+
+    return NULL;
+}
+
+static void *cx_hash_map_get(
+        CxMap const *map,
+        CxHashKey key
+) {
+    // we can safely cast, because we know the map stays untouched
+    return cx_hash_map_get_remove((CxMap *) map, key, false, false);
+}
+
+static void *cx_hash_map_remove(
+        CxMap *map,
+        CxHashKey key,
+        bool destroy
+) {
+    return cx_hash_map_get_remove(map, key, true, destroy);
+}
+
+static void *cx_hash_map_iter_current_entry(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    // struct has to have a compatible signature
+    return (struct cx_map_entry_s *) &(iter->kv_data);
+}
+
+static void *cx_hash_map_iter_current_key(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    struct cx_hash_map_element_s *elm = iter->elem_handle;
+    return &elm->key;
+}
+
+static void *cx_hash_map_iter_current_value(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    struct cx_hash_map_s const *map = iter->src_handle;
+    struct cx_hash_map_element_s *elm = iter->elem_handle;
+    if (map->base.store_pointer) {
+        return *(void **) elm->data;
+    } else {
+        return elm->data;
+    }
+}
+
+static bool cx_hash_map_iter_valid(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    return iter->elem_handle != NULL;
+}
+
+static void cx_hash_map_iter_next(void *it) {
+    struct cx_iterator_s *iter = it;
+    struct cx_hash_map_element_s *elm = iter->elem_handle;
+
+    // remove current element, if asked
+    if (iter->base.remove) {
+        // obtain mutable pointer to the map
+        struct cx_mut_iterator_s *miter = it;
+        struct cx_hash_map_s *map = miter->src_handle;
+
+        // clear the flag
+        iter->base.remove = false;
+
+        // determine the next element
+        struct cx_hash_map_element_s *next = elm->next;
+
+        // search the previous element
+        struct cx_hash_map_element_s *prev = NULL;
+        if (map->buckets[iter->slot] != elm) {
+            prev = map->buckets[iter->slot];
+            while (prev->next != elm) {
+                prev = prev->next;
+            }
+        }
+
+        // destroy
+        cx_invoke_destructor((struct cx_map_s *) map, elm->data);
+
+        // unlink
+        cx_hash_map_unlink(map, iter->slot, prev, elm);
+
+        // advance
+        elm = next;
+    } else {
+        // just advance
+        elm = elm->next;
+        iter->index++;
+    }
+
+    // search the next bucket, if required
+    struct cx_hash_map_s const *map = iter->src_handle;
+    while (elm == NULL && ++iter->slot < map->bucket_count) {
+        elm = map->buckets[iter->slot];
+    }
+
+    // fill the struct with the next element
+    iter->elem_handle = elm;
+    if (elm == NULL) {
+        iter->kv_data.key = NULL;
+        iter->kv_data.value = NULL;
+    } else {
+        iter->kv_data.key = &elm->key;
+        if (map->base.store_pointer) {
+            iter->kv_data.value = *(void **) elm->data;
+        } else {
+            iter->kv_data.value = elm->data;
+        }
+    }
+}
+
+static bool cx_hash_map_iter_flag_rm(void *it) {
+    struct cx_iterator_base_s *iter = it;
+    if (iter->mutating) {
+        iter->remove = true;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static CxIterator cx_hash_map_iterator(CxMap const *map) {
+    CxIterator iter;
+
+    iter.src_handle = map;
+    iter.base.valid = cx_hash_map_iter_valid;
+    iter.base.next = cx_hash_map_iter_next;
+    iter.base.current = cx_hash_map_iter_current_entry;
+    iter.base.flag_removal = cx_hash_map_iter_flag_rm;
+    iter.base.remove = false;
+    iter.base.mutating = false;
+
+    iter.slot = 0;
+    iter.index = 0;
+
+    if (map->size > 0) {
+        struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+        struct cx_hash_map_element_s *elm = hash_map->buckets[0];
+        while (elm == NULL) {
+            elm = hash_map->buckets[++iter.slot];
+        }
+        iter.elem_handle = elm;
+        iter.kv_data.key = &elm->key;
+        if (map->store_pointer) {
+            iter.kv_data.value = *(void **) elm->data;
+        } else {
+            iter.kv_data.value = elm->data;
+        }
+    } else {
+        iter.elem_handle = NULL;
+        iter.kv_data.key = NULL;
+        iter.kv_data.value = NULL;
+    }
+
+    return iter;
+}
+
+static CxIterator cx_hash_map_iterator_keys(CxMap const *map) {
+    CxIterator iter = cx_hash_map_iterator(map);
+    iter.base.current = cx_hash_map_iter_current_key;
+    return iter;
+}
+
+static CxIterator cx_hash_map_iterator_values(CxMap const *map) {
+    CxIterator iter = cx_hash_map_iterator(map);
+    iter.base.current = cx_hash_map_iter_current_value;
+    return iter;
+}
+
+static CxMutIterator cx_hash_map_mut_iterator(CxMap *map) {
+    CxIterator it = cx_hash_map_iterator(map);
+    it.base.mutating = true;
+
+    // we know the iterators share the same memory layout
+    CxMutIterator iter;
+    memcpy(&iter, &it, sizeof(CxMutIterator));
+    return iter;
+}
+
+static CxMutIterator cx_hash_map_mut_iterator_keys(CxMap *map) {
+    CxMutIterator iter = cx_hash_map_mut_iterator(map);
+    iter.base.current = cx_hash_map_iter_current_key;
+    return iter;
+}
+
+static CxMutIterator cx_hash_map_mut_iterator_values(CxMap *map) {
+    CxMutIterator iter = cx_hash_map_mut_iterator(map);
+    iter.base.current = cx_hash_map_iter_current_value;
+    return iter;
+}
+
+static cx_map_class cx_hash_map_class = {
+        cx_hash_map_destructor,
+        cx_hash_map_clear,
+        cx_hash_map_put,
+        cx_hash_map_get,
+        cx_hash_map_remove,
+        cx_hash_map_iterator,
+        cx_hash_map_iterator_keys,
+        cx_hash_map_iterator_values,
+        cx_hash_map_mut_iterator,
+        cx_hash_map_mut_iterator_keys,
+        cx_hash_map_mut_iterator_values,
+};
+
+CxMap *cxHashMapCreate(
+        CxAllocator const *allocator,
+        size_t itemsize,
+        size_t buckets
+) {
+    if (buckets == 0) {
+        // implementation defined default
+        buckets = 16;
+    }
+
+    struct cx_hash_map_s *map = cxCalloc(allocator, 1,
+                                         sizeof(struct cx_hash_map_s));
+    if (map == NULL) return NULL;
+
+    // initialize hash map members
+    map->bucket_count = buckets;
+    map->buckets = cxCalloc(allocator, buckets,
+                            sizeof(struct cx_hash_map_element_s *));
+    if (map->buckets == NULL) {
+        cxFree(allocator, map);
+        return NULL;
+    }
+
+    // initialize base members
+    map->base.cl = &cx_hash_map_class;
+    map->base.allocator = allocator;
+
+    if (itemsize > 0) {
+        map->base.store_pointer = false;
+        map->base.item_size = itemsize;
+    } else {
+        map->base.store_pointer = true;
+        map->base.item_size = sizeof(void *);
+    }
+
+    return (CxMap *) map;
+}
+
+int cxMapRehash(CxMap *map) {
+    struct cx_hash_map_s *hash_map = (struct cx_hash_map_s *) map;
+    if (map->size > ((hash_map->bucket_count * 3) >> 2)) {
+
+        size_t new_bucket_count = (map->size * 5) >> 1;
+        struct cx_hash_map_element_s **new_buckets = cxCalloc(
+                map->allocator,
+                new_bucket_count, sizeof(struct cx_hash_map_element_s *)
+        );
+
+        if (new_buckets == NULL) {
+            return 1;
+        }
+
+        // iterate through the elements and assign them to their new slots
+        cx_for_n(slot, hash_map->bucket_count) {
+            struct cx_hash_map_element_s *elm = hash_map->buckets[slot];
+            while (elm != NULL) {
+                struct cx_hash_map_element_s *next = elm->next;
+                size_t new_slot = elm->key.hash % new_bucket_count;
+
+                // find position where to insert
+                struct cx_hash_map_element_s *bucket_next = new_buckets[new_slot];
+                struct cx_hash_map_element_s *bucket_prev = NULL;
+                while (bucket_next != NULL &&
+                       bucket_next->key.hash < elm->key.hash) {
+                    bucket_prev = bucket_next;
+                    bucket_next = bucket_next->next;
+                }
+
+                // insert
+                if (bucket_prev == NULL) {
+                    elm->next = new_buckets[new_slot];
+                    new_buckets[new_slot] = elm;
+                } else {
+                    bucket_prev->next = elm;
+                    elm->next = bucket_next;
+                }
+
+                // advance
+                elm = next;
+            }
+        }
+
+        // assign result to the map
+        hash_map->bucket_count = new_bucket_count;
+        cxFree(map->allocator, hash_map->buckets);
+        hash_map->buckets = new_buckets;
+    }
+    return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/linked_list.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,919 @@
+/*
+ * 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.
+ */
+
+#include "cx/linked_list.h"
+#include "cx/utils.h"
+#include <string.h>
+#include <assert.h>
+
+// LOW LEVEL LINKED LIST FUNCTIONS
+
+#define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off)))
+#define ll_prev(node) CX_LL_PTR(node, loc_prev)
+#define ll_next(node) CX_LL_PTR(node, loc_next)
+#define ll_advance(node) CX_LL_PTR(node, loc_advance)
+#define ll_data(node) (((char*)(node))+loc_data)
+
+void *cx_linked_list_at(
+        void const *start,
+        size_t start_index,
+        ptrdiff_t loc_advance,
+        size_t index
+) {
+    assert(start != NULL);
+    assert(loc_advance >= 0);
+    size_t i = start_index;
+    void const *cur = start;
+    while (i != index && cur != NULL) {
+        cur = ll_advance(cur);
+        i < index ? i++ : i--;
+    }
+    return (void *) cur;
+}
+
+size_t cx_linked_list_find(
+        void const *start,
+        ptrdiff_t loc_advance,
+        ptrdiff_t loc_data,
+        cx_compare_func cmp_func,
+        void const *elem
+) {
+    assert(start != NULL);
+    assert(loc_advance >= 0);
+    assert(loc_data >= 0);
+    assert(cmp_func);
+
+    void const *node = start;
+    size_t index = 0;
+    do {
+        void *current = ll_data(node);
+        if (cmp_func(current, elem) == 0) {
+            return index;
+        }
+        node = ll_advance(node);
+        index++;
+    } while (node != NULL);
+    return index;
+}
+
+void *cx_linked_list_first(
+        void const *node,
+        ptrdiff_t loc_prev
+) {
+    return cx_linked_list_last(node, loc_prev);
+}
+
+void *cx_linked_list_last(
+        void const *node,
+        ptrdiff_t loc_next
+) {
+    assert(node != NULL);
+    assert(loc_next >= 0);
+
+    void const *cur = node;
+    void const *last;
+    do {
+        last = cur;
+    } while ((cur = ll_next(cur)) != NULL);
+
+    return (void *) last;
+}
+
+void *cx_linked_list_prev(
+        void const *begin,
+        ptrdiff_t loc_next,
+        void const *node
+) {
+    assert(begin != NULL);
+    assert(node != NULL);
+    assert(loc_next >= 0);
+    if (begin == node) return NULL;
+    void const *cur = begin;
+    void const *next;
+    while (1) {
+        next = ll_next(cur);
+        if (next == node) return (void *) cur;
+        cur = next;
+    }
+}
+
+void cx_linked_list_link(
+        void *left,
+        void *right,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    assert(loc_next >= 0);
+    ll_next(left) = right;
+    if (loc_prev >= 0) {
+        ll_prev(right) = left;
+    }
+}
+
+void cx_linked_list_unlink(
+        void *left,
+        void *right,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    assert (loc_next >= 0);
+    assert(ll_next(left) == right);
+    ll_next(left) = NULL;
+    if (loc_prev >= 0) {
+        assert(ll_prev(right) == left);
+        ll_prev(right) = NULL;
+    }
+}
+
+void cx_linked_list_add(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *new_node
+) {
+    void *last;
+    if (end == NULL) {
+        assert(begin != NULL);
+        last = *begin == NULL ? NULL : cx_linked_list_last(*begin, loc_next);
+    } else {
+        last = *end;
+    }
+    cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, last, new_node, new_node);
+}
+
+void cx_linked_list_prepend(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *new_node
+) {
+    cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, NULL, new_node, new_node);
+}
+
+void cx_linked_list_insert(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *node,
+        void *new_node
+) {
+    cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, node, new_node, new_node);
+}
+
+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
+) {
+    // find the end of the chain, if not specified
+    if (insert_end == NULL) {
+        insert_end = cx_linked_list_last(insert_begin, loc_next);
+    }
+
+    // determine the successor
+    void *successor;
+    if (node == NULL) {
+        assert(begin != NULL || (end != NULL && loc_prev >= 0));
+        if (begin != NULL) {
+            successor = *begin;
+            *begin = insert_begin;
+        } else {
+            successor = *end == NULL ? NULL : cx_linked_list_first(*end, loc_prev);
+        }
+    } else {
+        successor = ll_next(node);
+        cx_linked_list_link(node, insert_begin, loc_prev, loc_next);
+    }
+
+    if (successor == NULL) {
+        // the list ends with the new chain
+        if (end != NULL) {
+            *end = insert_end;
+        }
+    } else {
+        cx_linked_list_link(insert_end, successor, loc_prev, loc_next);
+    }
+}
+
+void cx_linked_list_remove(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        void *node
+) {
+    assert(node != NULL);
+    assert(loc_next >= 0);
+    assert(loc_prev >= 0 || begin != NULL);
+
+    // find adjacent nodes
+    void *next = ll_next(node);
+    void *prev;
+    if (loc_prev >= 0) {
+        prev = ll_prev(node);
+    } else {
+        prev = cx_linked_list_prev(*begin, loc_next, node);
+    }
+
+    // update next pointer of prev node, or set begin
+    if (prev == NULL) {
+        if (begin != NULL) {
+            *begin = next;
+        }
+    } else {
+        ll_next(prev) = next;
+    }
+
+    // update prev pointer of next node, or set end
+    if (next == NULL) {
+        if (end != NULL) {
+            *end = prev;
+        }
+    } else if (loc_prev >= 0) {
+        ll_prev(next) = prev;
+    }
+}
+
+size_t cx_linked_list_size(
+        void const *node,
+        ptrdiff_t loc_next
+) {
+    assert(loc_next >= 0);
+    size_t size = 0;
+    while (node != NULL) {
+        node = ll_next(node);
+        size++;
+    }
+    return size;
+}
+
+#ifndef CX_LINKED_LIST_SORT_SBO_SIZE
+#define CX_LINKED_LIST_SORT_SBO_SIZE 1024
+#endif
+
+static void *cx_linked_list_sort_merge(
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        ptrdiff_t loc_data,
+        size_t length,
+        void *ls,
+        void *le,
+        void *re,
+        cx_compare_func cmp_func
+) {
+    void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE];
+    void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ?
+                    malloc(sizeof(void *) * length) : sbo;
+    if (sorted == NULL) abort();
+    void *rc, *lc;
+
+    lc = ls;
+    rc = le;
+    size_t n = 0;
+    while (lc && lc != le && rc != re) {
+        if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) {
+            sorted[n] = lc;
+            lc = ll_next(lc);
+        } else {
+            sorted[n] = rc;
+            rc = ll_next(rc);
+        }
+        n++;
+    }
+    while (lc && lc != le) {
+        sorted[n] = lc;
+        lc = ll_next(lc);
+        n++;
+    }
+    while (rc && rc != re) {
+        sorted[n] = rc;
+        rc = ll_next(rc);
+        n++;
+    }
+
+    // Update pointer
+    if (loc_prev >= 0) ll_prev(sorted[0]) = NULL;
+    cx_for_n (i, length - 1) {
+        cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next);
+    }
+    ll_next(sorted[length - 1]) = NULL;
+
+    void *ret = sorted[0];
+    if (sorted != sbo) {
+        free(sorted);
+    }
+    return ret;
+}
+
+void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        ptrdiff_t loc_data,
+        cx_compare_func cmp_func
+) {
+    assert(begin != NULL);
+    assert(loc_next >= 0);
+    assert(loc_data >= 0);
+    assert(cmp_func);
+
+    void *lc, *ls, *le, *re;
+
+    // set start node
+    ls = *begin;
+
+    // check how many elements are already sorted
+    lc = ls;
+    size_t ln = 1;
+    while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) {
+        lc = ll_next(lc);
+        ln++;
+    }
+    le = ll_next(lc);
+
+    // if first unsorted node is NULL, the list is already completely sorted
+    if (le != NULL) {
+        void *rc;
+        size_t rn = 1;
+        rc = le;
+        // skip already sorted elements
+        while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) {
+            rc = ll_next(rc);
+            rn++;
+        }
+        re = ll_next(rc);
+
+        // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
+        void *sorted = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+                                                 ln + rn, ls, le, re, cmp_func);
+
+        // Something left? Sort it!
+        size_t remainder_length = cx_linked_list_size(re, loc_next);
+        if (remainder_length > 0) {
+            void *remainder = re;
+            cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func);
+
+            // merge sorted list with (also sorted) remainder
+            *begin = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data,
+                                               ln + rn + remainder_length,
+                                               sorted, remainder, NULL, cmp_func);
+        } else {
+            // no remainder - we've got our sorted list
+            *begin = sorted;
+        }
+        if (end) *end = cx_linked_list_last(sorted, loc_next);
+    }
+}
+
+int cx_linked_list_compare(
+        void const *begin_left,
+        void const *begin_right,
+        ptrdiff_t loc_advance,
+        ptrdiff_t loc_data,
+        cx_compare_func cmp_func
+) {
+    void const *left = begin_left, *right = begin_right;
+
+    while (left != NULL && right != NULL) {
+        void const *left_data = ll_data(left);
+        void const *right_data = ll_data(right);
+        int result = cmp_func(left_data, right_data);
+        if (result != 0) return result;
+        left = ll_advance(left);
+        right = ll_advance(right);
+    }
+
+    if (left != NULL) { return 1; }
+    else if (right != NULL) { return -1; }
+    else { return 0; }
+}
+
+void cx_linked_list_reverse(
+        void **begin,
+        void **end,
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next
+) {
+    assert(begin != NULL);
+    assert(loc_next >= 0);
+
+    // swap all links
+    void *prev = NULL;
+    void *cur = *begin;
+    while (cur != NULL) {
+        void *next = ll_next(cur);
+
+        ll_next(cur) = prev;
+        if (loc_prev >= 0) {
+            ll_prev(cur) = next;
+        }
+
+        prev = cur;
+        cur = next;
+    }
+
+    // update begin and end
+    if (end != NULL) {
+        *end = *begin;
+    }
+    *begin = prev;
+}
+
+// HIGH LEVEL LINKED LIST IMPLEMENTATION
+
+bool CX_DISABLE_LINKED_LIST_SWAP_SBO = false;
+
+typedef struct cx_linked_list_node cx_linked_list_node;
+struct cx_linked_list_node {
+    cx_linked_list_node *prev;
+    cx_linked_list_node *next;
+    char payload[];
+};
+
+#define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev)
+#define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next)
+#define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload)
+
+typedef struct {
+    struct cx_list_s base;
+    cx_linked_list_node *begin;
+    cx_linked_list_node *end;
+} cx_linked_list;
+
+static cx_linked_list_node *cx_ll_node_at(
+        cx_linked_list const *list,
+        size_t index
+) {
+    if (index >= list->base.size) {
+        return NULL;
+    } else if (index > list->base.size / 2) {
+        return cx_linked_list_at(list->end, list->base.size - 1, CX_LL_LOC_PREV, index);
+    } else {
+        return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index);
+    }
+}
+
+static int cx_ll_insert_at(
+        struct cx_list_s *list,
+        cx_linked_list_node *node,
+        void const *elem
+) {
+
+    // create the new new_node
+    cx_linked_list_node *new_node = cxMalloc(list->allocator,
+                                             sizeof(cx_linked_list_node) + list->item_size);
+
+    // sortir if failed
+    if (new_node == NULL) return 1;
+
+    // initialize new new_node
+    new_node->prev = new_node->next = NULL;
+    memcpy(new_node->payload, elem, list->item_size);
+
+    // insert
+    cx_linked_list *ll = (cx_linked_list *) list;
+    cx_linked_list_insert_chain(
+            (void **) &ll->begin, (void **) &ll->end,
+            CX_LL_LOC_PREV, CX_LL_LOC_NEXT,
+            node, new_node, new_node
+    );
+
+    // increase the size and return
+    list->size++;
+    return 0;
+}
+
+static size_t cx_ll_insert_array(
+        struct cx_list_s *list,
+        size_t index,
+        void const *array,
+        size_t n
+) {
+    // out-of bounds and corner case check
+    if (index > list->size || n == 0) return 0;
+
+    // find position efficiently
+    cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+
+    // perform first insert
+    if (0 != cx_ll_insert_at(list, node, array)) {
+        return 1;
+    }
+
+    // is there more?
+    if (n == 1) return 1;
+
+    // we now know exactly where we are
+    node = node == NULL ? ((cx_linked_list *) list)->begin : node->next;
+
+    // we can add the remaining nodes and immedately advance to the inserted node
+    char const *source = array;
+    for (size_t i = 1; i < n; i++) {
+        source += list->item_size;
+        if (0 != cx_ll_insert_at(list, node, source)) {
+            return i;
+        }
+        node = node->next;
+    }
+    return n;
+}
+
+static int cx_ll_insert_element(
+        struct cx_list_s *list,
+        size_t index,
+        void const *element
+) {
+    return 1 != cx_ll_insert_array(list, index, element, 1);
+}
+
+static int cx_ll_remove(
+        struct cx_list_s *list,
+        size_t index
+) {
+    cx_linked_list *ll = (cx_linked_list *) list;
+    cx_linked_list_node *node = cx_ll_node_at(ll, index);
+
+    // out-of-bounds check
+    if (node == NULL) return 1;
+
+    // element destruction
+    cx_invoke_destructor(list, node->payload);
+
+    // remove
+    cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+                          CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+
+    // adjust size
+    list->size--;
+
+    // free and return
+    cxFree(list->allocator, node);
+
+    return 0;
+}
+
+static void cx_ll_clear(struct cx_list_s *list) {
+    if (list->size == 0) return;
+
+    cx_linked_list *ll = (cx_linked_list *) list;
+    cx_linked_list_node *node = ll->begin;
+    while (node != NULL) {
+        cx_invoke_destructor(list, node->payload);
+        cx_linked_list_node *next = node->next;
+        cxFree(list->allocator, node);
+        node = next;
+    }
+    ll->begin = ll->end = NULL;
+    list->size = 0;
+}
+
+#ifndef CX_LINKED_LIST_SWAP_SBO_SIZE
+#define CX_LINKED_LIST_SWAP_SBO_SIZE 16
+#endif
+
+static int cx_ll_swap(
+        struct cx_list_s *list,
+        size_t i,
+        size_t j
+) {
+    if (i >= list->size || j >= list->size) return 1;
+    if (i == j) return 0;
+
+    // perform an optimized search that finds both elements in one run
+    cx_linked_list *ll = (cx_linked_list *) list;
+    size_t mid = list->size / 2;
+    size_t left, right;
+    if (i < j) {
+        left = i;
+        right = j;
+    } else {
+        left = j;
+        right = i;
+    }
+    cx_linked_list_node *nleft, *nright;
+    if (left < mid && right < mid) {
+        // case 1: both items left from mid
+        nleft = cx_ll_node_at(ll, left);
+        nright = nleft;
+        for (size_t c = left; c < right; c++) {
+            nright = nright->next;
+        }
+    } else if (left >= mid && right >= mid) {
+        // case 2: both items right from mid
+        nright = cx_ll_node_at(ll, right);
+        nleft = nright;
+        for (size_t c = right; c > left; c--) {
+            nleft = nleft->prev;
+        }
+    } else {
+        // case 3: one item left, one item right
+
+        // chose the closest to begin / end
+        size_t closest;
+        size_t other;
+        size_t diff2boundary = list->size - right - 1;
+        if (left <= diff2boundary) {
+            closest = left;
+            other = right;
+            nleft = cx_ll_node_at(ll, left);
+        } else {
+            closest = right;
+            other = left;
+            diff2boundary = left;
+            nright = cx_ll_node_at(ll, right);
+        }
+
+        // is other element closer to us or closer to boundary?
+        if (right - left <= diff2boundary) {
+            // search other element starting from already found element
+            if (closest == left) {
+                nright = nleft;
+                for (size_t c = left; c < right; c++) {
+                    nright = nright->next;
+                }
+            } else {
+                nleft = nright;
+                for (size_t c = right; c > left; c--) {
+                    nleft = nleft->prev;
+                }
+            }
+        } else {
+            // search other element starting at the boundary
+            if (closest == left) {
+                nright = cx_ll_node_at(ll, other);
+            } else {
+                nleft = cx_ll_node_at(ll, other);
+            }
+        }
+    }
+
+    if (list->item_size > CX_LINKED_LIST_SWAP_SBO_SIZE || CX_DISABLE_LINKED_LIST_SWAP_SBO) {
+        cx_linked_list_node *prev = nleft->prev;
+        cx_linked_list_node *next = nright->next;
+        cx_linked_list_node *midstart = nleft->next;
+        cx_linked_list_node *midend = nright->prev;
+
+        if (prev == NULL) {
+            ll->begin = nright;
+        } else {
+            prev->next = nright;
+        }
+        nright->prev = prev;
+        if (midstart == nright) {
+            // special case: both nodes are adjacent
+            nright->next = nleft;
+            nleft->prev = nright;
+        } else {
+            // likely case: a chain is between the two nodes
+            nright->next = midstart;
+            midstart->prev = nright;
+            midend->next = nleft;
+            nleft->prev = midend;
+        }
+        nleft->next = next;
+        if (next == NULL) {
+            ll->end = nleft;
+        } else {
+            next->prev = nleft;
+        }
+    } else {
+        // swap payloads to avoid relinking
+        char buf[CX_LINKED_LIST_SWAP_SBO_SIZE];
+        memcpy(buf, nleft->payload, list->item_size);
+        memcpy(nleft->payload, nright->payload, list->item_size);
+        memcpy(nright->payload, buf, list->item_size);
+    }
+
+    return 0;
+}
+
+static void *cx_ll_at(
+        struct cx_list_s const *list,
+        size_t index
+) {
+    cx_linked_list *ll = (cx_linked_list *) list;
+    cx_linked_list_node *node = cx_ll_node_at(ll, index);
+    return node == NULL ? NULL : node->payload;
+}
+
+static size_t cx_ll_find(
+        struct cx_list_s const *list,
+        void const *elem
+) {
+    return cx_linked_list_find(((cx_linked_list *) list)->begin,
+                               CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+                               list->cmpfunc, elem);
+}
+
+static void cx_ll_sort(struct cx_list_s *list) {
+    cx_linked_list *ll = (cx_linked_list *) list;
+    cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end,
+                        CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+                        list->cmpfunc);
+}
+
+static void cx_ll_reverse(struct cx_list_s *list) {
+    cx_linked_list *ll = (cx_linked_list *) list;
+    cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT);
+}
+
+static int cx_ll_compare(
+        struct cx_list_s const *list,
+        struct cx_list_s const *other
+) {
+    cx_linked_list *left = (cx_linked_list *) list;
+    cx_linked_list *right = (cx_linked_list *) other;
+    return cx_linked_list_compare(left->begin, right->begin,
+                                  CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+                                  list->cmpfunc);
+}
+
+static bool cx_ll_iter_valid(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    return iter->elem_handle != NULL;
+}
+
+static void cx_ll_iter_next(void *it) {
+    struct cx_iterator_base_s *itbase = it;
+    if (itbase->remove) {
+        itbase->remove = false;
+        struct cx_mut_iterator_s *iter = it;
+        struct cx_list_s *list = iter->src_handle;
+        cx_linked_list *ll = iter->src_handle;
+        cx_linked_list_node *node = iter->elem_handle;
+        iter->elem_handle = node->next;
+        cx_invoke_destructor(list, node->payload);
+        cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+                              CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+        list->size--;
+        cxFree(list->allocator, node);
+    } else {
+        struct cx_iterator_s *iter = it;
+        iter->index++;
+        cx_linked_list_node *node = iter->elem_handle;
+        iter->elem_handle = node->next;
+    }
+}
+
+static void cx_ll_iter_prev(void *it) {
+    struct cx_iterator_base_s *itbase = it;
+    if (itbase->remove) {
+        itbase->remove = false;
+        struct cx_mut_iterator_s *iter = it;
+        struct cx_list_s *list = iter->src_handle;
+        cx_linked_list *ll = iter->src_handle;
+        cx_linked_list_node *node = iter->elem_handle;
+        iter->elem_handle = node->prev;
+        iter->index--;
+        cx_invoke_destructor(list, node->payload);
+        cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+                              CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+        list->size--;
+        cxFree(list->allocator, node);
+    } else {
+        struct cx_iterator_s *iter = it;
+        iter->index--;
+        cx_linked_list_node *node = iter->elem_handle;
+        iter->elem_handle = node->prev;
+    }
+}
+
+static void *cx_ll_iter_current(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    cx_linked_list_node *node = iter->elem_handle;
+    return node->payload;
+}
+
+static bool cx_ll_iter_flag_rm(void *it) {
+    struct cx_iterator_base_s *iter = it;
+    if (iter->mutating) {
+        iter->remove = true;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static CxIterator cx_ll_iterator(
+        struct cx_list_s const *list,
+        size_t index,
+        bool backwards
+) {
+    CxIterator iter;
+    iter.index = index;
+    iter.src_handle = list;
+    iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index);
+    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.flag_removal = cx_ll_iter_flag_rm;
+    iter.base.mutating = false;
+    iter.base.remove = false;
+    return iter;
+}
+
+static int cx_ll_insert_iter(
+        CxMutIterator *iter,
+        void const *elem,
+        int prepend
+) {
+    struct cx_list_s *list = iter->src_handle;
+    cx_linked_list_node *node = iter->elem_handle;
+    if (node != NULL) {
+        assert(prepend >= 0 && prepend <= 1);
+        cx_linked_list_node *choice[2] = {node, node->prev};
+        int result = cx_ll_insert_at(list, choice[prepend], elem);
+        iter->index += prepend * (0 == result);
+        return result;
+    } else {
+        int result = cx_ll_insert_element(list, list->size, elem);
+        iter->index = list->size;
+        return result;
+    }
+}
+
+static void cx_ll_destructor(CxList *list) {
+    cx_linked_list *ll = (cx_linked_list *) list;
+
+    cx_linked_list_node *node = ll->begin;
+    while (node) {
+        void *next = node->next;
+        cxFree(list->allocator, node);
+        node = next;
+    }
+    // do not free the list pointer, this is just a destructor!
+}
+
+static cx_list_class cx_linked_list_class = {
+        cx_ll_destructor,
+        cx_ll_insert_element,
+        cx_ll_insert_array,
+        cx_ll_insert_iter,
+        cx_ll_remove,
+        cx_ll_clear,
+        cx_ll_swap,
+        cx_ll_at,
+        cx_ll_find,
+        cx_ll_sort,
+        cx_ll_compare,
+        cx_ll_reverse,
+        cx_ll_iterator,
+};
+
+CxList *cxLinkedListCreate(
+        CxAllocator const *allocator,
+        cx_compare_func comparator,
+        size_t item_size
+) {
+    if (allocator == NULL) {
+        allocator = cxDefaultAllocator;
+    }
+
+    cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
+    if (list == NULL) return NULL;
+
+    list->base.cl = &cx_linked_list_class;
+    list->base.allocator = allocator;
+    list->base.cmpfunc = comparator;
+
+    if (item_size > 0) {
+        list->base.item_size = item_size;
+    } else {
+        cxListStorePointers((CxList *) list);
+    }
+
+    return (CxList *) list;
+}
--- a/ucx/list.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/ucx/list.c	Fri Apr 21 21:25:32 2023 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * 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:
@@ -26,311 +26,249 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx/list.h"
+#include "cx/list.h"
+
+#include <string.h>
+
+// <editor-fold desc="Store Pointers Functionality">
+
+static _Thread_local cx_compare_func cx_pl_cmpfunc_impl;
+
+static int cx_pl_cmpfunc(
+        void const *l,
+        void const *r
+) {
+    void *const *lptr = l;
+    void *const *rptr = r;
+    void const *left = lptr == NULL ? NULL : *lptr;
+    void const *right = rptr == NULL ? NULL : *rptr;
+    return cx_pl_cmpfunc_impl(left, right);
+}
 
-UcxList *ucx_list_clone(UcxList *l, copy_func fnc, void *data) {
-    return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data);
+static void cx_pl_hack_cmpfunc(struct cx_list_s const *list) {
+    // cast away const - this is the hacky thing
+    struct cx_list_s *l = (struct cx_list_s *) list;
+    cx_pl_cmpfunc_impl = l->cmpfunc;
+    l->cmpfunc = cx_pl_cmpfunc;
+}
+
+static void cx_pl_unhack_cmpfunc(struct cx_list_s const *list) {
+    // cast away const - this is the hacky thing
+    struct cx_list_s *l = (struct cx_list_s *) list;
+    l->cmpfunc = cx_pl_cmpfunc_impl;
+}
+
+static void cx_pl_destructor(struct cx_list_s *list) {
+    list->climpl->destructor(list);
+}
+
+static int cx_pl_insert_element(
+        struct cx_list_s *list,
+        size_t index,
+        void const *element
+) {
+    return list->climpl->insert_element(list, index, &element);
 }
 
-UcxList *ucx_list_clone_a(UcxAllocator *alloc, UcxList *l,
-        copy_func fnc, void *data) {
-    UcxList *ret = NULL;
-    while (l) {
-        if (fnc) {
-            ret = ucx_list_append_a(alloc, ret, fnc(l->data, data));
-        } else {
-            ret = ucx_list_append_a(alloc, ret, l->data);
-        }
-        l = l->next;
-    }
+static size_t cx_pl_insert_array(
+        struct cx_list_s *list,
+        size_t index,
+        void const *array,
+        size_t n
+) {
+    return list->climpl->insert_array(list, index, array, n);
+}
+
+static int cx_pl_insert_iter(
+        struct cx_mut_iterator_s *iter,
+        void const *elem,
+        int prepend
+) {
+    struct cx_list_s *list = iter->src_handle;
+    return list->climpl->insert_iter(iter, &elem, prepend);
+}
+
+static int cx_pl_remove(
+        struct cx_list_s *list,
+        size_t index
+) {
+    return list->climpl->remove(list, index);
+}
+
+static void cx_pl_clear(struct cx_list_s *list) {
+    list->climpl->clear(list);
+}
+
+static int cx_pl_swap(
+        struct cx_list_s *list,
+        size_t i,
+        size_t j
+) {
+    return list->climpl->swap(list, i, j);
+}
+
+static void *cx_pl_at(
+        struct cx_list_s const *list,
+        size_t index
+) {
+    void **ptr = list->climpl->at(list, index);
+    return ptr == NULL ? NULL : *ptr;
+}
+
+static size_t cx_pl_find(
+        struct cx_list_s const *list,
+        void const *elem
+) {
+    cx_pl_hack_cmpfunc(list);
+    size_t ret = list->climpl->find(list, &elem);
+    cx_pl_unhack_cmpfunc(list);
     return ret;
 }
 
-int ucx_list_equals(const UcxList *l1, const UcxList *l2,
-        cmp_func fnc, void* data) {
-    if (l1 == l2) return 1;
-    
-    while (l1 != NULL && l2 != NULL) {
-        if (fnc == NULL) {
-            if (l1->data != l2->data) return 0;
-        } else {
-            if (fnc(l1->data, l2->data, data) != 0) return 0;
-        }
-        l1 = l1->next;
-        l2 = l2->next;
-    }
-    
-    return (l1 == NULL && l2 == NULL);
+static void cx_pl_sort(struct cx_list_s *list) {
+    cx_pl_hack_cmpfunc(list);
+    list->climpl->sort(list);
+    cx_pl_unhack_cmpfunc(list);
 }
 
-void ucx_list_free(UcxList *l) {
-    ucx_list_free_a(ucx_default_allocator(), l);
+static int cx_pl_compare(
+        struct cx_list_s const *list,
+        struct cx_list_s const *other
+) {
+    cx_pl_hack_cmpfunc(list);
+    int ret = list->climpl->compare(list, other);
+    cx_pl_unhack_cmpfunc(list);
+    return ret;
 }
 
-void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) {
-    UcxList *e = l, *f;
-    while (e != NULL) {
-        f = e;
-        e = e->next;
-        alfree(alloc, f);
-    }
+static void cx_pl_reverse(struct cx_list_s *list) {
+    list->climpl->reverse(list);
 }
 
-void ucx_list_free_content(UcxList* list, ucx_destructor destr) {
-    if (!destr) destr = free;
-    while (list != NULL) {
-        destr(list->data);
-        list = list->next;
-    }
-}
-
-UcxList *ucx_list_append(UcxList *l, void *data)  {
-    return ucx_list_append_a(ucx_default_allocator(), l, data);
+static void *cx_pl_iter_current(void const *it) {
+    struct cx_iterator_s const *iter = it;
+    void **ptr = iter->base.current_impl(it);
+    return ptr == NULL ? NULL : *ptr;
 }
 
-UcxList *ucx_list_append_a(UcxAllocator *alloc, UcxList *l, void *data)  {
-    UcxList *nl = (UcxList*) almalloc(alloc, sizeof(UcxList));
-    if (!nl) {
-        return NULL;
-    }
-    
-    nl->data = data;
-    nl->next = NULL;
-    if (l) {
-        UcxList *t = ucx_list_last(l);
-        t->next = nl;
-        nl->prev = t;
-        return l;
-    } else {
-        nl->prev = NULL;
-        return nl;
-    }
-}
-
-UcxList *ucx_list_prepend(UcxList *l, void *data) {
-    return ucx_list_prepend_a(ucx_default_allocator(), l, data);
+static struct cx_iterator_s cx_pl_iterator(
+        struct cx_list_s const *list,
+        size_t index,
+        bool backwards
+) {
+    struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards);
+    iter.base.current_impl = iter.base.current;
+    iter.base.current = cx_pl_iter_current;
+    return iter;
 }
 
-UcxList *ucx_list_prepend_a(UcxAllocator *alloc, UcxList *l, void *data) {
-    UcxList *nl = ucx_list_append_a(alloc, NULL, data);
-    if (!nl) {
-        return NULL;
-    }
-    l = ucx_list_first(l);
-    
-    if (l) {
-        nl->next = l;
-        l->prev = nl;
-    }
-    return nl;
-}
+static cx_list_class cx_pointer_list_class = {
+        cx_pl_destructor,
+        cx_pl_insert_element,
+        cx_pl_insert_array,
+        cx_pl_insert_iter,
+        cx_pl_remove,
+        cx_pl_clear,
+        cx_pl_swap,
+        cx_pl_at,
+        cx_pl_find,
+        cx_pl_sort,
+        cx_pl_compare,
+        cx_pl_reverse,
+        cx_pl_iterator,
+};
 
-UcxList *ucx_list_concat(UcxList *l1, UcxList *l2) {
-    if (l1) {
-        UcxList *last = ucx_list_last(l1);
-        last->next = l2;
-        if (l2) {
-            l2->prev = last;
-        }
-        return l1;
-    } else {
-        return l2;
+void cxListStoreObjects(CxList *list) {
+    list->store_pointer = false;
+    if (list->climpl != NULL) {
+        list->cl = list->climpl;
+        list->climpl = NULL;
     }
 }
 
-UcxList *ucx_list_last(const UcxList *l) {
-    if (l == NULL) return NULL;
-    
-    const UcxList *e = l;
-    while (e->next != NULL) {
-        e = e->next;
-    }
-    return (UcxList*)e;
-}
-
-ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem) {
-    ssize_t index = 0;
-    while (list) {
-        if (list == elem) {
-            return index;
-        }
-        list = list->next;
-        index++;
-    }
-    return -1;
+void cxListStorePointers(CxList *list) {
+    list->item_size = sizeof(void *);
+    list->store_pointer = true;
+    list->climpl = list->cl;
+    list->cl = &cx_pointer_list_class;
 }
 
-UcxList *ucx_list_get(const UcxList *l, size_t index) {
-    if (l == NULL) return NULL;
-
-    const UcxList *e = l;
-    while (e->next && index > 0) {
-        e = e->next;
-        index--;
-    }
-    
-    return (UcxList*)(index == 0 ? e : NULL);
-}
+// </editor-fold>
 
-ssize_t ucx_list_find(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) {
-    ssize_t index = 0;
-    UCX_FOREACH(e, l) {
-        if (fnc) {
-            if (fnc(elem, e->data, cmpdata) == 0) {
-                return index;
-            }
-        } else {
-            if (elem == e->data) {
-                return index;
-            }
+void cxListDestroy(CxList *list) {
+    if (list->simple_destructor) {
+        CxIterator iter = cxListIterator(list);
+        cx_foreach(void*, elem, iter) {
+            // already correctly resolved pointer - immediately invoke dtor
+            list->simple_destructor(elem);
         }
-        index++;
     }
-    return -1;
-}
-
-int ucx_list_contains(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) {
-    return ucx_list_find(l, elem, fnc, cmpdata) > -1;
-}
-
-size_t ucx_list_size(const UcxList *l) {
-    if (l == NULL) return 0;
-    
-    const UcxList *e = l;
-    size_t s = 1;
-    while (e->next != NULL) {
-        e = e->next;
-        s++;
+    if (list->advanced_destructor) {
+        CxIterator iter = cxListIterator(list);
+        cx_foreach(void*, elem, iter) {
+            // already correctly resolved pointer - immediately invoke dtor
+            list->advanced_destructor(list->destructor_data, elem);
+        }
     }
 
-    return s;
+    list->cl->destructor(list);
+    cxFree(list->allocator, list);
 }
 
-static UcxList *ucx_list_sort_merge(int length,
-        UcxList* ls, UcxList* le, UcxList* re,
-        cmp_func fnc, void* data) {
-
-    UcxList** sorted = (UcxList**) malloc(sizeof(UcxList*)*length);
-    UcxList *rc, *lc;
-
-    lc = ls; rc = le;
-    int n = 0;
-    while (lc && lc != le && rc != re) {
-        if (fnc(lc->data, rc->data, data) <= 0) {
-            sorted[n] = lc;
-            lc = lc->next;
+int cxListCompare(
+        CxList const *list,
+        CxList const *other
+) {
+    if ((list->store_pointer ^ other->store_pointer) ||
+        ((list->climpl == NULL) ^ (other->climpl != NULL)) ||
+        ((list->climpl != NULL ? list->climpl->compare : list->cl->compare) !=
+         (other->climpl != NULL ? other->climpl->compare : other->cl->compare))) {
+        // lists are definitely different - cannot use internal compare function
+        if (list->size == other->size) {
+            CxIterator left = cxListIterator(list);
+            CxIterator right = cxListIterator(other);
+            for (size_t i = 0; i < list->size; i++) {
+                void *leftValue = cxIteratorCurrent(left);
+                void *rightValue = cxIteratorCurrent(right);
+                int d = list->cmpfunc(leftValue, rightValue);
+                if (d != 0) {
+                    return d;
+                }
+                cxIteratorNext(left);
+                cxIteratorNext(right);
+            }
+            return 0;
         } else {
-            sorted[n] = rc;
-            rc = rc->next;
+            return list->size < other->size ? -1 : 1;
         }
-        n++;
-    }
-    while (lc && lc != le) {
-        sorted[n] = lc;
-        lc = lc->next;
-        n++;
-    }
-    while (rc && rc != re) {
-        sorted[n] = rc;
-        rc = rc->next;
-        n++;
-    }
-
-    // Update pointer
-    sorted[0]->prev = NULL;
-    for (int i = 0 ; i < length-1 ; i++) {
-        sorted[i]->next = sorted[i+1];
-        sorted[i+1]->prev = sorted[i];
-    }
-    sorted[length-1]->next = NULL;
-
-    UcxList *ret = sorted[0];
-    free(sorted);
-    return ret;
-}
-
-UcxList *ucx_list_sort(UcxList *l, cmp_func fnc, void *data) {
-    if (l == NULL) {
-        return NULL;
-    }
-
-    UcxList *lc;
-    int ln = 1;
-
-    UcxList *ls = l, *le, *re;
-    
-    // check how many elements are already sorted
-    lc = ls;
-    while (lc->next != NULL && fnc(lc->next->data, lc->data, data) > 0) {
-        lc = lc->next;
-        ln++;
-    }
-    le = lc->next;
-
-    if (le == NULL) {
-        return l; // this list is already sorted :)
     } else {
-        UcxList *rc;
-        int rn = 1;
-        rc = le;
-        // skip already sorted elements
-        while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) {
-            rc = rc->next;
-            rn++;
-        }
-        re = rc->next;
-
-        // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them
-        UcxList *sorted = ucx_list_sort_merge(ln+rn,
-                ls, le, re,
-                fnc, data);
-        
-        // Something left? Sort it!
-        size_t remainder_length = ucx_list_size(re);
-        if (remainder_length > 0) {
-            UcxList *remainder = ucx_list_sort(re, fnc, data);
-
-            // merge sorted list with (also sorted) remainder
-            l = ucx_list_sort_merge(ln+rn+remainder_length,
-                    sorted, remainder, NULL, fnc, data);
-        } else {
-            // no remainder - we've got our sorted list
-            l = sorted;
-        }
-
-        return l;
+        // lists are compatible
+        return list->cl->compare(list, other);
     }
 }
 
-UcxList *ucx_list_first(const UcxList *l) {
-    if (!l) {
-        return NULL;
-    }
-    
-    const UcxList *e = l;
-    while (e->prev) {
-        e = e->prev;
-    }
-    return (UcxList *)e;
+CxMutIterator cxListMutIteratorAt(
+        CxList *list,
+        size_t index
+) {
+    CxIterator it = list->cl->iterator(list, index, false);
+    it.base.mutating = true;
+
+    // we know the iterators share the same memory layout
+    CxMutIterator iter;
+    memcpy(&iter, &it, sizeof(CxMutIterator));
+    return iter;
 }
 
-UcxList *ucx_list_remove(UcxList *l, UcxList *e) {
-    return ucx_list_remove_a(ucx_default_allocator(), l, e);
+CxMutIterator cxListMutBackwardsIteratorAt(
+        CxList *list,
+        size_t index
+) {
+    CxIterator it = list->cl->iterator(list, index, true);
+    it.base.mutating = true;
+
+    // we know the iterators share the same memory layout
+    CxMutIterator iter;
+    memcpy(&iter, &it, sizeof(CxMutIterator));
+    return iter;
 }
-    
-UcxList *ucx_list_remove_a(UcxAllocator *alloc, UcxList *l, UcxList *e) {
-    if (l == e) {
-        l = e->next;
-    }
-    
-    if (e->next) {
-        e->next->prev = e->prev;
-    }
-    
-    if (e->prev) {
-        e->prev->next = e->next;
-    }
-    
-    alfree(alloc, e);
-    return l;
-}
--- a/ucx/logging.c	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-
-#include "ucx/logging.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <time.h>
-
-UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask) {
-    UcxLogger *logger = (UcxLogger*) malloc(sizeof(UcxLogger));
-    if (logger != NULL) {
-        logger->stream = stream;
-        logger->writer = (write_func)fwrite;
-        logger->dateformat = (char*) "%F %T %z ";
-        logger->level = level;
-        logger->mask = mask;
-        logger->levels = ucx_map_new(8);
-        
-        unsigned int l;
-        l = UCX_LOGGER_ERROR;
-        ucx_map_int_put(logger->levels, l, (void*) "[ERROR]");
-        l = UCX_LOGGER_WARN;
-        ucx_map_int_put(logger->levels, l, (void*) "[WARNING]");
-        l = UCX_LOGGER_INFO;
-        ucx_map_int_put(logger->levels, l, (void*) "[INFO]");
-        l = UCX_LOGGER_DEBUG;
-        ucx_map_int_put(logger->levels, l, (void*) "[DEBUG]");
-        l = UCX_LOGGER_TRACE;
-        ucx_map_int_put(logger->levels, l, (void*) "[TRACE]");
-    }
-
-    return logger;
-}
-
-void ucx_logger_free(UcxLogger *logger) {
-    ucx_map_free(logger->levels);
-    free(logger);
-}
-
-// estimated max. message length (documented)
-#define UCX_LOGGER_MSGMAX 4096
-
-void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file,
-        const unsigned int line, const char *format, ...) {
-    if (level <= logger->level) {
-        char msg[UCX_LOGGER_MSGMAX];
-        const char *text;
-        size_t k = 0;
-        size_t n;
-        
-        if ((logger->mask & UCX_LOGGER_LEVEL) > 0) {
-            text = (const char*) ucx_map_int_get(logger->levels, level);
-            if (!text) {
-                text = "[UNKNOWN]";
-            }
-            n = strlen(text);
-            n = n > 256 ? 256 : n;
-            memcpy(msg+k, text, n);
-            k += n;
-            msg[k++] = ' ';
-        }
-        if ((logger->mask & UCX_LOGGER_TIMESTAMP) > 0) {
-            time_t now = time(NULL);
-            k += strftime(msg+k, 128, logger->dateformat, localtime(&now));
-        }
-        if ((logger->mask & UCX_LOGGER_SOURCE) > 0) {
-            n = strlen(file);
-            memcpy(msg+k, file, n);
-            k += n;
-            k += sprintf(msg+k, ":%u ", line);
-        }
-        
-        if (k > 0) {
-            msg[k++] = '-'; msg[k++] = ' ';
-        }
-        
-        va_list args;
-        va_start (args, format);
-        k += vsnprintf(msg+k, UCX_LOGGER_MSGMAX-k-1, format, args);
-        va_end (args);        
-        
-        msg[k++] = '\n';
-        
-        logger->writer(msg, 1, k, logger->stream);
-    }
-}
--- a/ucx/map.c	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,337 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-
-#include "ucx/map.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-UcxMap *ucx_map_new(size_t size) {
-    return ucx_map_new_a(NULL, size);
-}
-
-UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size) {
-    if(size == 0) {
-        size = 16;
-    }
-       
-    if(!allocator) {
-        allocator = ucx_default_allocator();
-    }
-    
-    UcxMap *map = (UcxMap*)almalloc(allocator, sizeof(UcxMap));
-    if (!map) {
-        return NULL;
-    }
-    
-    map->allocator = allocator;
-    map->map = (UcxMapElement**)alcalloc(
-            allocator, size, sizeof(UcxMapElement*));
-    if(map->map == NULL) {
-        alfree(allocator, map);
-        return NULL;
-    }
-    map->size = size;
-    map->count = 0;
-
-    return map;
-}
-
-static void ucx_map_free_elmlist_contents(UcxMap *map) {
-    for (size_t n = 0 ; n < map->size ; n++) {
-        UcxMapElement *elem = map->map[n];
-        if (elem != NULL) {
-            do {
-                UcxMapElement *next = elem->next;
-                alfree(map->allocator, elem->key.data);
-                alfree(map->allocator, elem);
-                elem = next;
-            } while (elem != NULL);
-        }
-    }
-}
-
-void ucx_map_free(UcxMap *map) {
-    ucx_map_free_elmlist_contents(map);
-    alfree(map->allocator, map->map);
-    alfree(map->allocator, map);
-}
-
-void ucx_map_free_content(UcxMap *map, ucx_destructor destr) {
-    UcxMapIterator iter = ucx_map_iterator(map);
-    void *val;
-    UCX_MAP_FOREACH(key, val, iter) {
-        if (destr) {
-            destr(val);
-        } else {
-            alfree(map->allocator, val);
-        }
-    }
-}
-
-void ucx_map_clear(UcxMap *map) {
-    if (map->count == 0) {
-        return; // nothing to do
-    }
-    ucx_map_free_elmlist_contents(map);
-    memset(map->map, 0, map->size*sizeof(UcxMapElement*));
-    map->count = 0;
-}
-
-int ucx_map_copy(UcxMap *from, UcxMap *to, copy_func fnc, void *data) {
-    UcxMapIterator i = ucx_map_iterator(from);
-    void *value;
-    UCX_MAP_FOREACH(key, value, i) {
-        if (ucx_map_put(to, key, fnc ? fnc(value, data) : value)) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data) {
-    size_t bs = (map->count * 5) >> 1;
-    UcxMap *newmap = ucx_map_new(bs > map->size ? bs : map->size);
-    if (!newmap) {
-        return NULL;
-    }
-    ucx_map_copy(map, newmap, fnc, data);
-    return newmap;
-}
-
-int ucx_map_rehash(UcxMap *map) {
-    size_t load = (map->size * 3) >> 2;
-    if (map->count > load) {
-        UcxMap oldmap;
-        oldmap.map = map->map;
-        oldmap.size = map->size;
-        oldmap.count = map->count;
-        oldmap.allocator = map->allocator;
-        
-        map->size = (map->count * 5) >> 1;
-        map->map = (UcxMapElement**)alcalloc(
-                map->allocator, map->size, sizeof(UcxMapElement*));
-        if (!map->map) {
-            *map = oldmap;
-            return 1;
-        }
-        map->count = 0;
-        ucx_map_copy(&oldmap, map, NULL, NULL);
-        
-        /* free the UcxMapElement list of oldmap */
-        ucx_map_free_elmlist_contents(&oldmap);
-        alfree(map->allocator, oldmap.map);
-    }
-    return 0;
-}
-
-int ucx_map_put(UcxMap *map, UcxKey key, void *data) {
-    UcxAllocator *allocator = map->allocator;
-    
-    if (key.hash == 0) {
-        key.hash = ucx_hash((const char*)key.data, key.len);
-    }
-    
-    struct UcxMapKey mapkey;
-    mapkey.hash = key.hash;
-
-    size_t slot = mapkey.hash%map->size;
-    UcxMapElement *elm = map->map[slot];
-    UcxMapElement *prev = NULL;
-
-    while (elm && elm->key.hash < mapkey.hash) {
-        prev = elm;
-        elm = elm->next;
-    }
-    
-    if (!elm || elm->key.hash != mapkey.hash) {
-        UcxMapElement *e = (UcxMapElement*)almalloc(
-                allocator, sizeof(UcxMapElement));
-        if (!e) {
-            return -1;
-        }
-        e->key.data = NULL;
-        if (prev) {
-            prev->next = e;
-        } else {
-            map->map[slot] = e;
-        }
-        e->next = elm;
-        elm = e;
-    }
-    
-    if (!elm->key.data) {
-        void *kd = almalloc(allocator, key.len);
-        if (!kd) {
-            return -1;
-        }
-        memcpy(kd, key.data, key.len);
-        mapkey.data = kd;
-        mapkey.len = key.len;
-        elm->key = mapkey;
-        map->count++;
-    }
-    elm->data = data;
-
-    return 0;
-}
-
-static void* ucx_map_get_and_remove(UcxMap *map, UcxKey key, int remove) {
-    if(key.hash == 0) {
-        key.hash = ucx_hash((const char*)key.data, key.len);
-    }
-    
-    size_t slot = key.hash%map->size;
-    UcxMapElement *elm = map->map[slot];
-    UcxMapElement *pelm = NULL;
-    while (elm && elm->key.hash <= key.hash) {
-        if(elm->key.hash == key.hash) {
-            int n = (key.len > elm->key.len) ? elm->key.len : key.len;
-            if (memcmp(elm->key.data, key.data, n) == 0) {
-                void *data = elm->data;
-                if (remove) {
-                    if (pelm) {
-                        pelm->next = elm->next;
-                    } else {
-                        map->map[slot] = elm->next;
-                    }
-                    alfree(map->allocator, elm->key.data);
-                    alfree(map->allocator, elm);
-                    map->count--;
-                }
-
-                return data;
-            }
-        }
-        pelm = elm;
-        elm = pelm->next;
-    }
-
-    return NULL;
-}
-
-void *ucx_map_get(UcxMap *map, UcxKey key) {
-    return ucx_map_get_and_remove(map, key, 0);
-}
-
-void *ucx_map_remove(UcxMap *map, UcxKey key) {
-    return ucx_map_get_and_remove(map, key, 1);
-}
-
-UcxKey ucx_key(const void *data, size_t len) {
-    UcxKey key;
-    key.data = data;
-    key.len = len;
-    key.hash = ucx_hash((const char*)data, len);
-    return key;
-}
-
-
-int ucx_hash(const char *data, size_t len) {
-    /* murmur hash 2 */
-
-    int m = 0x5bd1e995;
-    int r = 24;
-
-    int h = 25 ^ len;
-
-    int i = 0;
-    while (len >= 4) {
-        int k = data[i + 0] & 0xFF;
-        k |= (data[i + 1] & 0xFF) << 8;
-        k |= (data[i + 2] & 0xFF) << 16;
-        k |= (data[i + 3] & 0xFF) << 24;
-
-        k *= m;
-        k ^= k >> r;
-        k *= m;
-
-        h *= m;
-        h ^= k;
-
-        i += 4;
-        len -= 4;
-    }
-
-    switch (len) {
-        case 3: h ^= (data[i + 2] & 0xFF) << 16;
-        /* no break */
-        case 2: h ^= (data[i + 1] & 0xFF) << 8;
-        /* no break */
-        case 1: h ^= (data[i + 0] & 0xFF); h *= m;
-        /* no break */
-    }
-
-    h ^= h >> 13;
-    h *= m;
-    h ^= h >> 15;
-
-    return h;
-}
-
-UcxMapIterator ucx_map_iterator(UcxMap *map) {
-    UcxMapIterator i;
-    i.map = map;
-    i.cur = NULL;
-    i.index = 0;
-    return i;
-}
-
-int ucx_map_iter_next(UcxMapIterator *i, UcxKey *key, void **elm) {
-    UcxMapElement *e = i->cur;
-    
-    if (e) {
-        e = e->next;
-    } else {
-        e = i->map->map[0];
-    }
-    
-    while (i->index < i->map->size) {
-        if (e) {
-            if (e->data) {
-                i->cur = e;
-                *elm = e->data;
-                key->data = e->key.data;
-                key->hash = e->key.hash;
-                key->len = e->key.len;
-                return 1;
-            }
-
-            e = e->next;
-        } else {
-            i->index++;
-            
-            if (i->index < i->map->size) {
-                e = i->map->map[i->index];
-            }
-        }
-    }
-    
-    return 0;
-}
-
--- a/ucx/mempool.c	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,237 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-
-#include "ucx/mempool.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#ifdef __cplusplus
-#define __STDC_FORMAT_MACROS
-#endif
-#include <inttypes.h>
-
-/** Capsule for destructible memory chunks. */
-typedef struct {
-    /** The destructor for the memory chunk. */
-    ucx_destructor destructor;
-    /**
-     * First byte of the memory chunk.
-     * Note, that the address <code>&amp;c</code> is also the address
-     * of the whole memory chunk.
-     */
-    char c;
-} ucx_memchunk;
-
-/** Capsule for data and its destructor. */
-typedef struct {
-    /** The destructor for the data. */
-    ucx_destructor destructor;
-    /** A pointer to the data. */
-    void           *ptr;
-} ucx_regdestr;
-
-#ifdef __cplusplus
-extern "C"
-#endif
-void ucx_mempool_shared_destr(void* ptr) {
-    ucx_regdestr *rd = (ucx_regdestr*)ptr;
-    rd->destructor(rd->ptr);
-}
-
-UcxMempool *ucx_mempool_new(size_t n) {
-    size_t poolsz;
-    if(ucx_szmul(n, sizeof(void*), &poolsz)) {
-        return NULL;
-    }
-    
-    UcxMempool *pool = (UcxMempool*)malloc(sizeof(UcxMempool));
-    if (!pool) {
-        return NULL;
-    }
-    
-    pool->data = (void**) malloc(poolsz);
-    if (pool->data == NULL) {
-        free(pool);
-        return NULL;
-    }
-    
-    pool->ndata = 0;
-    pool->size = n;
-    
-    UcxAllocator *allocator = (UcxAllocator*)malloc(sizeof(UcxAllocator));
-    if(!allocator) {
-        free(pool->data);
-        free(pool);
-        return NULL;
-    }
-    allocator->malloc = (ucx_allocator_malloc)ucx_mempool_malloc;
-    allocator->calloc = (ucx_allocator_calloc)ucx_mempool_calloc;
-    allocator->realloc = (ucx_allocator_realloc)ucx_mempool_realloc;
-    allocator->free = (ucx_allocator_free)ucx_mempool_free;
-    allocator->pool = pool;
-    pool->allocator = allocator;
-    
-    return pool;
-}
-
-int ucx_mempool_chcap(UcxMempool *pool, size_t newcap) {
-    if (newcap < pool->ndata) {
-        return 1;
-    }
-    
-    size_t newcapsz;
-    if(ucx_szmul(newcap, sizeof(void*), &newcapsz)) {
-        return 1;
-    }
-    
-    void **data = (void**) realloc(pool->data, newcapsz);
-    if (data) {
-        pool->data = data; 
-        pool->size = newcap;
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-void *ucx_mempool_malloc(UcxMempool *pool, size_t n) {
-    if(((size_t)-1) - sizeof(ucx_destructor) < n) {
-        return NULL;
-    }
-    
-    if (pool->ndata >= pool->size) {
-        size_t newcap = pool->size*2;
-        if (newcap < pool->size || ucx_mempool_chcap(pool, newcap)) {
-            return NULL;
-        }
-    }
-
-    void *p = malloc(sizeof(ucx_destructor) + n);
-    ucx_memchunk *mem = (ucx_memchunk*)p;
-    if (!mem) {
-        return NULL;
-    }
-
-    mem->destructor = NULL;
-    pool->data[pool->ndata] = mem;
-    pool->ndata++;
-
-    return &(mem->c);
-}
-
-void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize) {
-    size_t msz;
-    if(ucx_szmul(nelem, elsize, &msz)) {
-        return NULL;
-    }
-    
-    void *ptr = ucx_mempool_malloc(pool, msz);
-    if (!ptr) {
-        return NULL;
-    }
-    memset(ptr, 0, nelem * elsize);
-    return ptr;
-}
-
-void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n) {
-    if(((size_t)-1) - sizeof(ucx_destructor) < n) {
-        return NULL;
-    }
-    
-    char *mem = ((char*)ptr) - sizeof(ucx_destructor);
-    char *newm = (char*) realloc(mem, n + sizeof(ucx_destructor));
-    if (!newm) {
-        return NULL;
-    }
-    if (mem != newm) {
-        for(size_t i=0 ; i < pool->ndata ; i++) {
-            if(pool->data[i] == mem) {
-                pool->data[i] = newm;
-                return newm + sizeof(ucx_destructor);
-            }
-        }
-        fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
-          (intptr_t)ptr, (intptr_t)pool);
-        abort();
-    } else {
-        return newm + sizeof(ucx_destructor);
-    }
-}
-
-void ucx_mempool_free(UcxMempool *pool, void *ptr) {
-    ucx_memchunk *chunk = (ucx_memchunk*)((char*)ptr-sizeof(ucx_destructor));
-    for(size_t i=0 ; i<pool->ndata ; i++) {
-        if(chunk == pool->data[i]) {
-            if(chunk->destructor != NULL) {
-                chunk->destructor(&(chunk->c));
-            }
-            free(chunk);
-            size_t last_index = pool->ndata - 1;
-            if(i != last_index) {
-                pool->data[i] = pool->data[last_index];
-                pool->data[last_index] = NULL;
-            }
-            pool->ndata--;
-            return;
-        }
-    }
-    fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
-            (intptr_t)ptr, (intptr_t)pool);
-    abort();
-}
-
-void ucx_mempool_destroy(UcxMempool *pool) {
-    ucx_memchunk *chunk;
-    for(size_t i=0 ; i<pool->ndata ; i++) {
-        chunk = (ucx_memchunk*) pool->data[i];
-        if(chunk) {
-            if(chunk->destructor) {
-                chunk->destructor(&(chunk->c));
-            }
-            free(chunk);
-        }
-    }
-    free(pool->data);
-    free(pool->allocator);
-    free(pool);
-}
-
-void ucx_mempool_set_destr(void *ptr, ucx_destructor func) {
-    *(ucx_destructor*)((char*)ptr-sizeof(ucx_destructor)) = func;
-}
-
-void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr) {
-    ucx_regdestr *rd = (ucx_regdestr*)ucx_mempool_malloc(
-            pool,
-            sizeof(ucx_regdestr));
-    rd->destructor = destr;
-    rd->ptr = ptr;
-    ucx_mempool_set_destr(rd, ucx_mempool_shared_destr);
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/printf.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#include "cx/printf.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifndef CX_PRINTF_SBO_SIZE
+#define CX_PRINTF_SBO_SIZE 512
+#endif
+
+int cx_fprintf(
+        void *stream,
+        cx_write_func wfc,
+        char const *fmt,
+        ...
+) {
+    int ret;
+    va_list ap;
+    va_start(ap, fmt);
+    ret = cx_vfprintf(stream, wfc, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+int cx_vfprintf(
+        void *stream,
+        cx_write_func wfc,
+        char const *fmt,
+        va_list ap
+) {
+    char buf[CX_PRINTF_SBO_SIZE];
+    va_list ap2;
+    va_copy(ap2, ap);
+    int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap);
+    if (ret < 0) {
+        return ret;
+    } else if (ret < CX_PRINTF_SBO_SIZE) {
+        return (int) wfc(buf, 1, ret, stream);
+    } else {
+        int len = ret + 1;
+        char *newbuf = malloc(len);
+        if (!newbuf) {
+            return -1;
+        }
+
+        ret = vsnprintf(newbuf, len, fmt, ap2);
+        if (ret > 0) {
+            ret = (int) wfc(newbuf, 1, ret, stream);
+        }
+        free(newbuf);
+    }
+    return ret;
+}
+
+cxmutstr cx_asprintf_a(
+        CxAllocator const *allocator,
+        char const *fmt,
+        ...
+) {
+    va_list ap;
+    cxmutstr ret;
+    va_start(ap, fmt);
+    ret = cx_vasprintf_a(allocator, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+cxmutstr cx_vasprintf_a(
+        CxAllocator const *a,
+        char const *fmt,
+        va_list ap
+) {
+    cxmutstr s;
+    s.ptr = NULL;
+    s.length = 0;
+    char buf[CX_PRINTF_SBO_SIZE];
+    va_list ap2;
+    va_copy(ap2, ap);
+    int ret = vsnprintf(buf, CX_PRINTF_SBO_SIZE, fmt, ap);
+    if (ret > 0 && ret < CX_PRINTF_SBO_SIZE) {
+        s.ptr = cxMalloc(a, ret + 1);
+        if (s.ptr) {
+            s.length = (size_t) ret;
+            memcpy(s.ptr, buf, ret);
+            s.ptr[s.length] = '\0';
+        }
+    } else {
+        int len = ret + 1;
+        s.ptr = cxMalloc(a, len);
+        if (s.ptr) {
+            ret = vsnprintf(s.ptr, len, fmt, ap2);
+            if (ret < 0) {
+                free(s.ptr);
+                s.ptr = NULL;
+            } else {
+                s.length = (size_t) ret;
+            }
+        }
+    }
+    return s;
+}
+
--- a/ucx/properties.c	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,264 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-
-#include "ucx/properties.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-UcxProperties *ucx_properties_new() {
-    UcxProperties *parser = (UcxProperties*)malloc(
-            sizeof(UcxProperties));
-    if(!parser) {
-        return NULL;
-    }
-    
-    parser->buffer = NULL;
-    parser->buflen = 0;
-    parser->pos = 0;
-    parser->tmp = NULL;
-    parser->tmplen = 0;
-    parser->tmpcap = 0;
-    parser->error = 0;
-    parser->delimiter = '=';
-    parser->comment1 = '#';
-    parser->comment2 = 0;
-    parser->comment3 = 0;   
-    
-    return parser;
-}
-
-void ucx_properties_free(UcxProperties *parser) {
-    if(parser->tmp) {
-        free(parser->tmp);
-    }
-    free(parser);
-}
-
-void ucx_properties_fill(UcxProperties *parser, char *buf, size_t len) {
-    parser->buffer = buf;
-    parser->buflen = len;
-    parser->pos = 0;
-}
-
-static void parser_tmp_append(UcxProperties *parser, char *buf, size_t len) {
-    if(parser->tmpcap - parser->tmplen < len) {
-        size_t newcap = parser->tmpcap + len + 64;
-        parser->tmp = (char*)realloc(parser->tmp, newcap);
-        parser->tmpcap = newcap;
-    }
-    memcpy(parser->tmp + parser->tmplen, buf, len);
-    parser->tmplen += len;
-}
-
-int ucx_properties_next(UcxProperties *parser, sstr_t *name, sstr_t *value)  {   
-    if(parser->tmplen > 0) {
-        char *buf = parser->buffer + parser->pos;
-        size_t len = parser->buflen - parser->pos;
-        sstr_t str = sstrn(buf, len);
-        sstr_t nl = sstrchr(str, '\n');
-        if(nl.ptr) {
-            size_t newlen = (size_t)(nl.ptr - buf) + 1;
-            parser_tmp_append(parser, buf, newlen);
-            // the tmp buffer contains exactly one line now
-            
-            char *orig_buf = parser->buffer;
-            size_t orig_len = parser->buflen;
-            
-            parser->buffer = parser->tmp;
-            parser->buflen = parser->tmplen;
-            parser->pos = 0;    
-            parser->tmp = NULL;
-            parser->tmpcap = 0;
-            parser->tmplen = 0;
-            // run ucx_properties_next with the tmp buffer as main buffer
-            int ret = ucx_properties_next(parser, name, value);
-            
-            // restore original buffer
-            parser->tmp = parser->buffer;
-            parser->buffer = orig_buf;
-            parser->buflen = orig_len;
-            parser->pos = newlen;
-            
-            /*
-             * if ret == 0 the tmp buffer contained just space or a comment
-             * we parse again with the original buffer to get a name/value
-             * or a new tmp buffer
-             */
-            return ret ? ret : ucx_properties_next(parser, name, value);
-        } else {
-            parser_tmp_append(parser, buf, len);
-            return 0;
-        }
-    } else if(parser->tmp) {
-        free(parser->tmp);
-        parser->tmp = NULL;
-    }
-    
-    char comment1 = parser->comment1;
-    char comment2 = parser->comment2;
-    char comment3 = parser->comment3;
-    char delimiter = parser->delimiter;
-    
-    // get one line and parse it
-    while(parser->pos < parser->buflen) {
-        char *buf = parser->buffer + parser->pos;
-        size_t len = parser->buflen - parser->pos;
-        
-        /*
-         * First we check if we have at least one line. We also get indices of
-         * delimiter and comment chars
-         */
-        size_t delimiter_index = 0;
-        size_t comment_index = 0;
-        int has_comment = 0;
-
-        size_t i = 0;
-        char c = 0;
-        for(;i<len;i++) {
-            c = buf[i];
-            if(c == comment1 || c == comment2 || c == comment3) {
-                if(comment_index == 0) {
-                    comment_index = i;
-                    has_comment = 1;
-                }
-            } else if(c == delimiter) {
-                if(delimiter_index == 0 && !has_comment) {
-                    delimiter_index = i;
-                }
-            } else if(c == '\n') {
-                break;
-            }
-        }
-
-        if(c != '\n') {
-            // we don't have enough data for a line
-            // store remaining bytes in temporary buffer for next round
-            parser->tmpcap = len + 128;
-            parser->tmp = (char*)malloc(parser->tmpcap);
-            parser->tmplen = len;
-            memcpy(parser->tmp, buf, len);
-            return 0;
-        }
-        
-        sstr_t line = has_comment ? sstrn(buf, comment_index) : sstrn(buf, i);
-        // check line
-        if(delimiter_index == 0) {
-            line = sstrtrim(line);
-            if(line.length != 0) {
-                parser->error = 1;
-            }
-        } else {
-            sstr_t n = sstrn(buf, delimiter_index);
-            sstr_t v = sstrn(
-                    buf + delimiter_index + 1,
-                    line.length - delimiter_index - 1); 
-            n = sstrtrim(n);
-            v = sstrtrim(v);
-            if(n.length != 0 || v.length != 0) {
-                *name = n;
-                *value = v;
-                parser->pos += i + 1;
-                return 1;
-            } else {
-                parser->error = 1;
-            }
-        }
-        
-        parser->pos += i + 1;
-    }
-    
-    return 0;
-}
-
-int ucx_properties2map(UcxProperties *parser, UcxMap *map) {
-    sstr_t name;
-    sstr_t value;
-    while(ucx_properties_next(parser, &name, &value)) {
-        value = sstrdup_a(map->allocator, value);
-        if(!value.ptr) {
-            return 1;
-        }
-        if(ucx_map_sstr_put(map, name, value.ptr)) {
-            alfree(map->allocator, value.ptr);
-            return 1;
-        }
-    }
-    if (parser->error) {
-        return parser->error;
-    } else {
-        return 0;
-    }
-}
-
-// buffer size is documented - change doc, when you change bufsize!
-#define UCX_PROPLOAD_BUFSIZE  1024
-int ucx_properties_load(UcxMap *map, FILE *file) {
-    UcxProperties *parser = ucx_properties_new();
-    if(!(parser && map && file)) {
-        return 1;
-    }
-    
-    int error = 0;
-    size_t r;
-    char buf[UCX_PROPLOAD_BUFSIZE];
-    while((r = fread(buf, 1, UCX_PROPLOAD_BUFSIZE, file)) != 0) {
-        ucx_properties_fill(parser, buf, r);
-        error = ucx_properties2map(parser, map);
-        if (error) {
-            break;
-        }
-    }
-    ucx_properties_free(parser);
-    return error;
-}
-
-int ucx_properties_store(UcxMap *map, FILE *file) {
-    UcxMapIterator iter = ucx_map_iterator(map);
-    void *v;
-    sstr_t value;
-    size_t written;
-
-    UCX_MAP_FOREACH(k, v, iter) {
-        value = sstr((char*)v);
-
-        written = 0;
-        written += fwrite(k.data, 1, k.len, file);
-        written += fwrite(" = ", 1, 3, file);
-        written += fwrite(value.ptr, 1, value.length, file);
-        written += fwrite("\n", 1, 1, file);
-
-        if (written != k.len + value.length + 4) {
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
--- a/ucx/stack.c	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-
-#include "ucx/stack.h"
-
-#include <string.h>
-
-static size_t ucx_stack_align(size_t n) {
-    int align = n % sizeof(void*);
-    if (align) {
-        n += sizeof(void*) - align;
-    }
-    return n;
-}
-
-void ucx_stack_init(UcxStack *stack, char* space, size_t size) {
-    stack->size = size - size % sizeof(void*);
-    stack->space = space;
-    stack->top = NULL;
-    
-    stack->allocator.pool = stack;
-    stack->allocator.malloc = (ucx_allocator_malloc) ucx_stack_malloc;
-    stack->allocator.calloc = (ucx_allocator_calloc) ucx_stack_calloc;
-    stack->allocator.realloc = (ucx_allocator_realloc) ucx_stack_realloc;
-    stack->allocator.free = (ucx_allocator_free) ucx_stack_free;
-}
-
-void *ucx_stack_malloc(UcxStack *stack, size_t n) {
-
-    if (ucx_stack_avail(stack) < ucx_stack_align(n)) {
-        return NULL;
-    } else {
-        char *prev = stack->top;
-        if (stack->top) {
-            stack->top += ucx_stack_align(ucx_stack_topsize(stack));
-        } else {
-            stack->top = stack->space;
-        }
-        
-        ((struct ucx_stack_metadata*)stack->top)->prev = prev;
-        ((struct ucx_stack_metadata*)stack->top)->size = n;
-        stack->top += sizeof(struct ucx_stack_metadata);
-        
-        return stack->top;
-    }
-}
-
-void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize) {
-    void *mem = ucx_stack_malloc(stack, nelem*elsize);
-    memset(mem, 0, nelem*elsize);
-    return mem;
-}
-
-void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n) {
-    if (ptr == stack->top) {
-        if (stack->size - (stack->top - stack->space) < ucx_stack_align(n)) {
-            return NULL;
-        } else {
-            ((struct ucx_stack_metadata*)stack->top - 1)->size = n;
-            return ptr;
-        }
-    } else {
-        if (ucx_stack_align(((struct ucx_stack_metadata*)ptr - 1)->size) <
-                ucx_stack_align(n)) {
-            void *nptr = ucx_stack_malloc(stack, n);
-            if (nptr) {
-                memcpy(nptr, ptr, n);
-                ucx_stack_free(stack, ptr);
-                
-                return nptr;
-            } else {
-                return NULL;
-            }
-        } else {
-            ((struct ucx_stack_metadata*)ptr - 1)->size = n;
-            return ptr;
-        }
-    }
-}
-
-void ucx_stack_free(UcxStack *stack, void *ptr) {
-    if (ptr == stack->top) {
-        stack->top = ((struct ucx_stack_metadata*) stack->top - 1)->prev;
-    } else {
-        struct ucx_stack_metadata *next = (struct ucx_stack_metadata*)(
-            (char*)ptr +
-            ucx_stack_align(((struct ucx_stack_metadata*) ptr - 1)->size)
-        );
-        next->prev = ((struct ucx_stack_metadata*) ptr - 1)->prev;
-    }
-}
-
-void ucx_stack_popn(UcxStack *stack, void *dest, size_t n) {
-    if (ucx_stack_empty(stack)) {
-        return;
-    }
-    
-    if (dest) {
-        size_t len = ucx_stack_topsize(stack);
-        if (len > n) {
-            len = n;
-        }
-
-        memcpy(dest, stack->top, len);
-    }
-    
-    ucx_stack_free(stack, stack->top);
-}
-
-size_t ucx_stack_avail(UcxStack *stack) {
-    size_t avail = ((stack->top ? (stack->size
-                    - (stack->top - stack->space)
-                    - ucx_stack_align(ucx_stack_topsize(stack)))
-                    : stack->size));
-    
-    if (avail > sizeof(struct ucx_stack_metadata)) {
-        return avail - sizeof(struct ucx_stack_metadata);
-    } else {
-        return 0;
-    }
-}
-
-void *ucx_stack_push(UcxStack *stack, size_t n, const void *data) {
-    void *space = ucx_stack_malloc(stack, n);
-    if (space) {
-        memcpy(space, data, n);
-    }
-    return space;
-}
-
-void *ucx_stack_pusharr(UcxStack *stack,
-        size_t nelem, size_t elsize, const void *data) {
-    
-    // skip the memset by using malloc
-    void *space = ucx_stack_malloc(stack, nelem*elsize);
-    if (space) {
-        memcpy(space, data, nelem*elsize);
-    }
-    return space;
-}
--- a/ucx/string.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/ucx/string.c	Fri Apr 21 21:25:32 2023 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * 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:
@@ -26,57 +26,71 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx/string.h"
+#include "cx/string.h"
+#include "cx/utils.h"
 
-#include "ucx/allocator.h"
-
-#include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
-#include <stdint.h>
 #include <ctype.h>
 
-sstr_t sstr(char *cstring) {
-    sstr_t string;
-    string.ptr = cstring;
-    string.length = strlen(cstring);
-    return string;
+#ifndef _WIN32
+
+#include <strings.h> // for strncasecmp()
+
+#endif // _WIN32
+
+cxmutstr cx_mutstr(char *cstring) {
+    return (cxmutstr) {cstring, strlen(cstring)};
 }
 
-sstr_t sstrn(char *cstring, size_t length) {
-    sstr_t string;
-    string.ptr = cstring;
-    string.length = length;
-    return string;
+cxmutstr cx_mutstrn(
+        char *cstring,
+        size_t length
+) {
+    return (cxmutstr) {cstring, length};
+}
+
+cxstring cx_str(const char *cstring) {
+    return (cxstring) {cstring, strlen(cstring)};
 }
 
-scstr_t scstr(const char *cstring) {
-    scstr_t string;
-    string.ptr = cstring;
-    string.length = strlen(cstring);
-    return string;
+cxstring cx_strn(
+        const char *cstring,
+        size_t length
+) {
+    return (cxstring) {cstring, length};
 }
 
-scstr_t scstrn(const char *cstring, size_t length) {
-    scstr_t string;
-    string.ptr = cstring;
-    string.length = length;
-    return string;
+cxstring cx_strcast(cxmutstr str) {
+    return (cxstring) {str.ptr, str.length};
+}
+
+void cx_strfree(cxmutstr *str) {
+    free(str->ptr);
+    str->ptr = NULL;
+    str->length = 0;
 }
 
-
-size_t scstrnlen(size_t n, ...) {
-    va_list ap;
-    va_start(ap, n);
-    
-    size_t size = 0;
+void cx_strfree_a(
+        CxAllocator const *alloc,
+        cxmutstr *str
+) {
+    cxFree(alloc, str->ptr);
+    str->ptr = NULL;
+    str->length = 0;
+}
 
-    for (size_t i = 0 ; i < n ; i++) {
-        scstr_t str = va_arg(ap, scstr_t);
-        if(SIZE_MAX - str.length < size) {
-            size = SIZE_MAX;
-            break;
-        }
+size_t cx_strlen(
+        size_t count,
+        ...
+) {
+    if (count == 0) return 0;
+
+    va_list ap;
+    va_start(ap, count);
+    size_t size = 0;
+    cx_for_n(i, count) {
+        cxstring str = va_arg(ap, cxstring);
         size += str.length;
     }
     va_end(ap);
@@ -84,410 +98,333 @@
     return size;
 }
 
-static sstr_t sstrvcat_a(
-        UcxAllocator *a,
+cxmutstr cx_strcat_a(
+        CxAllocator const *alloc,
         size_t count,
-        scstr_t s1,
-        va_list ap) {
-    sstr_t str;
-    str.ptr = NULL;
-    str.length = 0;
-    if(count < 2) {
-        return str;
-    }
-    
-    scstr_t s2 = va_arg (ap, scstr_t);
-    
-    if(((size_t)-1) - s1.length < s2.length) {
-        return str;
-    }
-    
-    scstr_t *strings = (scstr_t*) calloc(count, sizeof(scstr_t));
-    if(!strings) {
-        return str;
-    }
-    
+        ...
+) {
+    cxstring *strings = calloc(count, sizeof(cxstring));
+    if (!strings) abort();
+
+    va_list ap;
+    va_start(ap, count);
+
     // get all args and overall length
-    strings[0] = s1;
-    strings[1] = s2;
-    size_t slen = s1.length + s2.length;
-    int error = 0;
-    for (size_t i=2;i<count;i++) {
-        scstr_t s = va_arg (ap, scstr_t);
+    size_t slen = 0;
+    cx_for_n(i, count) {
+        cxstring s = va_arg (ap, cxstring);
         strings[i] = s;
-        if(((size_t)-1) - s.length < slen) {
-            error = 1;
-            break;
-        }
         slen += s.length;
     }
-    if(error) {
-        free(strings);
-        return str;
-    }
-    
+
     // create new string
-    str.ptr = (char*) almalloc(a, slen + 1);
-    str.length = slen;
-    if(!str.ptr) {
-        free(strings);
-        str.length = 0;
-        return str;
-    }
-    
+    cxmutstr result;
+    result.ptr = cxMalloc(alloc, slen + 1);
+    result.length = slen;
+    if (result.ptr == NULL) abort();
+
     // concatenate strings
     size_t pos = 0;
-    for (size_t i=0;i<count;i++) {
-        scstr_t s = strings[i];
-        memcpy(str.ptr + pos, s.ptr, s.length);
+    cx_for_n(i, count) {
+        cxstring s = strings[i];
+        memcpy(result.ptr + pos, s.ptr, s.length);
         pos += s.length;
     }
-    
-    str.ptr[str.length] = '\0';
-    
+
+    // terminate string
+    result.ptr[result.length] = '\0';
+
+    // free temporary array
     free(strings);
-    
-    return str;
+
+    return result;
 }
 
-sstr_t scstrcat(size_t count, scstr_t s1, ...) {
-    va_list ap;
-    va_start(ap, s1);
-    sstr_t s = sstrvcat_a(ucx_default_allocator(), count, s1, ap);
-    va_end(ap);
-    return s;
+cxstring cx_strsubs(
+        cxstring string,
+        size_t start
+) {
+    return cx_strsubsl(string, start, string.length - start);
 }
 
-sstr_t scstrcat_a(UcxAllocator *a, size_t count, scstr_t s1, ...) {
-    va_list ap;
-    va_start(ap, s1);
-    sstr_t s = sstrvcat_a(a, count, s1, ap);
-    va_end(ap);
-    return s;
+cxmutstr cx_strsubs_m(
+        cxmutstr string,
+        size_t start
+) {
+    return cx_strsubsl_m(string, start, string.length - start);
 }
 
-static int ucx_substring(
-        size_t str_length,
+cxstring cx_strsubsl(
+        cxstring string,
         size_t start,
-        size_t length,
-        size_t *newlen,
-        size_t *newpos)
-{
-    *newlen = 0;
-    *newpos = 0;
-    
-    if(start > str_length) {
-        return 0;
+        size_t length
+) {
+    if (start > string.length) {
+        return (cxstring) {NULL, 0};
     }
-    
-    if(length > str_length - start) {
-        length = str_length - start;
+
+    size_t rem_len = string.length - start;
+    if (length > rem_len) {
+        length = rem_len;
     }
-    *newlen = length;
-    *newpos = start;
-    return 1;
+
+    return (cxstring) {string.ptr + start, length};
 }
 
-sstr_t sstrsubs(sstr_t s, size_t start) {
-    return sstrsubsl (s, start, s.length-start);
+cxmutstr cx_strsubsl_m(
+        cxmutstr string,
+        size_t start,
+        size_t length
+) {
+    cxstring result = cx_strsubsl(cx_strcast(string), start, length);
+    return (cxmutstr) {(char *) result.ptr, result.length};
 }
 
-sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) {
-    size_t pos;
-    sstr_t ret = { NULL, 0 };
-    if(ucx_substring(s.length, start, length, &ret.length, &pos)) {
-        ret.ptr = s.ptr + pos;
+cxstring cx_strchr(
+        cxstring string,
+        int chr
+) {
+    chr = 0xFF & chr;
+    // TODO: improve by comparing multiple bytes at once
+    cx_for_n(i, string.length) {
+        if (string.ptr[i] == chr) {
+            return cx_strsubs(string, i);
+        }
     }
-    return ret;
-}
-
-scstr_t scstrsubs(scstr_t string, size_t start) {
-    return scstrsubsl(string, start, string.length-start);
+    return (cxstring) {NULL, 0};
 }
 
-scstr_t scstrsubsl(scstr_t s, size_t start, size_t length) {
-    size_t pos;
-    scstr_t ret = { NULL, 0 };
-    if(ucx_substring(s.length, start, length, &ret.length, &pos)) {
-        ret.ptr = s.ptr + pos;
-    }
-    return ret;
+cxmutstr cx_strchr_m(
+        cxmutstr string,
+        int chr
+) {
+    cxstring result = cx_strchr(cx_strcast(string), chr);
+    return (cxmutstr) {(char *) result.ptr, result.length};
 }
 
-
-static int ucx_strchr(const char *str, size_t length, int chr, size_t *pos) {
-    for(size_t i=0;i<length;i++) {
-        if(str[i] == chr) {
-            *pos = i;
-            return 1;
-        }
-    }
-    return 0;
-}
-
-static int ucx_strrchr(const char *str, size_t length, int chr, size_t *pos) {
-    if(length > 0) {
-        for(size_t i=length ; i>0 ; i--) {
-            if(str[i-1] == chr) {
-                *pos = i-1;
-                return 1;
-            }
+cxstring cx_strrchr(
+        cxstring string,
+        int chr
+) {
+    chr = 0xFF & chr;
+    size_t i = string.length;
+    while (i > 0) {
+        i--;
+        // TODO: improve by comparing multiple bytes at once
+        if (string.ptr[i] == chr) {
+            return cx_strsubs(string, i);
         }
     }
-    return 0;
-}
-
-sstr_t sstrchr(sstr_t s, int c) {
-    size_t pos = 0;
-    if(ucx_strchr(s.ptr, s.length, c, &pos)) {
-        return sstrsubs(s, pos);
-    }
-    return sstrn(NULL, 0);
+    return (cxstring) {NULL, 0};
 }
 
-sstr_t sstrrchr(sstr_t s, int c) {
-    size_t pos = 0;
-    if(ucx_strrchr(s.ptr, s.length, c, &pos)) {
-        return sstrsubs(s, pos);
-    }
-    return sstrn(NULL, 0);
-}
-
-scstr_t scstrchr(scstr_t s, int c) {
-    size_t pos = 0;
-    if(ucx_strchr(s.ptr, s.length, c, &pos)) {
-        return scstrsubs(s, pos);
-    }
-    return scstrn(NULL, 0);
-}
-
-scstr_t scstrrchr(scstr_t s, int c) {
-    size_t pos = 0;
-    if(ucx_strrchr(s.ptr, s.length, c, &pos)) {
-        return scstrsubs(s, pos);
-    }
-    return scstrn(NULL, 0);
+cxmutstr cx_strrchr_m(
+        cxmutstr string,
+        int chr
+) {
+    cxstring result = cx_strrchr(cx_strcast(string), chr);
+    return (cxmutstr) {(char *) result.ptr, result.length};
 }
 
-#define ptable_r(dest, useheap, ptable, index) (dest = useheap ? \
-    ((size_t*)ptable)[index] : (size_t) ((uint8_t*)ptable)[index])
-
-#define ptable_w(useheap, ptable, index, src) do {\
-    if (!useheap) ((uint8_t*)ptable)[index] = (uint8_t) src;\
-    else ((size_t*)ptable)[index] = src;\
-    } while (0);
-
+#ifndef CX_STRSTR_SBO_SIZE
+#define CX_STRSTR_SBO_SIZE 512
+#endif
 
-static const char* ucx_strstr(
-        const char *str,
-        size_t length,
-        const char *match,
-        size_t matchlen,
-        size_t *newlen)
-{
-    *newlen = length;
-    if (matchlen == 0) {
-        return str;
+cxstring cx_strstr(
+        cxstring haystack,
+        cxstring needle
+) {
+    if (needle.length == 0) {
+        return haystack;
     }
-    
-    const char *result = NULL;
-    size_t resultlen = 0;
-    
+
+    // optimize for single-char needles
+    if (needle.length == 1) {
+        return cx_strchr(haystack, *needle.ptr);
+    }
+
     /*
      * IMPORTANT:
-     * our prefix table contains the prefix length PLUS ONE
-     * this is our decision, because we want to use the full range of size_t
-     * the original algorithm needs a (-1) at one single place
-     * and we want to avoid that
+     * Our prefix table contains the prefix length PLUS ONE
+     * this is our decision, because we want to use the full range of size_t.
+     * The original algorithm needs a (-1) at one single place,
+     * and we want to avoid that.
      */
-    
-    /* static prefix table */
-    static uint8_t s_prefix_table[256];
-    
-    /* check pattern length and use appropriate prefix table */
-    /* if the pattern exceeds static prefix table, allocate on the heap */
-    register int useheap = matchlen > 255;
-    register void* ptable = useheap ?
-        calloc(matchlen+1, sizeof(size_t)): s_prefix_table;
-    
-    /* keep counter in registers */
+
+    // local prefix table
+    size_t s_prefix_table[CX_STRSTR_SBO_SIZE];
+
+    // check needle length and use appropriate prefix table
+    // if the pattern exceeds static prefix table, allocate on the heap
+    bool useheap = needle.length >= CX_STRSTR_SBO_SIZE;
+    register size_t *ptable = useheap ? calloc(needle.length + 1,
+                                               sizeof(size_t)) : s_prefix_table;
+
+    // keep counter in registers
     register size_t i, j;
-    
-    /* fill prefix table */
-    i = 0; j = 0;
-    ptable_w(useheap, ptable, i, j);
-    while (i < matchlen) {
-        while (j >= 1 && match[j-1] != match[i]) {
-            ptable_r(j, useheap, ptable, j-1);
+
+    // fill prefix table
+    i = 0;
+    j = 0;
+    ptable[i] = j;
+    while (i < needle.length) {
+        while (j >= 1 && needle.ptr[j - 1] != needle.ptr[i]) {
+            j = ptable[j - 1];
         }
-        i++; j++;
-        ptable_w(useheap, ptable, i, j);
+        i++;
+        j++;
+        ptable[i] = j;
     }
 
-    /* search */
-    i = 0; j = 1;
-    while (i < length) {
-        while (j >= 1 && str[i] != match[j-1]) {
-            ptable_r(j, useheap, ptable, j-1);
+    // search
+    cxstring result = {NULL, 0};
+    i = 0;
+    j = 1;
+    while (i < haystack.length) {
+        while (j >= 1 && haystack.ptr[i] != needle.ptr[j - 1]) {
+            j = ptable[j - 1];
         }
-        i++; j++;
-        if (j-1 == matchlen) {
-            size_t start = i - matchlen;
-            result = str + start;
-            resultlen = length - start;
+        i++;
+        j++;
+        if (j - 1 == needle.length) {
+            size_t start = i - needle.length;
+            result.ptr = haystack.ptr + start;
+            result.length = haystack.length - start;
             break;
         }
     }
 
-    /* if prefix table was allocated on the heap, free it */
+    // if prefix table was allocated on the heap, free it
     if (ptable != s_prefix_table) {
         free(ptable);
     }
-    
-    *newlen = resultlen;
-    return result;
-}
-
-sstr_t scstrsstr(sstr_t string, scstr_t match) {
-    sstr_t result;
-    
-    size_t reslen;
-    const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen);
-    if(!resstr) {
-        result.ptr = NULL;
-        result.length = 0;
-        return result;
-    }
-    
-    size_t pos = resstr - string.ptr;
-    result.ptr = string.ptr + pos;
-    result.length = reslen;
-    
-    return result;
-}
-
-scstr_t scstrscstr(scstr_t string, scstr_t match) {
-    scstr_t result;
-    
-    size_t reslen;
-    const char *resstr = ucx_strstr(string.ptr, string.length, match.ptr, match.length, &reslen);
-    if(!resstr) {
-        result.ptr = NULL;
-        result.length = 0;
-        return result;
-    }
-    
-    size_t pos = resstr - string.ptr;
-    result.ptr = string.ptr + pos;
-    result.length = reslen;
-    
-    return result;
-}
-
-#undef ptable_r
-#undef ptable_w
-
-sstr_t* scstrsplit(scstr_t s, scstr_t d, ssize_t *n) {
-    return scstrsplit_a(ucx_default_allocator(), s, d, n);
-}
-
-sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t s, scstr_t d, ssize_t *n) {
-    if (s.length == 0 || d.length == 0) {
-        *n = -1;
-        return NULL;
-    }
-    
-    /* special cases: delimiter is at least as large as the string */
-    if (d.length >= s.length) {
-        /* exact match */
-        if (sstrcmp(s, d) == 0) {
-            *n = 0;
-            return NULL;
-        } else /* no match possible */ {
-            *n = 1;
-            sstr_t *result = (sstr_t*) almalloc(allocator, sizeof(sstr_t));
-            if(result) {
-                *result = sstrdup_a(allocator, s);
-            } else {
-                *n = -2;
-            }
-            return result;
-        }
-    }
-    
-    ssize_t nmax = *n;
-    size_t arrlen = 16;
-    sstr_t* result = (sstr_t*) alcalloc(allocator, arrlen, sizeof(sstr_t));
-
-    if (result) {
-        scstr_t curpos = s;
-        ssize_t j = 1;
-        while (1) {
-            scstr_t match;
-            /* optimize for one byte delimiters */
-            if (d.length == 1) {
-                match = curpos;
-                for (size_t i = 0 ; i < curpos.length ; i++) {
-                    if (curpos.ptr[i] == *(d.ptr)) {
-                        match.ptr = curpos.ptr + i;
-                        break;
-                    }
-                    match.length--;
-                }
-            } else {
-                match = scstrscstr(curpos, d);
-            }
-            if (match.length > 0) {
-                /* is this our last try? */
-                if (nmax == 0 || j < nmax) {
-                    /* copy the current string to the array */
-                    scstr_t item = scstrn(curpos.ptr, match.ptr - curpos.ptr);
-                    result[j-1] = sstrdup_a(allocator, item);
-                    size_t processed = item.length + d.length;
-                    curpos.ptr += processed;
-                    curpos.length -= processed;
-
-                    /* allocate memory for the next string */
-                    j++;
-                    if (j > arrlen) {
-                        arrlen *= 2;
-                        size_t reallocsz;
-                        sstr_t* reallocated = NULL;
-                        if(!ucx_szmul(arrlen, sizeof(sstr_t), &reallocsz)) {
-                            reallocated = (sstr_t*) alrealloc(
-                                    allocator, result, reallocsz);
-                        }
-                        if (reallocated) {
-                            result = reallocated;
-                        } else {
-                            for (ssize_t i = 0 ; i < j-1 ; i++) {
-                                alfree(allocator, result[i].ptr);
-                            }
-                            alfree(allocator, result);
-                            *n = -2;
-                            return NULL;
-                        }
-                    }
-                } else {
-                    /* nmax reached, copy the _full_ remaining string */
-                    result[j-1] = sstrdup_a(allocator, curpos);
-                    break;
-                }
-            } else {
-                /* no more matches, copy last string */
-                result[j-1] = sstrdup_a(allocator, curpos);
-                break;
-            }
-        }
-        *n = j;
-    } else {
-        *n = -2;
-    }
 
     return result;
 }
 
-int scstrcmp(scstr_t s1, scstr_t s2) {
+cxmutstr cx_strstr_m(
+        cxmutstr haystack,
+        cxstring needle
+) {
+    cxstring result = cx_strstr(cx_strcast(haystack), needle);
+    return (cxmutstr) {(char *) result.ptr, result.length};
+}
+
+size_t cx_strsplit(
+        cxstring string,
+        cxstring delim,
+        size_t limit,
+        cxstring *output
+) {
+    // special case: output limit is zero
+    if (limit == 0) return 0;
+
+    // special case: delimiter is empty
+    if (delim.length == 0) {
+        output[0] = string;
+        return 1;
+    }
+
+    // special cases: delimiter is at least as large as the string
+    if (delim.length >= string.length) {
+        // exact match
+        if (cx_strcmp(string, delim) == 0) {
+            output[0] = cx_strn(string.ptr, 0);
+            output[1] = cx_strn(string.ptr + string.length, 0);
+            return 2;
+        } else {
+            // no match possible
+            output[0] = string;
+            return 1;
+        }
+    }
+
+    size_t n = 0;
+    cxstring curpos = string;
+    while (1) {
+        ++n;
+        cxstring match = cx_strstr(curpos, delim);
+        if (match.length > 0) {
+            // is the limit reached?
+            if (n < limit) {
+                // copy the current string to the array
+                cxstring item = cx_strn(curpos.ptr, match.ptr - curpos.ptr);
+                output[n - 1] = item;
+                size_t processed = item.length + delim.length;
+                curpos.ptr += processed;
+                curpos.length -= processed;
+            } else {
+                // limit reached, copy the _full_ remaining string
+                output[n - 1] = curpos;
+                break;
+            }
+        } else {
+            // no more matches, copy last string
+            output[n - 1] = curpos;
+            break;
+        }
+    }
+
+    return n;
+}
+
+size_t cx_strsplit_a(
+        CxAllocator const *allocator,
+        cxstring string,
+        cxstring delim,
+        size_t limit,
+        cxstring **output
+) {
+    // find out how many splits we're going to make and allocate memory
+    size_t n = 0;
+    cxstring curpos = string;
+    while (1) {
+        ++n;
+        cxstring match = cx_strstr(curpos, delim);
+        if (match.length > 0) {
+            // is the limit reached?
+            if (n < limit) {
+                size_t processed = match.ptr - curpos.ptr + delim.length;
+                curpos.ptr += processed;
+                curpos.length -= processed;
+            } else {
+                // limit reached
+                break;
+            }
+        } else {
+            // no more matches
+            break;
+        }
+    }
+    *output = cxCalloc(allocator, n, sizeof(cxstring));
+    return cx_strsplit(string, delim, n, *output);
+}
+
+size_t cx_strsplit_m(
+        cxmutstr string,
+        cxstring delim,
+        size_t limit,
+        cxmutstr *output
+) {
+    return cx_strsplit(cx_strcast(string),
+                       delim, limit, (cxstring *) output);
+}
+
+size_t cx_strsplit_ma(
+        CxAllocator const *allocator,
+        cxmutstr string,
+        cxstring delim,
+        size_t limit,
+        cxmutstr **output
+) {
+    return cx_strsplit_a(allocator, cx_strcast(string),
+                         delim, limit, (cxstring **) output);
+}
+
+int cx_strcmp(
+        cxstring s1,
+        cxstring s2
+) {
     if (s1.length == s2.length) {
         return memcmp(s1.ptr, s2.ptr, s1.length);
     } else if (s1.length > s2.length) {
@@ -497,7 +434,10 @@
     }
 }
 
-int scstrcasecmp(scstr_t s1, scstr_t s2) {
+int cx_strcasecmp(
+        cxstring s1,
+        cxstring s2
+) {
     if (s1.length == s2.length) {
 #ifdef _WIN32
         return _strnicmp(s1.ptr, s2.ptr, s1.length);
@@ -511,129 +451,328 @@
     }
 }
 
-sstr_t scstrdup(scstr_t s) {
-    return sstrdup_a(ucx_default_allocator(), s);
+int cx_strcmp_p(
+        void const *s1,
+        void const *s2
+) {
+    cxstring const *left = s1;
+    cxstring const *right = s2;
+    return cx_strcmp(*left, *right);
+}
+
+int cx_strcasecmp_p(
+        void const *s1,
+        void const *s2
+) {
+    cxstring const *left = s1;
+    cxstring const *right = s2;
+    return cx_strcasecmp(*left, *right);
 }
 
-sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t s) {
-    sstr_t newstring;
-    newstring.ptr = (char*)almalloc(allocator, s.length + 1);
-    if (newstring.ptr) {
-        newstring.length = s.length;
-        newstring.ptr[newstring.length] = 0;
-        
-        memcpy(newstring.ptr, s.ptr, s.length);
-    } else {
-        newstring.length = 0;
+cxmutstr cx_strdup_a(
+        CxAllocator const *allocator,
+        cxstring string
+) {
+    cxmutstr result = {
+            cxMalloc(allocator, string.length + 1),
+            string.length
+    };
+    if (result.ptr == NULL) {
+        result.length = 0;
+        return result;
     }
-    
-    return newstring;
+    memcpy(result.ptr, string.ptr, string.length);
+    result.ptr[string.length] = '\0';
+    return result;
+}
+
+cxstring cx_strtrim(cxstring string) {
+    cxstring result = string;
+    // TODO: optimize by comparing multiple bytes at once
+    while (result.length > 0 && isspace(*result.ptr)) {
+        result.ptr++;
+        result.length--;
+    }
+    while (result.length > 0 && isspace(result.ptr[result.length - 1])) {
+        result.length--;
+    }
+    return result;
+}
+
+cxmutstr cx_strtrim_m(cxmutstr string) {
+    cxstring result = cx_strtrim(cx_strcast(string));
+    return (cxmutstr) {(char *) result.ptr, result.length};
+}
+
+bool cx_strprefix(
+        cxstring string,
+        cxstring prefix
+) {
+    if (string.length < prefix.length) return false;
+    return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
 }
 
+bool cx_strsuffix(
+        cxstring string,
+        cxstring suffix
+) {
+    if (string.length < suffix.length) return false;
+    return memcmp(string.ptr + string.length - suffix.length,
+                  suffix.ptr, suffix.length) == 0;
+}
 
-static size_t ucx_strtrim(const char *s, size_t len, size_t *newlen) {
-    const char *newptr = s;
-    size_t length = len;
-    
-    while(length > 0 && isspace(*newptr)) {
-        newptr++;
-        length--;
-    }
-    while(length > 0 && isspace(newptr[length-1])) {
-        length--;
-    }
-    
-    *newlen = length;
-    return newptr - s;
+bool cx_strcaseprefix(
+        cxstring string,
+        cxstring prefix
+) {
+    if (string.length < prefix.length) return false;
+#ifdef _WIN32
+    return _strnicmp(string.ptr, prefix.ptr, prefix.length) == 0;
+#else
+    return strncasecmp(string.ptr, prefix.ptr, prefix.length) == 0;
+#endif
 }
 
-sstr_t sstrtrim(sstr_t string) {
-    sstr_t newstr;
-    newstr.ptr = string.ptr
-                 + ucx_strtrim(string.ptr, string.length, &newstr.length);
-    return newstr;
+bool cx_strcasesuffix(
+        cxstring string,
+        cxstring suffix
+) {
+    if (string.length < suffix.length) return false;
+#ifdef _WIN32
+    return _strnicmp(string.ptr+string.length-suffix.length,
+                  suffix.ptr, suffix.length) == 0;
+#else
+    return strncasecmp(string.ptr + string.length - suffix.length,
+                       suffix.ptr, suffix.length) == 0;
+#endif
+}
+
+void cx_strlower(cxmutstr string) {
+    cx_for_n(i, string.length) {
+        string.ptr[i] = (char) tolower(string.ptr[i]);
+    }
 }
 
-scstr_t scstrtrim(scstr_t string) {
-    scstr_t newstr;
-    newstr.ptr = string.ptr
-                 + ucx_strtrim(string.ptr, string.length, &newstr.length);
-    return newstr;
+void cx_strupper(cxmutstr string) {
+    cx_for_n(i, string.length) {
+        string.ptr[i] = (char) toupper(string.ptr[i]);
+    }
 }
 
-int scstrprefix(scstr_t string, scstr_t prefix) {
-    if (string.length == 0) {
-        return prefix.length == 0;
-    }
-    if (prefix.length == 0) {
-        return 1;
-    }
-    
-    if (prefix.length > string.length) {
-        return 0;
-    } else {
-        return memcmp(string.ptr, prefix.ptr, prefix.length) == 0;
+#ifndef CX_STRREPLACE_INDEX_BUFFER_SIZE
+#define CX_STRREPLACE_INDEX_BUFFER_SIZE 64
+#endif
+
+struct cx_strreplace_ibuf {
+    size_t *buf;
+    struct cx_strreplace_ibuf *next;
+    unsigned int len;
+};
+
+static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
+    while (buf) {
+        struct cx_strreplace_ibuf *next = buf->next;
+        free(buf->buf);
+        free(buf);
+        buf = next;
     }
 }
 
-int scstrsuffix(scstr_t string, scstr_t suffix) {
-    if (string.length == 0) {
-        return suffix.length == 0;
+cxmutstr cx_strreplacen_a(
+        CxAllocator const *allocator,
+        cxstring str,
+        cxstring pattern,
+        cxstring replacement,
+        size_t replmax
+) {
+
+    if (pattern.length == 0 || pattern.length > str.length || replmax == 0)
+        return cx_strdup_a(allocator, str);
+
+    // Compute expected buffer length
+    size_t ibufmax = str.length / pattern.length;
+    size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
+    if (ibuflen > CX_STRREPLACE_INDEX_BUFFER_SIZE) {
+        ibuflen = CX_STRREPLACE_INDEX_BUFFER_SIZE;
     }
-    if (suffix.length == 0) {
-        return 1;
+
+    // Allocate first index buffer
+    struct cx_strreplace_ibuf *firstbuf, *curbuf;
+    firstbuf = curbuf = calloc(1, sizeof(struct cx_strreplace_ibuf));
+    if (!firstbuf) return cx_mutstrn(NULL, 0);
+    firstbuf->buf = calloc(ibuflen, sizeof(size_t));
+    if (!firstbuf->buf) {
+        free(firstbuf);
+        return cx_mutstrn(NULL, 0);
     }
-    
-    if (suffix.length > string.length) {
-        return 0;
-    } else {
-        return memcmp(string.ptr+string.length-suffix.length,
-            suffix.ptr, suffix.length) == 0;
+
+    // Search occurrences
+    cxstring searchstr = str;
+    size_t found = 0;
+    do {
+        cxstring match = cx_strstr(searchstr, pattern);
+        if (match.length > 0) {
+            // Allocate next buffer in chain, if required
+            if (curbuf->len == ibuflen) {
+                struct cx_strreplace_ibuf *nextbuf =
+                        calloc(1, sizeof(struct cx_strreplace_ibuf));
+                if (!nextbuf) {
+                    cx_strrepl_free_ibuf(firstbuf);
+                    return cx_mutstrn(NULL, 0);
+                }
+                nextbuf->buf = calloc(ibuflen, sizeof(size_t));
+                if (!nextbuf->buf) {
+                    free(nextbuf);
+                    cx_strrepl_free_ibuf(firstbuf);
+                    return cx_mutstrn(NULL, 0);
+                }
+                curbuf->next = nextbuf;
+                curbuf = nextbuf;
+            }
+
+            // Record match index
+            found++;
+            size_t idx = match.ptr - str.ptr;
+            curbuf->buf[curbuf->len++] = idx;
+            searchstr.ptr = match.ptr + pattern.length;
+            searchstr.length = str.length - idx - pattern.length;
+        } else {
+            break;
+        }
+    } while (searchstr.length > 0 && found < replmax);
+
+    // Allocate result string
+    cxmutstr result;
+    {
+        ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length;
+        size_t rcount = 0;
+        curbuf = firstbuf;
+        do {
+            rcount += curbuf->len;
+            curbuf = curbuf->next;
+        } while (curbuf);
+        result.length = str.length + rcount * adjlen;
+        result.ptr = cxMalloc(allocator, result.length + 1);
+        if (!result.ptr) {
+            cx_strrepl_free_ibuf(firstbuf);
+            return cx_mutstrn(NULL, 0);
+        }
     }
-}
+
+    // Build result string
+    curbuf = firstbuf;
+    size_t srcidx = 0;
+    char *destptr = result.ptr;
+    do {
+        for (size_t i = 0; i < curbuf->len; i++) {
+            // Copy source part up to next match
+            size_t idx = curbuf->buf[i];
+            size_t srclen = idx - srcidx;
+            if (srclen > 0) {
+                memcpy(destptr, str.ptr + srcidx, srclen);
+                destptr += srclen;
+                srcidx += srclen;
+            }
 
-sstr_t scstrlower(scstr_t string) {
-    sstr_t ret = sstrdup(string);
-    for (size_t i = 0; i < ret.length ; i++) {
-        ret.ptr[i] = tolower(ret.ptr[i]);
-    }
-    return ret;
+            // Copy the replacement and skip the source pattern
+            srcidx += pattern.length;
+            memcpy(destptr, replacement.ptr, replacement.length);
+            destptr += replacement.length;
+        }
+        curbuf = curbuf->next;
+    } while (curbuf);
+    memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
+
+    // Result is guaranteed to be zero-terminated
+    result.ptr[result.length] = '\0';
+
+    // Free index buffer
+    cx_strrepl_free_ibuf(firstbuf);
+
+    return result;
 }
 
-sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string) {
-    sstr_t ret = sstrdup_a(allocator, string);
-    for (size_t i = 0; i < ret.length ; i++) {
-        ret.ptr[i] = tolower(ret.ptr[i]);
-    }
-    return ret;
+CxStrtokCtx cx_strtok(
+        cxstring str,
+        cxstring delim,
+        size_t limit
+) {
+    CxStrtokCtx ctx;
+    ctx.str = str;
+    ctx.delim = delim;
+    ctx.limit = limit;
+    ctx.pos = 0;
+    ctx.next_pos = 0;
+    ctx.delim_pos = 0;
+    ctx.found = 0;
+    ctx.delim_more = NULL;
+    ctx.delim_more_count = 0;
+    return ctx;
 }
 
-sstr_t scstrupper(scstr_t string) {
-    sstr_t ret = sstrdup(string);
-    for (size_t i = 0; i < ret.length ; i++) {
-        ret.ptr[i] = toupper(ret.ptr[i]);
-    }
-    return ret;
+CxStrtokCtx cx_strtok_m(
+        cxmutstr str,
+        cxstring delim,
+        size_t limit
+) {
+    return cx_strtok(cx_strcast(str), delim, limit);
 }
 
-sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string) {
-    sstr_t ret = sstrdup_a(allocator, string);
-    for (size_t i = 0; i < ret.length ; i++) {
-        ret.ptr[i] = toupper(ret.ptr[i]);
+bool cx_strtok_next(
+        CxStrtokCtx *ctx,
+        cxstring *token
+) {
+    // abortion criteria
+    if (ctx->found >= ctx->limit || ctx->delim_pos >= ctx->str.length) {
+        return false;
+    }
+
+    // determine the search start
+    cxstring haystack = cx_strsubs(ctx->str, ctx->next_pos);
+
+    // search the next delimiter
+    cxstring delim = cx_strstr(haystack, ctx->delim);
+
+    // if found, make delim capture exactly the delimiter
+    if (delim.length > 0) {
+        delim.length = ctx->delim.length;
     }
-    return ret;
+
+    // if more delimiters are specified, check them now
+    if (ctx->delim_more_count > 0) {
+        cx_for_n(i, ctx->delim_more_count) {
+            cxstring d = cx_strstr(haystack, ctx->delim_more[i]);
+            if (d.length > 0 && (delim.length == 0 || d.ptr < delim.ptr)) {
+                delim.ptr = d.ptr;
+                delim.length = ctx->delim_more[i].length;
+            }
+        }
+    }
+
+    // store the token information and adjust the context
+    ctx->found++;
+    ctx->pos = ctx->next_pos;
+    token->ptr = &ctx->str.ptr[ctx->pos];
+    ctx->delim_pos = delim.length == 0 ?
+                     ctx->str.length : (size_t) (delim.ptr - ctx->str.ptr);
+    token->length = ctx->delim_pos - ctx->pos;
+    ctx->next_pos = ctx->delim_pos + delim.length;
+
+    return true;
 }
 
-// type adjustment functions
-scstr_t ucx_sc2sc(scstr_t str) {
-    return str;
+bool cx_strtok_next_m(
+        CxStrtokCtx *ctx,
+        cxmutstr *token
+) {
+    return cx_strtok_next(ctx, (cxstring *) token);
 }
-scstr_t ucx_ss2sc(sstr_t str) {
-    scstr_t cs;
-    cs.ptr = str.ptr;
-    cs.length = str.length;
-    return cs;
+
+void cx_strtok_delim(
+        CxStrtokCtx *ctx,
+        cxstring const *delim,
+        size_t count
+) {
+    ctx->delim_more = delim;
+    ctx->delim_more_count = count;
 }
-scstr_t ucx_ss2c_s(scstr_t c) {
-    return c;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/szmul.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,46 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2023 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.
+ */
+
+int cx_szmul_impl(
+        size_t a,
+        size_t b,
+        size_t *result
+) {
+    if (a == 0 || b == 0) {
+        *result = 0;
+        return 0;
+    }
+    size_t r = a * b;
+    if (r / b == a) {
+        *result = r;
+        return 0;
+    } else {
+        *result = 0;
+        return 1;
+    }
+}
\ No newline at end of file
--- a/ucx/test.c	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-
-#include "ucx/test.h"
-
-UcxTestSuite* ucx_test_suite_new() {
-    UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite));
-    if (suite != NULL) {
-        suite->success = 0;
-        suite->failure = 0;
-        suite->tests = NULL;
-    }
-
-    return suite;
-}
-
-void ucx_test_suite_free(UcxTestSuite* suite) {
-    UcxTestList *l = suite->tests;
-    while (l != NULL) {
-        UcxTestList *e = l;
-        l = l->next;
-        free(e);
-    }
-    free(suite);
-}
-
-int ucx_test_register(UcxTestSuite* suite, UcxTest test) {
-    if (suite->tests) {
-        UcxTestList *newelem = (UcxTestList*) malloc(sizeof(UcxTestList));
-        if (newelem) {
-            newelem->test = test;
-            newelem->next = NULL;
-            
-            UcxTestList *last = suite->tests;
-            while (last->next) {
-                last = last->next;
-            }
-            last->next = newelem;
-            
-            return EXIT_SUCCESS;
-        } else {
-            return EXIT_FAILURE;
-        }
-    } else {
-        suite->tests = (UcxTestList*) malloc(sizeof(UcxTestList));
-        if (suite->tests) {
-            suite->tests->test = test;
-            suite->tests->next = NULL;
-            
-            return EXIT_SUCCESS;
-        } else {
-            return EXIT_FAILURE;
-        }
-    }
-}
-
-void ucx_test_run(UcxTestSuite* suite, FILE* output) {
-    suite->success = 0;
-    suite->failure = 0;
-    for (UcxTestList* elem = suite->tests ; elem ; elem = elem->next) {
-        elem->test(suite, output);
-    }
-    fwrite("\nAll test completed.\n", 1, 21, output);
-    fprintf(output, "  Total:   %u\n  Success: %u\n  Failure: %u\n",
-            suite->success+suite->failure, suite->success, suite->failure);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/tree.c	Fri Apr 21 21:25:32 2023 +0200
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "cx/tree.h"
+#include "cx/linked_list.h"
+
+#define CX_TR_PTR(cur, off) *((void**)(((char*)(cur))+(off)))
+
+void cx_tree_add_sibling(void *node, ptrdiff_t loc_prev, ptrdiff_t loc_next, ptrdiff_t loc_parent, void *new_node) {
+    cx_linked_list_add(&node, NULL, loc_prev, loc_next, new_node);
+
+    // optional parent link
+    if (loc_parent >= 0) {
+        CX_TR_PTR(new_node, loc_parent) = CX_TR_PTR(node, loc_parent);
+    }
+}
+
+void cx_tree_add_child(void **children_begin, void **children_end,
+                       ptrdiff_t loc_prev, ptrdiff_t loc_next, void *new_node,
+                       ptrdiff_t loc_parent, void *parent) {
+    cx_linked_list_add(children_begin, children_end, loc_prev, loc_next, new_node);
+
+    // optional parent link
+    if (loc_parent >= 0) {
+        CX_TR_PTR(new_node, loc_parent) = parent;
+    }
+}
--- a/ucx/ucx.c	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/**
- * @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>
- * -&nbsp;or&nbsp;-
- * <a href="https://develop.uap-core.de/hg/ucx">
- * https://develop.uap-core.de/hg/ucx</a>
- * </p>
- * 
- * <h2>LICENCE</h2>
- * 
- * Copyright 2017 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.
- */
-
-#include "ucx/ucx.h"
-
-int ucx_szmul_impl(size_t a, size_t b, size_t *result) {
-    if(a == 0 || b == 0) {
-        *result = 0;
-        return 0;
-    }
-    size_t r = a * b;
-    if(r / b == a) {
-        *result = r;
-        return 0;
-    } else {
-        *result = 0;
-        return 1;
-    }
-}
-
--- a/ucx/ucx/allocator.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-/**
- * Allocator for custom memory management.
- * 
- * A UCX allocator consists of a pointer to the memory area / pool and four
- * function pointers to memory management functions operating on this memory
- * area / pool. These functions shall behave equivalent to the standard libc
- * functions <code>malloc(), calloc(), realloc()</code> and <code>free()</code>.
- * 
- * The signature of the memory management functions is based on the signature
- * of the respective libc function but each of them takes the pointer to the
- * memory area / pool as first argument.
- * 
- * As the pointer to the memory area / pool can be arbitrarily chosen, any data
- * can be provided to the memory management functions. A UcxMempool is just
- * one example.
- * 
- * @see mempool.h
- * @see UcxMap
- * 
- * @file   allocator.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_ALLOCATOR_H
-#define	UCX_ALLOCATOR_H
-
-#include "ucx.h"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * A function pointer to the allocators <code>malloc()</code> function.
- * @see UcxAllocator
- */
-typedef void*(*ucx_allocator_malloc)(void *pool, size_t n);
-
-/**
- * A function pointer to the allocators <code>calloc()</code> function.
- * @see UcxAllocator
- */
-typedef void*(*ucx_allocator_calloc)(void *pool, size_t n, size_t size);
-
-/**
- * A function pointer to the allocators <code>realloc()</code> function.
- * @see UcxAllocator
- */
-typedef void*(*ucx_allocator_realloc)(void *pool, void *data, size_t n);
-
-/**
- * A function pointer to the allocators <code>free()</code> function.
- * @see UcxAllocator
- */
-typedef void(*ucx_allocator_free)(void *pool, void *data);
-
-/**
- * UCX allocator data structure containing memory management functions.
- */
-typedef struct {
-    /** Pointer to an area of memory or a complex memory pool.
-     * This pointer will be passed to any memory management function as first
-     * argument.
-     */
-    void *pool;
-    /**
-     * The <code>malloc()</code> function for this allocator.
-     */
-    ucx_allocator_malloc  malloc;
-    /**
-     * The <code>calloc()</code> function for this allocator.
-     */
-    ucx_allocator_calloc  calloc;
-    /**
-     * The <code>realloc()</code> function for this allocator.
-     */
-    ucx_allocator_realloc realloc;
-    /**
-     * The <code>free()</code> function for this allocator.
-     */
-    ucx_allocator_free    free;
-} UcxAllocator;
-
-/**
- * Returns a pointer to the default allocator.
- * 
- * The default allocator contains wrappers to the standard libc memory
- * management functions. Use this function to get a pointer to a globally
- * available allocator. You may also define an own UcxAllocator by assigning
- * #UCX_ALLOCATOR_DEFAULT to a variable and pass the address of this variable
- * to any function that takes a UcxAllocator as argument. Note that using
- * this function is the recommended way of passing a default allocator, thus
- * it never runs out of scope.
- * 
- * @return a pointer to the default allocator
- * 
- * @see UCX_ALLOCATOR_DEFAULT
- */
-UcxAllocator *ucx_default_allocator();
-
-/**
- * A wrapper for the standard libc <code>malloc()</code> function.
- * @param ignore ignored (may be used by allocators for pooled memory)
- * @param n argument passed to <code>malloc()</code>
- * @return return value of <code>malloc()</code>
- */
-void *ucx_default_malloc(void *ignore, size_t n);
-/**
- * A wrapper for the standard libc <code>calloc()</code> function.
- * @param ignore ignored (may be used by allocators for pooled memory)
- * @param n argument passed to <code>calloc()</code>
- * @param size  argument passed to <code>calloc()</code>
- * @return return value of <code>calloc()</code>
- */
-void *ucx_default_calloc(void *ignore, size_t n, size_t size);
-/**
- * A wrapper for the standard libc <code>realloc()</code> function.
- * @param ignore ignored (may be used by allocators for pooled memory)
- * @param data argumend passed to <code>realloc()</code>
- * @param n argument passed to <code>realloc()</code>
- * @return return value of <code>realloc()</code>
- */
-void *ucx_default_realloc(void *ignore, void *data, size_t n);
-/**
- * A wrapper for the standard libc <code>free()</code> function.
- * @param ignore ignored (may be used by allocators for pooled memory)
- * @param data argument passed to <code>free()</code>
- */
-void ucx_default_free(void *ignore, void *data);
-
-/**
- * Shorthand for calling an allocators malloc function.
- * @param allocator the allocator to use
- * @param n size of space to allocate
- * @return a pointer to the allocated memory area
- */
-#define almalloc(allocator, n) ((allocator)->malloc((allocator)->pool, n))
-
-/**
- * Shorthand for calling an allocators calloc function.
- * @param allocator the allocator to use
- * @param n the count of elements the space should be allocated for
- * @param size the size of each element
- * @return a pointer to the allocated memory area
- */
-#define alcalloc(allocator, n, size) \
-        ((allocator)->calloc((allocator)->pool, n, size))
-
-/**
- * Shorthand for calling an allocators realloc function.
- * @param allocator the allocator to use
- * @param ptr the pointer to the memory area that shall be reallocated
- * @param n the new size of the allocated memory area
- * @return a pointer to the reallocated memory area
- */
-#define alrealloc(allocator, ptr, n) \
-        ((allocator)->realloc((allocator)->pool, ptr, n))
-
-/**
- * Shorthand for calling an allocators free function.
- * @param allocator the allocator to use
- * @param ptr the pointer to the memory area that shall be freed
- */
-#define alfree(allocator, ptr) ((allocator)->free((allocator)->pool, ptr))
-
-/**
- * Convenient macro for a default allocator <code>struct</code> definition.
- */
-#define UCX_ALLOCATOR_DEFAULT {NULL, \
-        ucx_default_malloc, ucx_default_calloc, ucx_default_realloc, \
-        ucx_default_free }
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_ALLOCATOR_H */
-
--- a/ucx/ucx/avl.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,353 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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 avl.h
- * 
- * AVL tree implementation.
- * 
- * This binary search tree implementation allows average O(1) insertion and
- * removal of elements (excluding binary search time).
- * 
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_AVL_H
-#define UCX_AVL_H
-
-#include "ucx.h"
-#include "allocator.h"
-#include <inttypes.h>
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * UCX AVL Node type.
- * 
- * @see UcxAVLNode
- */
-typedef struct UcxAVLNode UcxAVLNode;
-
-/**
- * UCX AVL Node.
- */
-struct UcxAVLNode {
-    /**
-     * The key for this node.
-     */
-    intptr_t key;
-    /**
-     * Data contained by this node.
-     */
-    void *value;
-    /**
-     * The height of this (sub)-tree.
-     */
-    size_t height;
-    /**
-     * Parent node.
-     */
-    UcxAVLNode *parent;
-    /**
-     * Root node of left subtree.
-     */
-    UcxAVLNode *left;
-    /**
-     * Root node of right subtree.
-     */
-    UcxAVLNode *right;
-};
-
-/**
- * UCX AVL Tree.
- */
-typedef struct {
-    /**
-     * The UcxAllocator that shall be used to manage the memory for node data.
-     */
-    UcxAllocator *allocator;
-    /**
-     * Root node of the tree.
-     */
-    UcxAVLNode *root;
-    /**
-     * Compare function that shall be used to compare the UcxAVLNode keys.
-     * @see UcxAVLNode.key
-     */
-    cmp_func cmpfunc;
-    /**
-     * Custom user data.
-     * This data will also be provided to the cmpfunc.
-     */
-    void *userdata;
-} UcxAVLTree;
-
-/**
- * Initializes a new UcxAVLTree with a default allocator.
- * 
- * @param cmpfunc the compare function that shall be used
- * @return a new UcxAVLTree object
- * @see ucx_avl_new_a()
- */
-UcxAVLTree *ucx_avl_new(cmp_func cmpfunc);
-
-/**
- * Initializes a new UcxAVLTree with the specified allocator.
- * 
- * The cmpfunc should be capable of comparing two keys within this AVL tree.
- * So if you want to use null terminated strings as keys, you could use the
- * ucx_cmp_str() function here.
- * 
- * @param cmpfunc the compare function that shall be used
- * @param allocator the UcxAllocator that shall be used
- * @return a new UcxAVLTree object
- */
-UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator);
-
-/**
- * Destroys a UcxAVLTree.
- * 
- * Note, that the contents are not automatically freed.
- * Use may use #ucx_avl_free_content() before calling this function.
- * 
- * @param tree the tree to destroy
- * @see ucx_avl_free_content()
- */
-void ucx_avl_free(UcxAVLTree *tree);
-
-/**
- * Frees the contents of a UcxAVLTree.
- * 
- * This is a convenience function that iterates over the tree and passes all
- * values to the specified destructor function.
- * 
- * If no destructor is specified (<code>NULL</code>), the free() function of
- * the tree's own allocator is used.
- * 
- * You must ensure, that it is valid to pass each value in the map to the same
- * destructor function.
- * 
- * You should free the entire tree afterwards, as the contents will be invalid.
- * 
- * @param tree for which the contents shall be freed
- * @param destr optional pointer to a destructor function
- * @see ucx_avl_free()
- */
-void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr);
-
-/**
- * Macro for initializing a new UcxAVLTree with the default allocator and a
- * ucx_cmp_ptr() compare function.
- * 
- * @return a new default UcxAVLTree object
- */
-#define ucx_avl_default_new() \
-    ucx_avl_new_a(ucx_cmp_ptr, ucx_default_allocator())
-
-/**
- * Gets the node from the tree, that is associated with the specified key.
- * @param tree the UcxAVLTree
- * @param key the key
- * @return the node (or <code>NULL</code>, if the key is not present)
- */
-UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key);
-
-/**
- * Gets the value from the tree, that is associated with the specified key.
- * @param tree the UcxAVLTree
- * @param key the key
- * @return the value (or <code>NULL</code>, if the key is not present)
- */
-void *ucx_avl_get(UcxAVLTree *tree, intptr_t key);
-
-/**
- * A mode for #ucx_avl_find_node() with the same behavior as
- * #ucx_avl_get_node().
- */
-#define UCX_AVL_FIND_EXACT         0
-/**
- * A mode for #ucx_avl_find_node() finding the node whose key is at least
- * as large as the specified key.
- */
-#define UCX_AVL_FIND_LOWER_BOUNDED 1
-/**
- * A mode for #ucx_avl_find_node() finding the node whose key is at most
- * as large as the specified key.
- */
-#define UCX_AVL_FIND_UPPER_BOUNDED 2
-/**
- * A mode for #ucx_avl_find_node() finding the node with a key that is as close
- * to the specified key as possible. If the key is present, the behavior is
- * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on
- * empty trees.
- */
-#define UCX_AVL_FIND_CLOSEST       3
-
-/**
- * Finds a node within the tree. The following modes are supported:
- * <ul>
- * <li>#UCX_AVL_FIND_EXACT: the same behavior as #ucx_avl_get_node()</li>
- * <li>#UCX_AVL_FIND_LOWER_BOUNDED: finds the node whose key is at least
- * as large as the specified key</li>
- * <li>#UCX_AVL_FIND_UPPER_BOUNDED: finds the node whose key is at most
- * as large as the specified key</li>
- * <li>#UCX_AVL_FIND_CLOSEST: finds the node with a key that is as close to
- * the specified key as possible. If the key is present, the behavior is
- * like #ucx_avl_get_node(). This mode only returns <code>NULL</code> on
- * empty trees.</li> 
- * </ul>
- * 
- * The distance function provided MUST agree with the compare function of
- * the AVL tree.
- * 
- * @param tree the UcxAVLTree
- * @param key the key
- * @param dfnc the distance function
- * @param mode the find mode
- * @return the node (or <code>NULL</code>, if no node can be found)
- */
-UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key,
-        distance_func dfnc, int mode);
-
-/**
- * Finds a value within the tree.
- * See #ucx_avl_find_node() for details.
- * 
- * @param tree the UcxAVLTree
- * @param key the key
- * @param dfnc the distance function
- * @param mode the find mode
- * @return the value (or <code>NULL</code>, if no value can be found)
- */
-void *ucx_avl_find(UcxAVLTree *tree, intptr_t key,
-        distance_func dfnc, int mode);
-
-/**
- * Puts a key/value pair into the tree.
- * 
- * Attention: use this function only, if a possible old value does not need
- * to be preserved.
- * 
- * @param tree the UcxAVLTree
- * @param key the key
- * @param value the new value
- * @return zero, if and only if the operation succeeded
- */
-int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value);
-
-/**
- * Puts a key/value pair into the tree.
- * 
- * This is a secure function which saves the old value to the variable pointed
- * at by oldvalue.
- * 
- * @param tree the UcxAVLTree
- * @param key the key
- * @param value the new value
- * @param oldvalue optional: a pointer to the location where a possible old
- * value shall be stored
- * @return zero, if and only if the operation succeeded
- */
-int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, void **oldvalue);
-
-/**
- * Removes a node from the AVL tree.
- * 
- * Note: the specified node is logically removed. The tree implementation
- * decides which memory area is freed. In most cases the here provided node
- * is freed, so its further use is generally undefined.
- * 
- * @param tree the UcxAVLTree
- * @param node the node to remove
- * @return zero, if and only if an element has been removed
- */
-int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node);
-
-/**
- * Removes an element from the AVL tree.
- * 
- * @param tree the UcxAVLTree
- * @param key the key
- * @return zero, if and only if an element has been removed
- */
-int ucx_avl_remove(UcxAVLTree *tree, intptr_t key);
-
-/**
- * Removes an element from the AVL tree.
- * 
- * This is a secure function which saves the old key and value data from node
- * to the variables at the location of oldkey and oldvalue (if specified), so
- * they can be freed afterwards (if necessary).
- * 
- * Note: the returned key in oldkey is possibly not the same as the provided
- * key for the lookup (in terms of memory location).
- * 
- * @param tree the UcxAVLTree
- * @param key the key of the element to remove
- * @param oldkey optional: a pointer to the location where the old key shall be
- * stored
- * @param oldvalue optional: a pointer to the location where the old value
- * shall be stored
- * @return zero, if and only if an element has been removed
- */
-int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key,
-        intptr_t *oldkey, void **oldvalue);
-
-/**
- * Counts the nodes in the specified UcxAVLTree.
- * @param tree the AVL tree
- * @return the node count
- */
-size_t ucx_avl_count(UcxAVLTree *tree);
-
-/**
- * Finds the in-order predecessor of the given node.
- * @param node an AVL node
- * @return the in-order predecessor of the given node, or <code>NULL</code> if
- * the given node is the in-order minimum
- */
-UcxAVLNode* ucx_avl_pred(UcxAVLNode* node);
-
-/**
- * Finds the in-order successor of the given node.
- * @param node an AVL node
- * @return the in-order successor of the given node, or <code>NULL</code> if
- * the given node is the in-order maximum
- */
-UcxAVLNode* ucx_avl_succ(UcxAVLNode* node);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_AVL_H */
-
--- a/ucx/ucx/buffer.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,339 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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 buffer.h
- * 
- * Advanced buffer implementation.
- * 
- * Instances of UcxBuffer can be used to read from or to write to like one
- * would do with a stream. This allows the use of ucx_stream_copy() to copy
- * contents from one buffer to another.
- * 
- * Some features for convenient use of the buffer
- * can be enabled. See the documentation of the macro constants for more
- * information.
- * 
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_BUFFER_H
-#define	UCX_BUFFER_H
-
-#include "ucx.h"
-#include <sys/types.h>
-#include <stdio.h>
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * No buffer features enabled (all flags cleared).
- */
-#define UCX_BUFFER_DEFAULT      0x00
-
-/**
- * If this flag is enabled, the buffer will automatically free its contents.
- */
-#define UCX_BUFFER_AUTOFREE     0x01
-
-/**
- * If this flag is enabled, the buffer will automatically extends its capacity.
- */
-#define UCX_BUFFER_AUTOEXTEND   0x02
-
-/** UCX Buffer. */
-typedef struct {
-    /** A pointer to the buffer contents. */
-    char *space;
-    /** Current position of the buffer. */
-    size_t pos;
-    /** Current capacity (i.e. maximum size) of the buffer. */
-    size_t capacity;
-    /** Current size of the buffer content. */
-    size_t size;
-    /**
-     * Flag register for buffer features.
-     * @see #UCX_BUFFER_DEFAULT
-     * @see #UCX_BUFFER_AUTOFREE
-     * @see #UCX_BUFFER_AUTOEXTEND
-     */
-    int flags;
-} UcxBuffer;
-
-/**
- * Creates a new buffer.
- * 
- * <b>Note:</b> you may provide <code>NULL</code> as argument for
- * <code>space</code>. Then this function will allocate the space and enforce
- * the #UCX_BUFFER_AUTOFREE flag.
- * 
- * @param space pointer to the memory area, or <code>NULL</code> to allocate
- * new memory
- * @param capacity the capacity of the buffer
- * @param flags buffer features (see UcxBuffer.flags)
- * @return the new buffer
- */
-UcxBuffer *ucx_buffer_new(void *space, size_t capacity, int flags);
-
-/**
- * Destroys a buffer.
- * 
- * If the #UCX_BUFFER_AUTOFREE feature is enabled, the contents of the buffer
- * are also freed.
- * 
- * @param buffer the buffer to destroy
- */
-void ucx_buffer_free(UcxBuffer* buffer);
-
-/**
- * Creates a new buffer and fills it with extracted content from another buffer.
- * 
- * <b>Note:</b> the #UCX_BUFFER_AUTOFREE feature is enforced for the new buffer.
- * 
- * @param src the source buffer
- * @param start the start position of extraction
- * @param length the count of bytes to extract (must not be zero)
- * @param flags feature mask for the new buffer
- * @return a new buffer containing the extraction
- */
-UcxBuffer* ucx_buffer_extract(UcxBuffer *src,
-        size_t start, size_t length, int flags);
-
-/**
- * A shorthand macro for the full extraction of the buffer.
- * 
- * @param src the source buffer
- * @param flags feature mask for the new buffer
- * @return a new buffer with the extracted content
- */
-#define ucx_buffer_clone(src,flags) \
-    ucx_buffer_extract(src, 0, (src)->capacity, flags)
-
-
-/**
- * Shifts the contents of the buffer by the given offset.
- * 
- * If the offset is positive, the contents are shifted to the right.
- * If auto extension is enabled, the buffer grows, if necessary.
- * In case the auto extension fails, this function returns a non-zero value and
- * no contents are changed.
- * If auto extension is disabled, the contents that do not fit into the buffer
- * are discarded.
- * 
- * If the offset is negative, the contents are shifted to the left where the
- * first <code>shift</code> bytes are discarded.
- * The new size of the buffer is the old size minus
- * the absolute shift value.
- * If this value is larger than the buffer size, the buffer is emptied (but
- * not cleared, see the security note below).
- * 
- * The buffer position gets shifted alongside with the content but is kept
- * within the boundaries of the buffer.
- * 
- * <b>Security note:</b> the shifting operation does <em>not</em> erase the
- * previously occupied memory cells. You can easily do that manually, e.g. by
- * calling <code>memset(buffer->space, 0, shift)</code> for a right shift or
- * <code>memset(buffer->size, 0, buffer->capacity-buffer->size)</code>
- * for a left shift.
- * 
- * @param buffer the buffer
- * @param shift the shift offset (negative means left shift)
- * @return 0 on success, non-zero if a required auto-extension fails
- */
-int ucx_buffer_shift(UcxBuffer* buffer, off_t shift);
-
-/**
- * Shifts the buffer to the right.
- * See ucx_buffer_shift() for details.
- * 
- * @param buffer the buffer
- * @param shift the shift offset
- * @return 0 on success, non-zero if a required auto-extension fails
- * @see ucx_buffer_shift()
- */
-int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift);
-
-/**
- * Shifts the buffer to the left.
- * 
- * See ucx_buffer_shift() for details. Note, however, that this method expects
- * a positive shift offset.
- * 
- * Since a left shift cannot fail due to memory allocation problems, this
- * function always returns zero.
- * 
- * @param buffer the buffer
- * @param shift the shift offset
- * @return always zero
- * @see ucx_buffer_shift()
- */
-int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift);
-
-
-/**
- * Moves the position of the buffer.
- * 
- * The new position is relative to the <code>whence</code> argument.
- *
- * SEEK_SET marks the start of the buffer.
- * SEEK_CUR marks the current position.
- * SEEK_END marks the end of the buffer.
- * 
- * With an offset of zero, this function sets the buffer position to zero
- * (SEEK_SET), the buffer size (SEEK_END) or leaves the buffer position
- * unchanged (SEEK_CUR).
- * 
- * @param buffer
- * @param offset position offset relative to <code>whence</code>
- * @param whence one of SEEK_SET, SEEK_CUR or SEEK_END
- * @return 0 on success, non-zero if the position is invalid
- *
- */
-int ucx_buffer_seek(UcxBuffer *buffer, off_t offset, int whence);
-
-/**
- * Clears the buffer by resetting the position and deleting the data.
- * 
- * The data is deleted by a zeroing it with call to <code>memset()</code>.
- * 
- * @param buffer the buffer to be cleared
- */
-#define ucx_buffer_clear(buffer) memset((buffer)->space, 0, (buffer)->size); \
-        (buffer)->size = 0; (buffer)->pos = 0;
-
-/**
- * Tests, if the buffer position has exceeded the buffer capacity.
- * 
- * @param buffer the buffer to test
- * @return non-zero, if the current buffer position has exceeded the last
- * available byte of the buffer.
- */
-int ucx_buffer_eof(UcxBuffer *buffer);
-
-
-/**
- * Extends the capacity of the buffer.
- * 
- * <b>Note:</b> The buffer capacity increased by a power of two. I.e.
- * the buffer capacity is doubled, as long as it would not hold the current
- * content plus the additional required bytes.
- * 
- * <b>Attention:</b> the argument provided is the number of <i>additional</i>
- * bytes the buffer shall hold. It is <b>NOT</b> the total number of bytes the
- * buffer shall hold.
- * 
- * @param buffer the buffer to extend
- * @param additional_bytes the number of additional bytes the buffer shall
- * <i>at least</i> hold
- * @return 0 on success or a non-zero value on failure
- */
-int ucx_buffer_extend(UcxBuffer *buffer, size_t additional_bytes);
-
-/**
- * Writes data to a UcxBuffer.
- * 
- * The position of the buffer is increased by the number of bytes written.
- * 
- * @param ptr a pointer to the memory area containing the bytes to be written
- * @param size the length of one element
- * @param nitems the element count
- * @param buffer the UcxBuffer to write to
- * @return the total count of bytes written
- */
-size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
-        UcxBuffer *buffer);
-
-/**
- * Reads data from a UcxBuffer.
- * 
- * The position of the buffer is increased by the number of bytes read.
- * 
- * @param ptr a pointer to the memory area where to store the read data
- * @param size the length of one element
- * @param nitems the element count
- * @param buffer the UcxBuffer to read from
- * @return the total number of elements read
- */
-size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
-        UcxBuffer *buffer);
-
-/**
- * Writes a character to a buffer.
- * 
- * The least significant byte of the argument is written to the buffer. If the
- * end of the buffer is reached and #UCX_BUFFER_AUTOEXTEND feature is enabled,
- * the buffer capacity is extended by ucx_buffer_extend(). If the feature is
- * disabled or buffer extension fails, <code>EOF</code> is returned.
- * 
- * On successful write the position of the buffer is increased.
- * 
- * @param buffer the buffer to write to
- * @param c the character to write as <code>int</code> value
- * @return the byte that has bean written as <code>int</code> value or
- * <code>EOF</code> when the end of the stream is reached and automatic
- * extension is not enabled or not possible
- */
-int ucx_buffer_putc(UcxBuffer *buffer, int c);
-
-/**
- * Gets a character from a buffer.
- * 
- * The current position of the buffer is increased after a successful read.
- * 
- * @param buffer the buffer to read from
- * @return the character as <code>int</code> value or <code>EOF</code>, if the
- * end of the buffer is reached
- */
-int ucx_buffer_getc(UcxBuffer *buffer);
-
-/**
- * Writes a string to a buffer.
- * 
- * @param buffer the buffer
- * @param str the string
- * @return the number of bytes written
- */
-size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str);
-
-/**
- * Returns the complete buffer content as sstr_t.
- * @param buffer the buffer
- * @return the result of <code>sstrn()</code> with the buffer space and size
- * as arguments
- */
-#define ucx_buffer_to_sstr(buffer) sstrn((buffer)->space, (buffer)->size)
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_BUFFER_H */
-
--- a/ucx/ucx/list.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,396 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-/**
- * Doubly linked list implementation.
- * 
- * @file   list.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_LIST_H
-#define	UCX_LIST_H
-
-#include "ucx.h"
-#include "allocator.h"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * Loop statement for UCX lists.
- * 
- * The first argument is the name of the iteration variable. The scope of
- * this variable is limited to the <code>UCX_FOREACH</code> statement.
- * 
- * The second argument is a pointer to the list. In most cases this will be the
- * pointer to the first element of the list, but it may also be an arbitrary
- * element of the list. The iteration will then start with that element.
- * 
- * @param list The first element of the list
- * @param elem The variable name of the element
- */
-#define UCX_FOREACH(elem,list) \
-        for (UcxList* elem = list ; elem != NULL ; elem = elem->next)
-
-/**
- * UCX list type.
- * @see UcxList
- */
-typedef struct UcxList UcxList;
-
-/**
- * UCX list structure.
- */
-struct UcxList {
-    /**
-     * List element payload.
-     */
-    void    *data;
-    /**
-     * Pointer to the next list element or <code>NULL</code>, if this is the
-     * last element.
-     */
-    UcxList *next;
-    /**
-     * Pointer to the previous list element or <code>NULL</code>, if this is
-     * the first element.
-     */
-    UcxList *prev;
-};
-
-/**
- * Creates an element-wise copy of a list.
- * 
- * This function clones the specified list by creating new list elements and
- * copying the data with the specified copy_func(). If no copy_func() is
- * specified, a shallow copy is created and the new list will reference the
- * same data as the source list.
- * 
- * @param list the list to copy
- * @param cpyfnc a pointer to the function that shall copy an element (may be
- * <code>NULL</code>)
- * @param data additional data for the copy_func()
- * @return a pointer to the copy
- */
-UcxList *ucx_list_clone(UcxList *list, copy_func cpyfnc, void* data);
-
-/**
- * Creates an element-wise copy of a list using a UcxAllocator.
- * 
- * See ucx_list_clone() for details.
- * 
- * You might want to pass the allocator via the <code>data</code> parameter,
- * to access it within the copy function for making deep copies.
- * 
- * @param allocator the allocator to use
- * @param list the list to copy
- * @param cpyfnc a pointer to the function that shall copy an element (may be
- * <code>NULL</code>)
- * @param data additional data for the copy_func()
- * @return a pointer to the copy
- * @see ucx_list_clone()
- */
-UcxList *ucx_list_clone_a(UcxAllocator *allocator, UcxList *list,
-        copy_func cpyfnc, void* data);
-
-/**
- * Compares two UCX lists element-wise by using a compare function.
- * 
- * Each element of the two specified lists are compared by using the specified
- * compare function and the additional data. The type and content of this
- * additional data depends on the cmp_func() used.
- * 
- * If the list pointers denote elements within a list, the lists are compared
- * starting with the denoted elements. Thus any previous elements are not taken
- * into account. This might be useful to check, if certain list tails match
- * each other.
- * 
- * @param list1 the first list
- * @param list2 the second list
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return 1, if and only if the two lists equal element-wise, 0 otherwise
- */
-int ucx_list_equals(const UcxList *list1, const UcxList *list2,
-        cmp_func cmpfnc, void* data);
-
-/**
- * Destroys the entire list.
- * 
- * The members of the list are not automatically freed, so ensure they are
- * otherwise referenced or destroyed by ucx_list_free_contents().
- * Otherwise, a memory leak is likely to occur.
- * 
- * <b>Caution:</b> the argument <b>MUST</b> denote an entire list (i.e. a call
- * to ucx_list_first() on the argument must return the argument itself)
- * 
- * @param list the list to free
- * @see ucx_list_free_contents()
- */
-void ucx_list_free(UcxList *list);
-
-/**
- * Destroys the entire list using a UcxAllocator.
- * 
- * See ucx_list_free() for details.
- * 
- * @param allocator the allocator to use
- * @param list the list to free
- * @see ucx_list_free()
- */
-void ucx_list_free_a(UcxAllocator *allocator, UcxList *list);
-
-/**
- * Destroys the contents of the specified list by calling the specified
- * destructor on each of them.
- * 
- * Note, that the contents are not usable afterwards and the list should be
- * destroyed with ucx_list_free().
- *
- * If no destructor is specified (<code>NULL</code>), stdlib's free() is used.
- * 
- * @param list the list for which the contents shall be freed
- * @param destr optional destructor function
- * @see ucx_list_free()
- */
-void ucx_list_free_content(UcxList* list, ucx_destructor destr);
-
-
-/**
- * Inserts an element at the end of the list.
- * 
- * This is generally an O(n) operation, as the end of the list is retrieved with
- * ucx_list_last().
- * 
- * @param list the list where to append the data, or <code>NULL</code> to
- * create a new list
- * @param data the data to insert
- * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
- * the newly created list otherwise
- */
-UcxList *ucx_list_append(UcxList *list, void *data);
-
-/**
- * Inserts an element at the end of the list using a UcxAllocator.
- * 
- * See ucx_list_append() for details.
- * 
- * @param allocator the allocator to use
- * @param list the list where to append the data, or <code>NULL</code> to
- * create a new list
- * @param data the data to insert
- * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
- * the newly created list otherwise
- * @see ucx_list_append()
- */
-UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data);
-
-
-/**
- * Inserts an element at the beginning of the list.
- * 
- * You <i>should</i> overwrite the old list pointer by calling
- * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. However, you may
- * also perform successive calls of ucx_list_prepend() on the same list pointer,
- * as this function always searchs for the head of the list with
- * ucx_list_first().
- * 
- * @param list the list where to insert the data or <code>NULL</code> to create
- * a new list
- * @param data the data to insert
- * @return a pointer to the new list head
- */
-UcxList *ucx_list_prepend(UcxList *list, void *data);
-
-/**
- * Inserts an element at the beginning of the list using a UcxAllocator.
- * 
- * See ucx_list_prepend() for details.
- * 
- * @param allocator the allocator to use
- * @param list the list where to insert the data or <code>NULL</code> to create
- * a new list
- * @param data the data to insert
- * @return a pointer to the new list head
- * @see ucx_list_prepend()
- */
-UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data);
-
-/**
- * Concatenates two lists.
- * 
- * Either of the two arguments may be <code>NULL</code>.
- * 
- * This function modifies the references to the next/previous element of
- * the last/first element of <code>list1</code>/<code>
- * list2</code>.
- * 
- * @param list1 first list
- * @param list2 second list
- * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is
- * returned, otherwise <code>list1</code> is returned
- */
-UcxList *ucx_list_concat(UcxList *list1, UcxList *list2);
-
-/**
- * Returns the first element of a list.
- * 
- * If the argument is the list pointer, it is directly returned. Otherwise
- * this function traverses to the first element of the list and returns the
- * list pointer.
- * 
- * @param elem one element of the list
- * @return the first element of the list, the specified element is a member of
- */
-UcxList *ucx_list_first(const UcxList *elem);
-
-/**
- * Returns the last element of a list.
- * 
- * If the argument has no successor, it is the last element and therefore
- * directly returned. Otherwise this function traverses to the last element of
- * the list and returns it.
- * 
- * @param elem one element of the list
- * @return the last element of the list, the specified element is a member of
- */
-UcxList *ucx_list_last(const UcxList *elem);
-
-/**
- * Returns the list element at the specified index.
- * 
- * @param list the list to retrieve the element from
- * @param index index of the element to return
- * @return the element at the specified index or <code>NULL</code>, if the
- * index is greater than the list size
- */
-UcxList *ucx_list_get(const UcxList *list, size_t index);
-
-/**
- * Returns the index of an element.
- * 
- * @param list the list where to search for the element
- * @param elem the element to find
- * @return the index of the element or -1 if the list does not contain the
- * element
- */
-ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem);
-
-/**
- * Returns the element count of the list.
- * 
- * @param list the list whose elements are counted
- * @return the element count
- */
-size_t ucx_list_size(const UcxList *list);
-
-/**
- * Returns the index of an element containing the specified data.
- *
- * This function uses a cmp_func() to compare the data of each list element
- * with the specified data. If no cmp_func is provided, the pointers are
- * compared.
- * 
- * If the list contains the data more than once, the index of the first
- * occurrence is returned.
- *  
- * @param list the list where to search for the data
- * @param elem the element data
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return the index of the element containing the specified data or -1 if the
- * data is not found in this list
- */
-ssize_t ucx_list_find(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
-
-/**
- * Checks, if a list contains a specific element.
- * 
- * An element is found, if ucx_list_find() returns a value greater than -1.
- * 
- * @param list the list where to search for the data
- * @param elem the element data
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return 1, if and only if the list contains the specified element data
- * @see ucx_list_find()
- */
-int ucx_list_contains(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
-
-/**
- * Sorts a UcxList with natural merge sort.
- * 
- * This function uses O(n) additional temporary memory for merge operations
- * that is automatically freed after each merge.
- * 
- * As the head of the list might change, you <b>MUST</b> call this function
- * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>.
- * 
- * @param list the list to sort
- * @param cmpfnc the function that shall be used to compare the element data
- * @param data additional data for the cmp_func()
- * @return the sorted list
- */
-UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data);
-
-/**
- * Removes an element from the list.
- * 
- * If the first element is removed, the list pointer changes. So it is
- * <i>highly recommended</i> to <i>always</i> update the pointer by calling
- * <code>mylist = ucx_list_remove(mylist, myelem);</code>.
- * 
- * @param list the list from which the element shall be removed
- * @param element the element to remove
- * @return returns the updated list pointer or <code>NULL</code>, if the list
- * is now empty
- */
-UcxList *ucx_list_remove(UcxList *list, UcxList *element);
-
-/**
- * Removes an element from the list using a UcxAllocator.
- * 
- * See ucx_list_remove() for details.
- * 
- * @param allocator the allocator to use
- * @param list the list from which the element shall be removed
- * @param element the element to remove
- * @return returns the updated list pointer or <code>NULL</code>, if the list
- * @see ucx_list_remove()
- */
-UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list,
-        UcxList *element);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_LIST_H */
-
--- a/ucx/ucx/logging.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,250 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-/**
- * Logging API.
- * 
- * @file   logging.h
- * @author Mike Becker, Olaf Wintermann
- */
-#ifndef UCX_LOGGING_H
-#define UCX_LOGGING_H
-
-#include "ucx.h"
-#include "map.h"
-#include "string.h"
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* leave enough space for custom log levels */
-
-/** Log level for error messages. */
-#define UCX_LOGGER_ERROR        0x00
-    
-/** Log level for warning messages. */
-#define UCX_LOGGER_WARN         0x10
-
-/** Log level for information messages. */
-#define UCX_LOGGER_INFO         0x20
-
-/** Log level for debug messages. */
-#define UCX_LOGGER_DEBUG        0x30
-
-/** Log level for trace messages. */
-#define UCX_LOGGER_TRACE        0x40
-
-/**
- * Output flag for the log level. 
- * If this flag is set, the log message will contain the log level.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_LEVEL        0x01
-
-/**
- * Output flag for the timestmap.
- * If this flag is set, the log message will contain the timestmap.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_TIMESTAMP    0x02
-
-/**
- * Output flag for the source.
- * If this flag is set, the log message will contain the source file and line
- * number.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_SOURCE       0x04
-
-/**
- * The UCX Logger object.
- */
-typedef struct {
-    /** The stream this logger writes its messages to.*/
-    void *stream;
-
-    /**
-     * The write function that shall be used.
-     * For standard file or stdout loggers this might be standard fwrite
-     * (default).
-     */
-    write_func writer;
-
-    /**
-     * The date format for timestamp outputs including the delimiter
-     * (default: <code>"%F %T %z "</code>).
-     * @see UCX_LOGGER_TIMESTAMP
-     */
-    char *dateformat;
-
-    /**
-     * The level, this logger operates on.
-     * If a log command is issued, the message will only be logged, if the log
-     * level of the message is less or equal than the log level of the logger.
-     */
-    unsigned int level;
-
-    /**
-     * A configuration mask for automatic output. 
-     * For each flag that is set, the logger automatically outputs some extra
-     * information like the timestamp or the source file and line number.
-     * See the documentation for the flags for details.
-     */
-    unsigned int mask;
-
-    /**
-     * A map of valid log levels for this logger.
-     * 
-     * The keys represent all valid log levels and the values provide string
-     * representations, that are used, if the UCX_LOGGER_LEVEL flag is set.
-     * 
-     * The exact data types are <code>unsigned int</code> for the key and
-     * <code>const char*</code> for the value.
-     * 
-     * @see UCX_LOGGER_LEVEL
-     */
-    UcxMap* levels;
-} UcxLogger;
-
-/**
- * Creates a new logger.
- * @param stream the stream, which the logger shall write to
- * @param level the level on which the logger shall operate
- * @param mask configuration mask (cf. UcxLogger.mask)
- * @return a new logger object
- */
-UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask);
-
-/**
- * Destroys the logger.
- * 
- * The map containing the valid log levels is also automatically destroyed.
- * 
- * @param logger the logger to destroy
- */
-void ucx_logger_free(UcxLogger* logger);
-
-/**
- * Internal log function - use macros instead.
- * 
- * This function uses the <code>format</code> and variadic arguments for a
- * printf()-style output of the log message.
- * 
- * Dependent on the UcxLogger.mask some information is prepended. The complete
- * format is:
- * 
- * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code>
- * 
- * <b>Attention:</b> the message (including automatically generated information)
- * is limited to 4096 characters. The level description is limited to
- * 256 characters and the timestamp string is limited to 128 characters.
- * 
- * @param logger the logger to use
- * @param level the level to log on
- * @param file information about the source file
- * @param line information about the source line number
- * @param format format string
- * @param ... arguments
- * @see ucx_logger_log()
- */
-void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file,
-        const unsigned int line, const char* format, ...);
-
-/**
- * Registers a custom log level.
- * @param logger the logger
- * @param level the log level as unsigned integer
- * @param name a string literal describing the level
- */
-#define ucx_logger_register_level(logger, level, name) {\
-        unsigned int l; \
-            l = level; \
-            ucx_map_int_put(logger->levels, l, (void*) "[" name "]"); \
-        } while (0);
-
-/**
- * Logs a message at the specified level.
- * @param logger the logger to use
- * @param level the level to log the message on
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_log(logger, level, ...) \
-    ucx_logger_logf(logger, level, __FILE__, __LINE__, __VA_ARGS__)
-
-/**
- * Shortcut for logging an error message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_error(logger, ...) \
-    ucx_logger_log(logger, UCX_LOGGER_ERROR, __VA_ARGS__)
-
-/**
- * Shortcut for logging an information message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_info(logger, ...) \
-    ucx_logger_log(logger, UCX_LOGGER_INFO, __VA_ARGS__)
-
-/**
- * Shortcut for logging a warning message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_warn(logger, ...) \
-    ucx_logger_log(logger, UCX_LOGGER_WARN, __VA_ARGS__)
-
-/**
- * Shortcut for logging a debug message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_debug(logger, ...) \
-    ucx_logger_log(logger, UCX_LOGGER_DEBUG, __VA_ARGS__)
-
-/**
- * Shortcut for logging a trace message.
- * @param logger the logger to use
- * @param ... format string and arguments
- * @see ucx_logger_logf()
- */
-#define ucx_logger_trace(logger, ...) \
-    ucx_logger_log(logger, UCX_LOGGER_TRACE, __VA_ARGS__)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_LOGGING_H */
--- a/ucx/ucx/map.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,435 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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 map.h
- * 
- * Hash map implementation.
- * 
- * This implementation uses murmur hash 2 and separate chaining with linked
- * lists.
- * 
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_MAP_H
-#define	UCX_MAP_H
-
-#include "ucx.h"
-#include "string.h"
-#include "allocator.h"
-#include <stdio.h>
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * Loop statement for UCX maps.
- * 
- * The <code>key</code> variable is implicitly defined, but the
- * <code>value</code> variable must be already declared as type information
- * cannot be inferred.
- * 
- * @param key the variable name for the key
- * @param value the variable name for the value
- * @param iter a UcxMapIterator
- * @see ucx_map_iterator()
- */
-#define UCX_MAP_FOREACH(key,value,iter) \
-        for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&value);)
-
-/** Type for the UCX map. @see UcxMap */
-typedef struct UcxMap          UcxMap;
-
-/** Type for a key of a UcxMap. @see UcxKey */
-typedef struct UcxKey          UcxKey;
-
-/** Type for an element of a UcxMap. @see UcxMapElement */
-typedef struct UcxMapElement   UcxMapElement;
-
-/** Type for an iterator over a UcxMap. @see UcxMapIterator */
-typedef struct UcxMapIterator  UcxMapIterator;
-
-/** Structure for the UCX map. */
-struct UcxMap {
-    /** An allocator that is used for the map elements. */
-    UcxAllocator  *allocator;
-    /** The array of map element lists. */
-    UcxMapElement **map;
-    /** The size of the map is the length of the element list array. */
-    size_t        size;
-    /** The count of elements currently stored in this map. */
-    size_t        count;
-};
-
-/** Structure to publicly denote a key of a UcxMap. */
-struct UcxKey {
-    /** The key data. */
-    const void *data;
-    /** The length of the key data. */
-    size_t     len;
-    /** A cache for the hash value of the key data. */
-    int        hash;
-};
-
-/** Internal structure for a key of a UcxMap. */
-struct UcxMapKey {
-    /** The key data. */
-    void    *data;
-    /** The length of the key data. */
-    size_t  len;
-    /** The hash value of the key data. */
-    int     hash;
-};
-
-/** Structure for an element of a UcxMap. */
-struct UcxMapElement {
-    /** The value data. */
-    void              *data;
-    
-    /** A pointer to the next element in the current list. */
-    UcxMapElement     *next;
-    
-    /** The corresponding key. */
-    struct UcxMapKey  key;
-};
-
-/** Structure for an iterator over a UcxMap. */
-struct UcxMapIterator {
-    /** The map to iterate over. */
-    UcxMap        *map;
-    
-    /** The current map element. */
-    UcxMapElement *cur;
-    
-    /**
-     * The current index of the element list array.
-     * <b>Attention: </b> this is <b>NOT</b> the element index! Do <b>NOT</b>
-     * manually iterate over the map by increasing this index. Use
-     * ucx_map_iter_next().
-     * @see UcxMap.map*/
-    size_t        index;
-};
-
-/**
- * Creates a new hash map with the specified size.
- * @param size the size of the hash map
- * @return a pointer to the new hash map
- */
-UcxMap *ucx_map_new(size_t size);
-
-/**
- * Creates a new hash map with the specified size using a UcxAllocator.
- * @param allocator the allocator to use
- * @param size the size of the hash map
- * @return a pointer to the new hash map
- */
-UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size);
-
-/**
- * Frees a hash map.
- * 
- * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
- * before calling this function to achieve that.
- * 
- * @param map the map to be freed
- * @see ucx_map_free_content()
- */
-void ucx_map_free(UcxMap *map);
-
-/**
- * Frees the contents of a hash map.
- * 
- * This is a convenience function that iterates over the map and passes all
- * values to the specified destructor function.
- * 
- * If no destructor is specified (<code>NULL</code>), the free() function of
- * the map's own allocator is used.
- * 
- * You must ensure, that it is valid to pass each value in the map to the same
- * destructor function.
- * 
- * You should free or clear the map afterwards, as the contents will be invalid.
- * 
- * @param map for which the contents shall be freed
- * @param destr optional pointer to a destructor function
- * @see ucx_map_free()
- * @see ucx_map_clear()
- */
-void ucx_map_free_content(UcxMap *map, ucx_destructor destr);
-
-/**
- * Clears a hash map.
- * 
- * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
- * before calling this function to achieve that.
- * 
- * @param map the map to be cleared
- * @see ucx_map_free_content()
- */
-void ucx_map_clear(UcxMap *map);
-
-
-/**
- * Copies contents from a map to another map using a copy function.
- * 
- * <b>Note:</b> The destination map does not need to be empty. However, if it
- * contains data with keys that are also present in the source map, the contents
- * are overwritten.
- * 
- * @param from the source map
- * @param to the destination map
- * @param fnc the copy function or <code>NULL</code> if the pointer address
- * shall be copied
- * @param data additional data for the copy function
- * @return 0 on success or a non-zero value on memory allocation errors
- */
-int ucx_map_copy(UcxMap *from, UcxMap *to, copy_func fnc, void *data);
-
-/**
- * Clones the map and rehashes if necessary.
- * 
- * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant.
- * This function <i>always</i> ensures a new UcxMap.size of at least
- * 2.5*UcxMap.count.
- * 
- * @param map the map to clone
- * @param fnc the copy function to use or <code>NULL</code> if the new and
- * the old map shall share the data pointers
- * @param data additional data for the copy function
- * @return the cloned map
- * @see ucx_map_copy()
- */
-UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data);
-
-/**
- * Increases size of the hash map, if necessary.
- * 
- * The load value is 0.75*UcxMap.size. If the element count exceeds the load
- * value, the map needs to be rehashed. Otherwise no action is performed and
- * this function simply returns 0.
- * 
- * The rehashing process ensures, that the UcxMap.size is at least
- * 2.5*UcxMap.count. So there is enough room for additional elements without
- * the need of another soon rehashing.
- * 
- * You can use this function to dramatically increase access performance.
- * 
- * @param map the map to rehash
- * @return 1, if a memory allocation error occurred, 0 otherwise
- */
-int ucx_map_rehash(UcxMap *map);
-
-/**
- * Puts a key/value-pair into the map.
- * 
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- */
-int ucx_map_put(UcxMap *map, UcxKey key, void *value);
-
-/**
- * Retrieves a value by using a key.
- * 
- * @param map the map
- * @param key the key
- * @return the value
- */
-void* ucx_map_get(UcxMap *map, UcxKey key);
-
-/**
- * Removes a key/value-pair from the map by using the key.
- * 
- * @param map the map
- * @param key the key
- * @return the removed value
- */
-void* ucx_map_remove(UcxMap *map, UcxKey key);
-
-/**
- * Shorthand for putting data with a sstr_t key into the map.
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- * @see ucx_map_put()
- */
-#define ucx_map_sstr_put(map, key, value) \
-    ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value)
-
-/**
- * Shorthand for putting data with a C string key into the map.
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- * @see ucx_map_put()
- */
-#define ucx_map_cstr_put(map, key, value) \
-    ucx_map_put(map, ucx_key(key, strlen(key)), (void*)value)
-
-/**
- * Shorthand for putting data with an integer key into the map.
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- * @see ucx_map_put()
- */
-#define ucx_map_int_put(map, key, value) \
-    ucx_map_put(map, ucx_key(&key, sizeof(key)), (void*)value)
-
-/**
- * Shorthand for getting data from the map with a sstr_t key.
- * @param map the map
- * @param key the key
- * @return the value
- * @see ucx_map_get()
- */
-#define ucx_map_sstr_get(map, key) \
-    ucx_map_get(map, ucx_key(key.ptr, key.length))
-
-/**
- * Shorthand for getting data from the map with a C string key.
- * @param map the map
- * @param key the key
- * @return the value
- * @see ucx_map_get()
- */
-#define ucx_map_cstr_get(map, key) \
-    ucx_map_get(map, ucx_key(key, strlen(key)))
-
-/**
- * Shorthand for getting data from the map with an integer key.
- * @param map the map
- * @param key the key
- * @return the value
- * @see ucx_map_get()
- */
-#define ucx_map_int_get(map, key) \
-    ucx_map_get(map, ucx_key(&key, sizeof(int)))
-
-/**
- * Shorthand for removing data from the map with a sstr_t key.
- * @param map the map
- * @param key the key
- * @return the removed value
- * @see ucx_map_remove()
- */
-#define ucx_map_sstr_remove(map, key) \
-    ucx_map_remove(map, ucx_key(key.ptr, key.length))
-
-/**
- * Shorthand for removing data from the map with a C string key.
- * @param map the map
- * @param key the key
- * @return the removed value
- * @see ucx_map_remove()
- */
-#define ucx_map_cstr_remove(map, key) \
-    ucx_map_remove(map, ucx_key(key, strlen(key)))
-
-/**
- * Shorthand for removing data from the map with an integer key.
- * @param map the map
- * @param key the key
- * @return the removed value
- * @see ucx_map_remove()
- */
-#define ucx_map_int_remove(map, key) \
-    ucx_map_remove(map, ucx_key(&key, sizeof(key)))
-
-/**
- * Creates a UcxKey based on the given data.
- * 
- * This function implicitly computes the hash.
- * 
- * @param data the data for the key
- * @param len the length of the data
- * @return a UcxKey with implicitly computed hash
- * @see ucx_hash()
- */
-UcxKey ucx_key(const void *data, size_t len);
-
-/**
- * Computes a murmur hash-2.
- * 
- * @param data the data to hash
- * @param len the length of the data
- * @return the murmur hash-2 of the data
- */
-int ucx_hash(const char *data, size_t len);
-
-/**
- * Creates an iterator for a map.
- * 
- * <b>Note:</b> A UcxMapIterator iterates over all elements in all element
- * lists successively. Therefore the order highly depends on the key hashes and
- * may vary under different map sizes. So generally you may <b>NOT</b> rely on
- * the iteration order.
- * 
- * <b>Note:</b> The iterator is <b>NOT</b> initialized. You need to call
- * ucx_map_iter_next() at least once before accessing any information. However,
- * it is not recommended to access the fields of a UcxMapIterator directly.
- * 
- * @param map the map to create the iterator for
- * @return an iterator initialized on the first element of the
- * first element list
- * @see ucx_map_iter_next()
- */
-UcxMapIterator ucx_map_iterator(UcxMap *map);
-
-/**
- * Proceeds to the next element of the map (if any).
- * 
- * Subsequent calls on the same iterator proceed to the next element and
- * store the key/value-pair into the memory specified as arguments of this
- * function.
- * 
- * If no further elements are found, this function returns zero and leaves the
- * last found key/value-pair in memory.
- * 
- * @param iterator the iterator to use
- * @param key a pointer to the memory where to store the key
- * @param value a pointer to the memory where to store the value
- * @return 1, if another element was found, 0 if all elements has been processed
- * @see ucx_map_iterator()
- */
-int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value);
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_MAP_H */
-
--- a/ucx/ucx/mempool.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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 mempool.h
- * 
- * Memory pool implementation.
- * 
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_MEMPOOL_H
-#define	UCX_MEMPOOL_H
-
-#include "ucx.h"
-#include "allocator.h"
-#include <stddef.h>
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * UCX mempool structure.
- */
-typedef struct {
-    /** UcxAllocator based on this pool */
-    UcxAllocator *allocator;
-    
-    /** List of pointers to pooled memory. */
-    void         **data;
-    
-    /** Count of pooled memory items. */
-    size_t       ndata;
-    
-    /** Memory pool size. */
-    size_t       size;
-} UcxMempool;
-
-/** Shorthand for a new default memory pool with a capacity of 16 elements. */
-#define ucx_mempool_new_default() ucx_mempool_new(16)
-
-
-/**
- * Creates a memory pool with the specified initial size.
- * 
- * As the created memory pool automatically grows in size by factor two when
- * trying to allocate memory on a full pool, it is recommended that you use
- * a power of two for the initial size.
- * 
- * @param n initial pool size (should be a power of two, e.g. 16)
- * @return a pointer to the new memory pool
- * @see ucx_mempool_new_default()
- */
-UcxMempool *ucx_mempool_new(size_t n);
-
-/**
- * Resizes a memory pool.
- * 
- * This function will fail if the new capacity is not sufficient for the
- * present data.
- * 
- * @param pool the pool to resize
- * @param newcap the new capacity
- * @return zero on success or non-zero on failure
- */
-int ucx_mempool_chcap(UcxMempool *pool, size_t newcap);
-
-/**
- * Allocates pooled memory.
- * 
- * @param pool the memory pool
- * @param n amount of memory to allocate
- * @return a pointer to the allocated memory
- * @see ucx_allocator_malloc()
- */
-void *ucx_mempool_malloc(UcxMempool *pool, size_t n);
-/**
- * Allocates a pooled memory array.
- * 
- * The content of the allocated memory is set to zero.
- * 
- * @param pool the memory pool
- * @param nelem amount of elements to allocate
- * @param elsize amount of memory per element
- * @return a pointer to the allocated memory
- * @see ucx_allocator_calloc()
- */
-void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize);
-
-/**
- * Reallocates pooled memory.
- * 
- * If the memory to be reallocated is not contained by the specified pool, the
- * behavior is undefined.
- * 
- * @param pool the memory pool
- * @param ptr a pointer to the memory that shall be reallocated
- * @param n the new size of the memory
- * @return a pointer to the new location of the memory
- * @see ucx_allocator_realloc()
- */
-void *ucx_mempool_realloc(UcxMempool *pool, void *ptr, size_t n);
-
-/**
- * Frees pooled memory.
- * 
- * Before freeing the memory, the specified destructor function (if any)
- * is called.
- * 
- * If you specify memory, that is not pooled by the specified memory pool, the
- * program will terminate with a call to <code>abort()</code>.
- * 
- * @param pool the memory pool
- * @param ptr a pointer to the memory that shall be freed
- * @see ucx_mempool_set_destr()
- */
-void ucx_mempool_free(UcxMempool *pool, void *ptr);
-
-/**
- * Destroys a memory pool.
- * 
- * For each element the destructor function (if any) is called and the element
- * is freed.
- * 
- * Each of the registered destructor function that has no corresponding element
- * within the pool (namely those registered by ucx_mempool_reg_destr) is
- * called interleaving with the element destruction, but with guarantee to the
- * order in which they were registered (FIFO order).
- * 
- * 
- * @param pool the mempool to destroy
- */
-void ucx_mempool_destroy(UcxMempool *pool);
-
-/**
- * Sets a destructor function for the specified memory.
- * 
- * The destructor is automatically called when the memory is freed or the
- * pool is destroyed.
- * A destructor for pooled memory <b>MUST NOT</b> free the memory itself,
- * as this is done by the pool. Use a destructor to free any resources
- * managed by the pooled object.
- * 
- * The only requirement for the specified memory is, that it <b>MUST</b> be
- * pooled memory by a UcxMempool or an element-compatible mempool. The pointer
- * to the destructor function is saved in a reserved area before the actual
- * memory.
- * 
- * @param ptr pooled memory
- * @param func a pointer to the destructor function
- * @see ucx_mempool_free()
- * @see ucx_mempool_destroy()
- */
-void ucx_mempool_set_destr(void *ptr, ucx_destructor func);
-
-/**
- * Registers a destructor function for the specified (non-pooled) memory.
- *
- * This is useful, if you have memory that has not been allocated by a mempool,
- * but shall be managed by a mempool.
- * 
- * This function creates an entry in the specified mempool and the memory will
- * therefore (logically) convert to pooled memory.
- * <b>However, this does not cause the memory to be freed automatically!</b>.
- * If you want to use this function, make the memory pool free non-pooled
- * memory, the specified destructor function must call <code>free()</code>
- * by itself. But keep in mind, that you then MUST NOT use this destructor
- * function with pooled memory (e.g. in ucx_mempool_set_destr()), as it
- * would cause a double-free.
- * 
- * @param pool the memory pool
- * @param ptr data the destructor is registered for
- * @param destr a pointer to the destructor function
- */
-void ucx_mempool_reg_destr(UcxMempool *pool, void *ptr, ucx_destructor destr);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_MEMPOOL_H */
-
--- a/ucx/ucx/properties.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,221 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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 properties.h
- * 
- * Load / store utilities for properties files.
- * 
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_PROPERTIES_H
-#define	UCX_PROPERTIES_H
-
-#include "ucx.h"
-#include "map.h"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * UcxProperties object for parsing properties data.
- * Most of the fields are for internal use only. You may configure the
- * properties parser, e.g. by changing the used delimiter or specifying 
- * up to three different characters that shall introduce comments.
- */
-typedef struct {
-    /**
-     * Input buffer (don't set manually).
-     * Automatically set by calls to ucx_properties_fill().
-     */
-    char   *buffer;
-    
-    /**
-     * Length of the input buffer (don't set manually).
-     * Automatically set by calls to ucx_properties_fill().
-     */
-    size_t buflen;
-    
-    /**
-     * Current buffer position (don't set manually).
-     * Used by ucx_properties_next().
-     */
-    size_t pos;
-    
-    /**
-     * Internal temporary buffer (don't set manually).
-     * Used by ucx_properties_next().
-     */
-    char   *tmp;
-    
-    /**
-     * Internal temporary buffer length (don't set manually).
-     * Used by ucx_properties_next().
-     */
-    size_t tmplen;
-    
-    /**
-     * Internal temporary buffer capacity (don't set manually).
-     * Used by ucx_properties_next().
-     */
-    size_t tmpcap;
-    
-    /**
-     * Parser error code.
-     * This is always 0 on success and a nonzero value on syntax errors.
-     * The value is set by ucx_properties_next().
-     */
-    int    error;
-    
-    /**
-     * The delimiter that shall be used.
-     * This is '=' by default.
-     */
-    char   delimiter;
-    
-    /**
-     * The first comment character.
-     * This is '#' by default.
-     */
-    char   comment1;
-    
-    /**
-     * The second comment character.
-     * This is not set by default.
-     */
-    char   comment2;
-    
-    /**
-     * The third comment character.
-     * This is not set by default.
-     */
-    char   comment3;
-} UcxProperties;
-
-
-/**
- * Constructs a new UcxProperties object.
- * @return a pointer to the new UcxProperties object
- */
-UcxProperties *ucx_properties_new();
-
-/**
- * Destroys a UcxProperties object.
- * @param prop the UcxProperties object to destroy
- */
-void ucx_properties_free(UcxProperties *prop);
-
-/**
- * Sets the input buffer for the properties parser.
- * 
- * After calling this function, you may parse the data by calling
- * ucx_properties_next() until it returns 0. The function ucx_properties2map()
- * is a convenience function that reads as much data as possible by using this
- * function.
- * 
- * 
- * @param prop the UcxProperties object
- * @param buf a pointer to the new buffer
- * @param len the payload length of the buffer
- * @see ucx_properties_next()
- * @see ucx_properties2map()
- */
-void ucx_properties_fill(UcxProperties *prop, char *buf, size_t len);
-
-/**
- * Retrieves the next key/value-pair.
- * 
- * This function returns a nonzero value as long as there are key/value-pairs
- * found. If no more key/value-pairs are found, you may refill the input buffer
- * with ucx_properties_fill().
- * 
- * <b>Attention:</b> the sstr_t.ptr pointers of the output parameters point to
- * memory within the input buffer of the parser and will get invalid some time.
- * If you want long term copies of the key/value-pairs, use sstrdup() after
- * calling this function.
- * 
- * @param prop the UcxProperties object
- * @param name a pointer to the sstr_t that shall contain the property name
- * @param value a pointer to the sstr_t that shall contain the property value
- * @return Nonzero, if a key/value-pair was successfully retrieved
- * @see ucx_properties_fill()
- */
-int ucx_properties_next(UcxProperties *prop, sstr_t *name, sstr_t *value);
-
-/**
- * Retrieves all available key/value-pairs and puts them into a UcxMap.
- * 
- * This is done by successive calls to ucx_properties_next() until no more
- * key/value-pairs can be retrieved.
- * 
- * The memory for the map values is allocated by the map's own allocator.
- * 
- * @param prop the UcxProperties object
- * @param map the target map
- * @return The UcxProperties.error code (i.e. 0 on success).
- * @see ucx_properties_fill()
- * @see UcxMap.allocator
- */
-int ucx_properties2map(UcxProperties *prop, UcxMap *map);
-
-/**
- * Loads a properties file to a UcxMap.
- * 
- * This is a convenience function that reads data from an input
- * stream until the end of the stream is reached.
- * 
- * @param map the map object to write the key/value-pairs to
- * @param file the <code>FILE*</code> stream to read from
- * @return 0 on success, or a non-zero value on error
- * 
- * @see ucx_properties_fill()
- * @see ucx_properties2map()
- */
-int ucx_properties_load(UcxMap *map, FILE *file);
-
-/**
- * Stores a UcxMap to a file.
- * 
- * The key/value-pairs are written by using the following format:
- * 
- * <code>[key] = [value]\\n</code>
- * 
- * @param map the map to store
- * @param file the <code>FILE*</code> stream to write to
- * @return 0 on success, or a non-zero value on error
- */
-int ucx_properties_store(UcxMap *map, FILE *file);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_PROPERTIES_H */
-
--- a/ucx/ucx/stack.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,240 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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 stack.h
- * 
- * Default stack memory allocation implementation.
- * 
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_STACK_H
-#define	UCX_STACK_H
-
-#include "ucx.h"
-#include "allocator.h"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-
-/**
- * UCX stack structure.
- */
-typedef struct {
-    /** UcxAllocator based on this stack */
-    UcxAllocator allocator;
-    
-    /** Stack size. */
-    size_t size;
-    
-    /** Pointer to the bottom of the stack */
-    char *space;
-    
-    /** Pointer to the top of the stack */
-    char *top;
-} UcxStack;
-
-/**
- * Metadata for each UCX stack element.
- */
-struct ucx_stack_metadata {
-    /**
-     * Location of the previous element (<code>NULL</code> if this is the first)
-     */
-    char *prev;
-    
-    /** Size of this element */
-    size_t size;
-};
-
-/**
- * Initializes UcxStack structure with memory.
- * 
- * @param stack a pointer to an uninitialized stack structure
- * @param space the memory area that shall be managed
- * @param size size of the memory area
- * @return a new UcxStack structure
- */
-void ucx_stack_init(UcxStack *stack, char* space, size_t size);
-
-/**
- * Allocates stack memory.
- * 
- * @param stack a pointer to the stack
- * @param n amount of memory to allocate
- * @return a pointer to the allocated memory or <code>NULL</code> on stack
- * overflow
- * @see ucx_allocator_malloc()
- */
-void *ucx_stack_malloc(UcxStack *stack, size_t n);
-
-/**
- * Allocates memory with #ucx_stack_malloc() and copies the specified data if
- * the allocation was successful.
- * 
- * @param stack a pointer to the stack
- * @param n amount of memory to allocate
- * @param data a pointer to the data to copy
- * @return a pointer to the allocated memory
- * @see ucx_stack_malloc
- */
-void *ucx_stack_push(UcxStack *stack, size_t n, const void *data);
-
-/**
- * Allocates an array of stack memory
- * 
- * The content of the allocated memory is set to zero.
- * 
- * @param stack a pointer to the stack
- * @param nelem amount of elements to allocate
- * @param elsize amount of memory per element
- * @return a pointer to the allocated memory
- * @see ucx_allocator_calloc()
- */
-void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize);
-
-/**
- * Allocates memory with #ucx_stack_calloc() and copies the specified data if
- * the allocation was successful.
- * 
- * @param stack a pointer to the stack
- * @param nelem amount of elements to allocate
- * @param elsize amount of memory per element
- * @param data a pointer to the data
- * @return a pointer to the allocated memory
- * @see ucx_stack_calloc
- */
-void *ucx_stack_pusharr(UcxStack *stack,
-        size_t nelem, size_t elsize, const void *data);
-
-/**
- * Reallocates memory on the stack.
- * 
- * Shrinking memory is always safe. Extending memory can be very expensive. 
- * 
- * @param stack the stack
- * @param ptr a pointer to the memory that shall be reallocated
- * @param n the new size of the memory
- * @return a pointer to the new location of the memory
- * @see ucx_allocator_realloc()
- */
-void *ucx_stack_realloc(UcxStack *stack, void *ptr, size_t n);
-
-/**
- * Frees memory on the stack.
- * 
- * Freeing stack memory behaves in a special way.
- * 
- * If the element, that should be freed, is the top most element of the stack,
- * it is removed from the stack. Otherwise it is marked as freed. Marked
- * elements are removed, when they become the top most elements of the stack.
- * 
- * @param stack a pointer to the stack
- * @param ptr a pointer to the memory that shall be freed
- */
-void ucx_stack_free(UcxStack *stack, void *ptr);
-
-
-/**
- * Returns the size of the top most element.
- * @param stack a pointer to the stack
- * @return the size of the top most element
- */
-#define ucx_stack_topsize(stack) ((stack)->top ? ((struct ucx_stack_metadata*)\
-                                  (stack)->top - 1)->size : 0)
-
-/**
- * Removes the top most element from the stack and copies the content to <code>
- * dest</code>, if specified.
- * 
- * Use #ucx_stack_topsize()# to get the amount of memory that must be available
- * at the location of <code>dest</code>.
- * 
- * @param stack a pointer to the stack
- * @param dest the location where the contents shall be written to, or <code>
- * NULL</code>, if the element shall only be removed.
- * @see ucx_stack_free
- * @see ucx_stack_popn
- */
-#define ucx_stack_pop(stack, dest) ucx_stack_popn(stack, dest, (size_t)-1)
-
-/**
- * Removes the top most element from the stack and copies the content to <code>
- * dest</code>.
- * 
- * This function copies at most <code>n</code> bytes to the destination, but
- * the element is always freed as a whole.
- * If the element was larger than <code>n</code>, the remaining data is lost.
- * 
- * @param stack a pointer to the stack
- * @param dest the location where the contents shall be written to
- * @param n copies at most n bytes to <code>dest</code>
- * @see ucx_stack_pop
- */
-void ucx_stack_popn(UcxStack *stack, void *dest, size_t n);
-
-/**
- * Returns the remaining available memory on the specified stack.
- * 
- * @param stack a pointer to the stack
- * @return the remaining available memory
- */
-size_t ucx_stack_avail(UcxStack *stack);
-
-/**
- * Checks, if the stack is empty.
- * 
- * @param stack a pointer to the stack
- * @return nonzero, if the stack is empty, zero otherwise
- */
-#define ucx_stack_empty(stack) (!(stack)->top)
-
-/**
- * Computes a recommended size for the stack memory area. Note, that
- * reallocations have not been taken into account, so you might need to reserve
- * twice as much memory to allow many reallocations.
- * 
- * @param size the approximate payload
- * @param elems the approximate count of element allocations
- * @return a recommended size for the stack space based on the information
- * provided
- */
-#define ucx_stack_dim(size, elems) (size+sizeof(struct ucx_stack_metadata) * \
-                                    (elems + 1))
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_STACK_H */
-
--- a/ucx/ucx/string.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,934 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-/**
- * Bounded string implementation.
- * 
- * The UCX strings (<code>sstr_t</code>) provide an alternative to C strings.
- * The main difference to C strings is, that <code>sstr_t</code> does <b>not
- * need to be <code>NULL</code>-terminated</b>. Instead the length is stored
- * within the structure.
- * 
- * When using <code>sstr_t</code>, developers must be full aware of what type
- * of string (<code>NULL</code>-terminated) or not) they are using, when 
- * accessing the <code>char* ptr</code> directly.
- * 
- * The UCX string module provides some common string functions, known from
- * standard libc, working with <code>sstr_t</code>.
- * 
- * @file   string.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_STRING_H
-#define	UCX_STRING_H
-
-#include "ucx.h"
-#include "allocator.h"
-#include <stddef.h>
-
-/*
- * Use this macro to disable the shortcuts if you experience macro collision.
- */
-#ifndef UCX_NO_SSTR_SHORTCUTS
-/**
- * Shortcut for a <code>sstr_t struct</code>
- * or <code>scstr_t struct</code> literal.
- */
-#define ST(s) { s, sizeof(s)-1 }
-
-/** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */
-#define S(s) sstrn(s, sizeof(s)-1)
-
-/** Shortcut for the conversion of a C string to a <code>scstr_t</code>. */
-#define SC(s) scstrn(s, sizeof(s)-1)
-#endif /* UCX_NO_SSTR_SHORTCUTS */
-
-/*
- * Use this macro to disable the format macros.
- */
-#ifndef UCX_NO_SSTR_FORMAT_MACROS
-/** Expands a sstr_t or scstr_t to printf arguments. */
-#define SFMT(s) (int) (s).length, (s).ptr
-
-/** Format specifier for a sstr_t or scstr_t. */
-#define PRIsstr ".*s"
-#endif /* UCX_NO_SSTR_FORMAT_MACROS */
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-/**
- * The UCX string structure.
- */
-typedef struct {
-   /** A pointer to the string
-    * (<b>not necessarily <code>NULL</code>-terminated</b>) */
-    char *ptr;
-    /** The length of the string */
-    size_t length;
-} sstr_t;
-
-/**
- * The UCX string structure for immutable (constant) strings.
- */
-typedef struct {
-    /** A constant pointer to the immutable string
-     * (<b>not necessarily <code>NULL</code>-terminated</b>) */
-    const char *ptr;
-    /** The length of the string */
-    size_t length;
-} scstr_t;
-
-#ifdef	__cplusplus
-}
-#endif
-
-
-#ifdef __cplusplus
-/**
- * One of two type adjustment functions that return a scstr_t.
- * 
- * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
- * 
- * <b>Do not use this function manually.</b>
- * 
- * @param str some sstr_t
- * @return an immutable (scstr_t) version of the provided string.
- */
-inline scstr_t s2scstr(sstr_t s) {
-    scstr_t c;
-    c.ptr = s.ptr;
-    c.length = s.length;
-    return c;
-}
-
-/**
- * One of two type adjustment functions that return a scstr_t.
- * 
- * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
- * This variant is used, when the string is already immutable and no operation
- * needs to be performed.
- * 
- * <b>Do not use this function manually.</b>
- * 
- * @param str some scstr_t
- * @return the argument itself
- */
-inline scstr_t s2scstr(scstr_t str) {
-    return str;
-}
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return the an immutable version of the provided string
- */
-#define SCSTR(s) s2scstr(s)
-#else
-
-/**
- * One of two type adjustment functions that return a scstr_t.
- * 
- * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
- * This variant is used, when the string is already immutable and no operation
- * needs to be performed.
- * 
- * <b>Do not use this function manually.</b>
- * 
- * @param str some scstr_t
- * @return the argument itself
- */
-scstr_t ucx_sc2sc(scstr_t str);
-
-/**
- * One of two type adjustment functions that return a scstr_t.
- * 
- * Used <b>internally</b> to convert a UCX string to an immutable UCX string.
- * 
- * <b>Do not use this function manually.</b>
- * 
- * @param str some sstr_t
- * @return an immutable (scstr_t) version of the provided string.
- */
-scstr_t ucx_ss2sc(sstr_t str);
-
-#if __STDC_VERSION__ >= 201112L
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return the an immutable version of the provided string
- */
-#define SCSTR(str) _Generic(str, sstr_t: ucx_ss2sc, scstr_t: ucx_sc2sc)(str)
-
-#elif defined(__GNUC__) || defined(__clang__)
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return the an immutable version of the provided string
- */
-#define SCSTR(str) __builtin_choose_expr( \
-        __builtin_types_compatible_p(typeof(str), sstr_t), \
-        ucx_ss2sc, \
-        ucx_sc2sc)(str)
-
-#elif defined(__sun)
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return the an immutable version of the provided string
- */
-#define SCSTR(str) ({typeof(str) ucx_tmp_var_str = str; \
-	scstr_t ucx_tmp_var_c; \
-	ucx_tmp_var_c.ptr = ucx_tmp_var_str.ptr;\
-	ucx_tmp_var_c.length = ucx_tmp_var_str.length;\
-	ucx_tmp_var_c; })
-#else /* no generics and no builtins */
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * 
- * This <b>internal</b> function (ab)uses the C standard an expects one single
- * argument which is then implicitly converted to scstr_t without a warning.
- * 
- * <b>Do not use this function manually.</b>
- * 
- * @return the an immutable version of the provided string
- */
-scstr_t ucx_ss2c_s();
-
-/**
- * Converts a UCX string to an immutable UCX string (scstr_t).
- * @param str some UCX string
- * @return the an immutable version of the provided string
- */
-#define SCSTR(str) ucx_ss2c_s(str)
-#endif /* C11 feature test */
-
-#endif /* C++ */
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-
-/**
- * Creates a new sstr_t based on a C string.
- * 
- * The length is implicitly inferred by using a call to <code>strlen()</code>.
- *
- * <b>Note:</b> the sstr_t will hold a <i>reference</i> to the C string. If you
- * do want a copy, use sstrdup() on the return value of this function.
- * 
- * If you need to wrap a constant string, use scstr().
- * 
- * @param cstring the C string to wrap
- * @return a new sstr_t containing the C string
- * 
- * @see sstrn()
- */
-sstr_t sstr(char *cstring);
-
-/**
- * Creates a new sstr_t of the specified length based on a C string.
- *
- * <b>Note:</b> the sstr_t will hold a <i>reference</i> to the C string. If you
- * do want a copy, use sstrdup() on the return value of this function.
- * 
- * If you need to wrap a constant string, use scstrn().
- * 
- * @param cstring  the C string to wrap
- * @param length   the length of the string
- * @return a new sstr_t containing the C string
- * 
- * @see sstr()
- * @see S()
- */
-sstr_t sstrn(char *cstring, size_t length);
-
-/**
- * Creates a new scstr_t based on a constant C string.
- * 
- * The length is implicitly inferred by using a call to <code>strlen()</code>.
- *
- * <b>Note:</b> the scstr_t will hold a <i>reference</i> to the C string. If you
- * do want a copy, use scstrdup() on the return value of this function.
- * 
- * @param cstring the C string to wrap
- * @return a new scstr_t containing the C string
- * 
- * @see scstrn()
- */
-scstr_t scstr(const char *cstring);
-
-
-/**
- * Creates a new scstr_t of the specified length based on a constant C string.
- *
- * <b>Note:</b> the scstr_t will hold a <i>reference</i> to the C string. If you
- * do want a copy, use scstrdup() on the return value of this function.
- * 
- * 
- * @param cstring  the C string to wrap
- * @param length   the length of the string
- * @return a new scstr_t containing the C string
- * 
- * @see scstr()
- */
-scstr_t scstrn(const char *cstring, size_t length);
-
-/**
- * Returns the cumulated length of all specified strings.
- * 
- * <b>Attention:</b> if the count argument does not match the count of the
- * specified strings, the behavior is undefined.
- *
- * @param count    the total number of specified strings (so at least 1)
- * @param ...      all strings
- * @return the cumulated length of all strings
- */
-size_t scstrnlen(size_t count, ...);
-
-/**
- * Alias for scstrnlen() which automatically converts the arguments.
- * 
- * @param count    the total number of specified strings (so at least 1)
- * @param ...      all strings
- * @return the cumulated length of all strings
- */
-#define sstrnlen(count, ...) scstrnlen(count, __VA_ARGS__)
-
-/**
- * Concatenates two or more strings.
- * 
- * The resulting string will be allocated by standard <code>malloc()</code>. 
- * So developers <b>MUST</b> pass the sstr_t.ptr to <code>free()</code>.
- * 
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated.
- *
- * @param count   the total number of strings to concatenate
- * @param s1      first string
- * @param ...     all remaining strings
- * @return the concatenated string
- */
-sstr_t scstrcat(size_t count, scstr_t s1, ...);
-
-/**
- * Alias for scstrcat() which automatically converts the arguments.
- * 
- * @param count   the total number of strings to concatenate
- * @param s1      first string
- * @param ...     all remaining strings
- * @return the concatenated string
- */
-#define sstrcat(count, s1, ...) scstrcat(count, SCSTR(s1), __VA_ARGS__)
-
-/**
- * Concatenates two or more strings using a UcxAllocator.
- * 
- * See scstrcat() for details.
- *
- * @param a       the allocator to use
- * @param count   the total number of strings to concatenate
- * @param s1      first string
- * @param ...     all remaining strings
- * @return the concatenated string
- */
-sstr_t scstrcat_a(UcxAllocator *a, size_t count, scstr_t s1, ...);
-
-/**
- * Alias for scstrcat_a() which automatically converts the arguments.
- * 
- * See sstrcat() for details.
- *
- * @param a       the allocator to use
- * @param count   the total number of strings to concatenate
- * @param s1      first string
- * @param ...     all remaining strings
- * @return the concatenated string
- */
-#define sstrcat_a(a, count, s1, ...) \
-    scstrcat_a(a, count, SCSTR(s1), __VA_ARGS__)
-
-/**
- * Returns a substring starting at the specified location.
- * 
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and will <b>NOT</b> be <code>NULL</code>-terminated.
- * Use sstrdup() to get a copy.
- * 
- * @param string input string
- * @param start  start location of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- * 
- * @see sstrsubsl()
- * @see sstrchr()
- */
-sstr_t sstrsubs(sstr_t string, size_t start);
-
-/**
- * Returns a substring with a maximum length starting at the specified location.
- * 
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and will <b>NOT</b> be <code>NULL</code>-terminated.
- * Use sstrdup() to get a copy.
- * 
- * @param string input string
- * @param start  start location of the substring
- * @param length the maximum length of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- * with a maximum length of <code>length</code>
- * 
- * @see sstrsubs()
- * @see sstrchr()
- */
-sstr_t sstrsubsl(sstr_t string, size_t start, size_t length);
-
-/**
- * Returns a substring of an immutable string starting at the specified
- * location.
- * 
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and will <b>NOT</b> be <code>NULL</code>-terminated.
- * Use scstrdup() to get a copy.
- * 
- * @param string input string
- * @param start  start location of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- * 
- * @see scstrsubsl()
- * @see scstrchr()
- */
-scstr_t scstrsubs(scstr_t string, size_t start);
-
-/**
- * Returns a substring of an immutable string with a maximum length starting
- * at the specified location.
- * 
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and will <b>NOT</b> be <code>NULL</code>-terminated.
- * Use scstrdup() to get a copy.
- * 
- * @param string input string
- * @param start  start location of the substring
- * @param length the maximum length of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- * with a maximum length of <code>length</code>
- * 
- * @see scstrsubs()
- * @see scstrchr()
- */
-scstr_t scstrsubsl(scstr_t string, size_t start, size_t length);
-
-/**
- * Returns a substring starting at the location of the first occurrence of the
- * specified character.
- * 
- * If the string does not contain the character, an empty string is returned.
- * 
- * @param string the string where to locate the character
- * @param chr    the character to locate
- * @return       a substring starting at the first location of <code>chr</code>
- * 
- * @see sstrsubs()
- */
-sstr_t sstrchr(sstr_t string, int chr);
-
-/**
- * Returns a substring starting at the location of the last occurrence of the
- * specified character.
- * 
- * If the string does not contain the character, an empty string is returned.
- * 
- * @param string the string where to locate the character
- * @param chr    the character to locate
- * @return       a substring starting at the last location of <code>chr</code>
- * 
- * @see sstrsubs()
- */
-sstr_t sstrrchr(sstr_t string, int chr);
-
-/**
- * Returns an immutable substring starting at the location of the first
- * occurrence of the specified character.
- * 
- * If the string does not contain the character, an empty string is returned.
- * 
- * @param string the string where to locate the character
- * @param chr    the character to locate
- * @return       a substring starting at the first location of <code>chr</code>
- * 
- * @see scstrsubs()
- */
-scstr_t scstrchr(scstr_t string, int chr);
-
-/**
- * Returns an immutable substring starting at the location of the last
- * occurrence of the specified character.
- * 
- * If the string does not contain the character, an empty string is returned.
- * 
- * @param string the string where to locate the character
- * @param chr    the character to locate
- * @return       a substring starting at the last location of <code>chr</code>
- * 
- * @see scstrsubs()
- */
-scstr_t scstrrchr(scstr_t string, int chr);
-
-/**
- * Returns a substring starting at the location of the first occurrence of the
- * specified string.
- * 
- * If the string does not contain the other string, an empty string is returned.
- * 
- * If <code>match</code> is an empty string, the complete <code>string</code> is
- * returned.
- * 
- * @param string the string to be scanned
- * @param match  string containing the sequence of characters to match
- * @return       a substring starting at the first occurrence of
- *               <code>match</code>, or an empty string, if the sequence is not
- *               present in <code>string</code>
- */
-sstr_t scstrsstr(sstr_t string, scstr_t match);
-
-/**
- * Alias for scstrsstr() which automatically converts the match string.
- * 
- * @param string the string to be scanned
- * @param match  string containing the sequence of characters to match
- * @return       a substring starting at the first occurrence of
- *               <code>match</code>, or an empty string, if the sequence is not
- *               present in <code>string</code>
- */
-#define sstrstr(string, match) scstrsstr(string, SCSTR(match))
-
-/**
- * Returns an immutable substring starting at the location of the
- * first occurrence of the specified immutable string.
- * 
- * If the string does not contain the other string, an empty string is returned.
- * 
- * If <code>match</code> is an empty string, the complete <code>string</code> is
- * returned.
- * 
- * @param string the string to be scanned
- * @param match  string containing the sequence of characters to match
- * @return       a substring starting at the first occurrence of
- *               <code>match</code>, or an empty string, if the sequence is not
- *               present in <code>string</code>
- */
-scstr_t scstrscstr(scstr_t string, scstr_t match);
-
-/**
- * Alias for scstrscstr() which automatically converts the match string.
- * 
- * @param string the string to be scanned
- * @param match  string containing the sequence of characters to match
- * @return       a substring starting at the first occurrence of
- *               <code>match</code>, or an empty string, if the sequence is not
- *               present in <code>string</code>
- */
-#define sstrscstr(string, match) scstrscstr(string, SCSTR(match))
-
-/**
- * Splits a string into parts by using a delimiter string.
- * 
- * This function will return <code>NULL</code>, if one of the following happens:
- * <ul>
- *   <li>the string length is zero</li>
- *   <li>the delimeter length is zero</li>
- *   <li>the string equals the delimeter</li>
- *   <li>memory allocation fails</li>
- * </ul>
- * 
- * The integer referenced by <code>count</code> is used as input and determines
- * the maximum size of the resulting array, i.e. the maximum count of splits to
- * perform + 1.
- * 
- * The integer referenced by <code>count</code> is also used as output and is
- * set to
- * <ul>
- *   <li>-2, on memory allocation errors</li>
- *   <li>-1, if either the string or the delimiter is an empty string</li>
- *   <li>0, if the string equals the delimiter</li>
- *   <li>1, if the string does not contain the delimiter</li>
- *   <li>the count of array items, otherwise</li>
- * </ul>
- * 
- * If the string starts with the delimiter, the first item of the resulting
- * array will be an empty string.
- * 
- * If the string ends with the delimiter and the maximum list size is not
- * exceeded, the last array item will be an empty string.
- * In case the list size would be exceeded, the last array item will be the
- * remaining string after the last split, <i>including</i> the terminating
- * delimiter.
- * 
- * <b>Attention:</b> The array pointer <b>AND</b> all sstr_t.ptr of the array
- * items must be manually passed to <code>free()</code>. Use sstrsplit_a() with
- * an allocator to managed memory, to avoid this.
- *
- * @param string the string to split
- * @param delim  the delimiter string
- * @param count  IN: the maximum size of the resulting array (0 = no limit),
- *               OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- * 
- * @see scstrsplit_a()
- */
-sstr_t* scstrsplit(scstr_t string, scstr_t delim, ssize_t *count);
-
-/**
- * Alias for scstrsplit() which automatically converts the arguments.
- * 
- * @param string the string to split
- * @param delim  the delimiter string
- * @param count  IN: the maximum size of the resulting array (0 = no limit),
- *               OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- * 
- * @see sstrsplit_a()
- */
-#define sstrsplit(string, delim, count) \
-    scstrsplit(SCSTR(string), SCSTR(delim), count)
-
-/**
- * Performing scstrsplit() using a UcxAllocator.
- * 
- * <i>Read the description of scstrsplit() for details.</i>
- * 
- * The memory for the sstr_t.ptr pointers of the array items and the memory for
- * the sstr_t array itself are allocated by using the UcxAllocator.malloc()
- * function.
- * 
- * <b>Note:</b> the allocator is not used for memory that is freed within the
- * same call of this function (locally scoped variables).
- * 
- * @param allocator the UcxAllocator used for allocating memory
- * @param string the string to split
- * @param delim  the delimiter string
- * @param count  IN: the maximum size of the resulting array (0 = no limit),
- *               OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- * 
- * @see scstrsplit()
- */
-sstr_t* scstrsplit_a(UcxAllocator *allocator, scstr_t string, scstr_t delim,
-        ssize_t *count);
-
-/**
- * Alias for scstrsplit_a() which automatically converts the arguments.
- * 
- * @param allocator the UcxAllocator used for allocating memory
- * @param string the string to split
- * @param delim  the delimiter string
- * @param count  IN: the maximum size of the resulting array (0 = no limit),
- *               OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- * 
- * @see sstrsplit()
- */
-#define sstrsplit_a(allocator, string, delim, count) \
-    scstrsplit_a(allocator, SCSTR(string), SCSTR(delim), count)
-
-/**
- * Compares two UCX strings with standard <code>memcmp()</code>.
- * 
- * At first it compares the scstr_t.length attribute of the two strings. The
- * <code>memcmp()</code> function is called, if and only if the lengths match.
- * 
- * @param s1 the first string
- * @param s2 the second string
- * @return -1, if the length of s1 is less than the length of s2 or 1, if the 
- * length of s1 is greater than the length of s2 or the result of
- * <code>memcmp()</code> otherwise (i.e. 0 if the strings match)
- */
-int scstrcmp(scstr_t s1, scstr_t s2);
-
-/**
- * Alias for scstrcmp() which automatically converts its arguments.
- * 
- * @param s1 the first string
- * @param s2 the second string
- * @return -1, if the length of s1 is less than the length of s2 or 1, if the 
- * length of s1 is greater than the length of s2 or the result of
- * <code>memcmp()</code> otherwise (i.e. 0 if the strings match)
- */
-#define sstrcmp(s1, s2) scstrcmp(SCSTR(s1), SCSTR(s2))
-
-/**
- * Compares two UCX strings ignoring the case.
- * 
- * At first it compares the scstr_t.length attribute of the two strings. If and
- * only if the lengths match, both strings are compared char by char ignoring
- * the case.
- * 
- * @param s1 the first string
- * @param s2 the second string
- * @return -1, if the length of s1 is less than the length of s2 or 1, if the 
- * length of s1 is greater than the length of s2 or the result of the platform
- * specific string comparison function ignoring the case.
- */
-int scstrcasecmp(scstr_t s1, scstr_t s2);
-
-/**
- * Alias for scstrcasecmp() which automatically converts the arguments.
- * 
- * @param s1 the first string
- * @param s2 the second string
- * @return -1, if the length of s1 is less than the length of s2 or 1, if the 
- * length of s1 is greater than the length of s2 or the result of the platform
- * specific string comparison function ignoring the case.
- */
-#define sstrcasecmp(s1, s2) scstrcasecmp(SCSTR(s1), SCSTR(s2))
-
-/**
- * Creates a duplicate of the specified string.
- * 
- * The new sstr_t will contain a copy allocated by standard
- * <code>malloc()</code>. So developers <b>MUST</b> pass the sstr_t.ptr to
- * <code>free()</code>.
- * 
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated and mutable, regardless of the argument.
- * 
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see scstrdup_a()
- */
-sstr_t scstrdup(scstr_t string);
-
-/**
- * Alias for scstrdup() which automatically converts the argument.
- * 
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see sstrdup_a()
- */
-#define sstrdup(string) scstrdup(SCSTR(string))
-
-/**
- * Creates a duplicate of the specified string using a UcxAllocator.
- * 
- * The new sstr_t will contain a copy allocated by the allocators
- * UcxAllocator.malloc() function. So it is implementation depended, whether the
- * returned sstr_t.ptr pointer must be passed to the allocators
- * UcxAllocator.free() function manually.
- * 
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated and mutable, regardless of the argument.
- * 
- * @param allocator a valid instance of a UcxAllocator
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see scstrdup()
- */
-sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t string);
-
-/**
- * Alias for scstrdup_a() which automatically converts the argument.
- * 
- * @param allocator a valid instance of a UcxAllocator
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see scstrdup()
- */
-#define sstrdup_a(allocator, string) scstrdup_a(allocator, SCSTR(string))
-
-
-/**
- * Omits leading and trailing spaces.
- * 
- * This function returns a new sstr_t containing a trimmed version of the
- * specified string.
- * 
- * <b>Note:</b> the new sstr_t references the same memory, thus you
- * <b>MUST NOT</b> pass the sstr_t.ptr of the return value to
- * <code>free()</code>. It is also highly recommended to avoid assignments like
- * <code>mystr = sstrtrim(mystr);</code> as you lose the reference to the
- * source string. Assignments of this type are only permitted, if the
- * sstr_t.ptr of the source string does not need to be freed or if another
- * reference to the source string exists.
- * 
- * @param string the string that shall be trimmed
- * @return a new sstr_t containing the trimmed string
- */
-sstr_t sstrtrim(sstr_t string);
-
-/**
- * Omits leading and trailing spaces.
- * 
- * This function returns a new scstr_t containing a trimmed version of the
- * specified string.
- * 
- * <b>Note:</b> the new scstr_t references the same memory, thus you
- * <b>MUST NOT</b> pass the scstr_t.ptr of the return value to
- * <code>free()</code>. It is also highly recommended to avoid assignments like
- * <code>mystr = scstrtrim(mystr);</code> as you lose the reference to the
- * source string. Assignments of this type are only permitted, if the
- * scstr_t.ptr of the source string does not need to be freed or if another
- * reference to the source string exists.
- * 
- * @param string the string that shall be trimmed
- * @return a new scstr_t containing the trimmed string
- */
-scstr_t scstrtrim(scstr_t string);
-
-/**
- * Checks, if a string has a specific prefix.
- * @param string the string to check
- * @param prefix the prefix the string should have
- * @return 1, if and only if the string has the specified prefix, 0 otherwise
- */
-int scstrprefix(scstr_t string, scstr_t prefix);
-
-/**
- * Alias for scstrprefix() which automatically converts the arguments.
- * 
- * @param string the string to check
- * @param prefix the prefix the string should have
- * @return 1, if and only if the string has the specified prefix, 0 otherwise
- */
-#define sstrprefix(string, prefix) scstrprefix(SCSTR(string), SCSTR(prefix))
-
-/**
- * Checks, if a string has a specific suffix.
- * @param string the string to check
- * @param suffix the suffix the string should have
- * @return 1, if and only if the string has the specified suffix, 0 otherwise
- */
-int scstrsuffix(scstr_t string, scstr_t suffix);
-
-/**
- * Alias for scstrsuffix() which automatically converts the arguments.
- *
- * @param string the string to check
- * @param suffix the suffix the string should have
- * @return 1, if and only if the string has the specified suffix, 0 otherwise
- */
-#define sstrsuffix(string, suffix) scstrsuffix(SCSTR(string), SCSTR(suffix))
-
-/**
- * Returns a lower case version of a string.
- * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of scstrdup() for the implications.
- * 
- * @param string the input string
- * @return the resulting lower case string
- * @see scstrdup()
- */
-sstr_t scstrlower(scstr_t string);
-
-/**
- * Alias for scstrlower() which automatically converts the argument.
- * 
- * @param string the input string
- * @return the resulting lower case string
- */
-#define sstrlower(string) scstrlower(SCSTR(string))
-
-/**
- * Returns a lower case version of a string.
- * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of scstrdup_a() for the implications.
- * 
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting lower case string
- * @see scstrdup_a()
- */
-sstr_t scstrlower_a(UcxAllocator *allocator, scstr_t string);
-
-
-/**
- * Alias for scstrlower_a() which automatically converts the argument.
- * 
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting lower case string
- */
-#define sstrlower_a(allocator, string) scstrlower_a(allocator, SCSTR(string))
-
-/**
- * Returns a upper case version of a string.
- * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of scstrdup() for the implications.
- * 
- * @param string the input string
- * @return the resulting upper case string
- * @see scstrdup()
- */
-sstr_t scstrupper(scstr_t string);
-
-/**
- * Alias for scstrupper() which automatically converts the argument.
- * 
- * @param string the input string
- * @return the resulting upper case string
- */
-#define sstrupper(string) scstrupper(SCSTR(string))
-
-/**
- * Returns a upper case version of a string.
- * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of scstrdup_a() for the implications.
- * 
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting upper case string
- * @see scstrdup_a()
- */
-sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string);
-
-/**
- * Alias for scstrupper_a() which automatically converts the argument.
- * 
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting upper case string
- */
-#define sstrupper_a(allocator, string) scstrupper_a(allocator, string)
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_STRING_H */
--- a/ucx/ucx/test.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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: test.h
- * 
- * UCX Test Framework.
- * 
- * Usage of this test framework:
- *
- * **** IN HEADER FILE: ****
- *
- * <pre>
- * UCX_TEST(function_name);
- * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
- * </pre>
- *
- * **** IN SOURCE FILE: ****
- * <pre>
- * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
- *   // tests with UCX_TEST_ASSERT()
- * }
- * 
- * UCX_TEST(function_name) {
- *   // memory allocation and other stuff here
- *   #UCX_TEST_BEGIN
- *   // tests with UCX_TEST_ASSERT() and/or
- *   // calls with UCX_TEST_CALL_SUBROUTINE() here
- *   #UCX_TEST_END
- *   // cleanup of memory here
- * }
- * </pre>
- *
- * <b>Note:</b> if a test fails, a longjump is performed
- * back to the #UCX_TEST_BEGIN macro!
- * 
- * <b>Attention:</b> Do not call own functions within a test, that use
- * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE().
- * 
- *
- * @author Mike Becker
- * @author Olaf Wintermann
- *
- */
-
-#ifndef UCX_TEST_H
-#define	UCX_TEST_H
-
-#include "ucx.h"
-#include <stdio.h>
-#include <string.h>
-#include <setjmp.h>
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-#ifndef __FUNCTION__
-
-/**
- * Alias for the <code>__func__</code> preprocessor macro.
- * Some compilers use <code>__func__</code> and others use __FUNCTION__.
- * We use __FUNCTION__ so we define it for those compilers which use
- * <code>__func__</code>.
- */
-#define __FUNCTION__ __func__
-#endif
-
-/** Type for the UcxTestSuite. */
-typedef struct UcxTestSuite UcxTestSuite;
-
-/** Pointer to a test function. */
-typedef void(*UcxTest)(UcxTestSuite*,FILE*);
-
-/** Type for the internal list of test cases. */
-typedef struct UcxTestList UcxTestList;
-
-/** Structure for the internal list of test cases. */
-struct UcxTestList {
-    
-    /** Test case. */
-    UcxTest test;
-    
-    /** Pointer to the next list element. */
-    UcxTestList *next;
-};
-
-/**
- * A test suite containing multiple test cases.
- */
-struct UcxTestSuite {
-    
-    /** The number of successful tests after the suite has been run. */
-    unsigned int success;
-    
-    /** The number of failed tests after the suite has been run. */
-    unsigned int failure;
-    
-    /**
-     * Internal list of test cases.
-     * Use ucx_test_register() to add tests to this list.
-     */
-    UcxTestList *tests;
-};
-
-/**
- * Creates a new test suite.
- * @return a new test suite
- */
-UcxTestSuite* ucx_test_suite_new();
-
-/**
- * Destroys a test suite.
- * @param suite the test suite to destroy
- */
-void ucx_test_suite_free(UcxTestSuite* suite);
-
-/**
- * Registers a test function with the specified test suite.
- * 
- * @param suite the suite, the test function shall be added to
- * @param test the test function to register
- * @return <code>EXIT_SUCCESS</code> on success or
- * <code>EXIT_FAILURE</code> on failure
- */
-int ucx_test_register(UcxTestSuite* suite, UcxTest test);
-
-/**
- * Runs a test suite and writes the test log to the specified stream.
- * @param suite the test suite to run
- * @param outstream the stream the log shall be written to
- */
-void ucx_test_run(UcxTestSuite* suite, FILE* outstream);
-
-/**
- * Macro for a #UcxTest function header.
- * 
- * Use this macro to declare and/or define a #UcxTest function.
- * 
- * @param name the name of the test function
- */
-#define UCX_TEST(name) void name(UcxTestSuite* _suite_,FILE *_output_)
-
-/**
- * Marks the begin of a test.
- * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>after</b>
- * #UCX_TEST_BEGIN.
- * 
- * @see #UCX_TEST_END
- */
-#define UCX_TEST_BEGIN fwrite("Running ", 1, 8, _output_);\
-        fwrite(__FUNCTION__, 1, strlen(__FUNCTION__), _output_);\
-        fwrite("... ", 1, 4, _output_);\
-        jmp_buf _env_; \
-        if (!setjmp(_env_)) {
-
-/**
- * Checks a test assertion.
- * If the assertion is correct, the test carries on. If the assertion is not
- * correct, the specified message (terminated by a dot and a line break) is
- * written to the test suites output stream.
- * @param condition the condition to check
- * @param message the message that shall be printed out on failure
- */
-#define UCX_TEST_ASSERT(condition,message) if (!(condition)) { \
-        fwrite(message".\n", 1, 2+strlen(message), _output_); \
-        _suite_->failure++; \
-        longjmp(_env_, 1);\
-    }
-
-/**
- * Macro for a test subroutine function header.
- * 
- * Use this to declare and/or define a subroutine that can be called by using
- * UCX_TEST_CALL_SUBROUTINE().
- * 
- * @param name the name of the subroutine
- * @param ... the parameter list
- * 
- * @see UCX_TEST_CALL_SUBROUTINE()
- */
-#define UCX_TEST_SUBROUTINE(name,...) void name(UcxTestSuite* _suite_,\
-        FILE *_output_, jmp_buf _env_, __VA_ARGS__)
-
-/**
- * Macro for calling a test subroutine.
- * 
- * Subroutines declared with UCX_TEST_SUBROUTINE() can be called by using this
- * macro.
- * 
- * <b>Note:</b> You may <b>only</b> call subroutines within a #UCX_TEST_BEGIN-
- * #UCX_TEST_END-block.
- * 
- * @param name the name of the subroutine
- * @param ... the argument list
- * 
- * @see UCX_TEST_SUBROUTINE()
- */
-#define UCX_TEST_CALL_SUBROUTINE(name,...) \
-        name(_suite_,_output_,_env_,__VA_ARGS__);
-
-/**
- * Marks the end of a test.
- * <b>Note:</b> Any UCX_TEST_ASSERT() calls must be performed <b>before</b>
- * #UCX_TEST_END.
- * 
- * @see #UCX_TEST_BEGIN
- */
-#define UCX_TEST_END fwrite("success.\n", 1, 9, _output_); _suite_->success++;}
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_TEST_H */
-
--- a/ucx/ucx/ucx.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,195 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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.
- */
-/**
- * Main UCX Header providing most common definitions.
- * 
- * @file   ucx.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_H
-#define	UCX_H
-
-/** Major UCX version as integer constant. */
-#define UCX_VERSION_MAJOR   2
-
-/** 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)
-
-#include <stdlib.h>
-#include <stdint.h>
-
-#ifdef _WIN32
-#if !(defined __ssize_t_defined || defined _SSIZE_T_)
-#include <BaseTsd.h>
-typedef SSIZE_T ssize_t;
-#define __ssize_t_defined
-#define _SSIZE_T_
-#endif /* __ssize_t_defined and _SSIZE_T */
-#else /* !_WIN32 */
-#include <sys/types.h>
-#endif /* _WIN32 */
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-    
-
-/**
- * A function pointer to a destructor function.
- * @see ucx_mempool_setdestr()
- * @see ucx_mempool_regdestr()
- */
-typedef void(*ucx_destructor)(void*);
-
-/**
- * Function pointer to a compare function.
- * 
- * The compare function shall take three arguments: the two values that shall be
- * compared and optional additional data.
- * The function shall then return -1 if the first argument is less than the
- * second argument, 1 if the first argument is greater than the second argument
- * and 0 if both arguments are equal. If the third argument is
- * <code>NULL</code>, it shall be ignored.
- */
-typedef int(*cmp_func)(const void*,const void*,void*);
-
-/**
- * Function pointer to a distance function.
- * 
- * The distance function shall take three arguments: the two values for which
- * the distance shall be computed and optional additional data.
- * The function shall then return the signed distance as integer value.
- */
-typedef intmax_t(*distance_func)(const void*,const void*,void*);
-
-/**
- * Function pointer to a copy function.
- * 
- * The copy function shall create a copy of the first argument and may use
- * additional data provided by the second argument. If the second argument is
- * <code>NULL</code>, it shall be ignored.
-
- * <b>Attention:</b> if pointers returned by functions of this type may be
- * passed to <code>free()</code> depends on the implementation of the
- * respective <code>copy_func</code>.
- */
-typedef void*(*copy_func)(const void*,void*);
-
-/**
- * Function pointer to a write function.
- * 
- * The signature of the write function shall be compatible to the signature
- * of standard <code>fwrite</code>, though it may use arbitrary data types for
- * source and destination.
- * 
- * The arguments shall contain (in ascending order): a pointer to the source,
- * the length of one element, the element count and a pointer to the
- * destination.
- */
-typedef size_t(*write_func)(const void*, size_t, size_t, void*);
-
-/**
- * Function pointer to a read function.
- * 
- * The signature of the read function shall be compatible to the signature
- * of standard <code>fread</code>, though it may use arbitrary data types for
- * source and destination.
- * 
- * The arguments shall contain (in ascending order): a pointer to the
- * destination, the length of one element, the element count and a pointer to
- * the source.
- */
-typedef size_t(*read_func)(void*, size_t, size_t, void*);
-
-
-
-#if __GNUC__ >= 5 || defined(__clang__)
-#define UCX_MUL_BUILTIN
-
-#if __WORDSIZE == 32
-/**
- * Alias for <code>__builtin_umul_overflow</code>.
- * 
- * 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 ucx_szmul(a, b, result) __builtin_umul_overflow(a, b, result)
-#else /* __WORDSIZE != 32 */
-/**
- * Alias for <code>__builtin_umull_overflow</code>.
- * 
- * 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 ucx_szmul(a, b, result) __builtin_umull_overflow(a, b, result)
-#endif /* __WORDSIZE */
-
-#else /* no GNUC or clang bultin */
-
-/**
- * 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
- */
-#define ucx_szmul(a, b, result) ucx_szmul_impl(a, b, result)
-
-int ucx_szmul_impl(size_t a, size_t b, size_t *result);
-
-#endif
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_H */
-
--- a/ucx/ucx/utils.h	Sun Apr 16 14:12:24 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,319 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2017 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
- * 
- * Compare, copy and printf functions.
- * 
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_UTILS_H
-#define UCX_UTILS_H
-
-#include "ucx.h"
-#include "string.h"
-#include "allocator.h"
-#include <inttypes.h>
-#include <string.h>
-#include <stdarg.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Default buffer size for ucx_stream_copy() and ucx_stream_ncopy().
- */
-#define UCX_STREAM_COPY_BUFSIZE 4096
-
-/**
- * Copies a string.
- * @param s the string to copy
- * @param data omitted
- * @return a pointer to a copy of s1 that can be passed to free(void*)
- */
-void *ucx_strcpy(const void *s, void *data);
-
-/**
- * Copies a memory area.
- * @param m a pointer to the memory area
- * @param n a pointer to the size_t containing the size of the memory area
- * @return a pointer to a copy of the specified memory area that can
- * be passed to free(void*)
- */
-void *ucx_memcpy(const void *m, void *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 <code>NULL</code> if a buffer
- * shall be implicitly created on the heap
- * @param bufsize the size of the copy buffer - if <code>NULL</code> was
- * provided for <code>buf</code>, this is the size of the buffer that shall be
- * implicitly created
- * @param n the maximum number of bytes that shall be copied
- * @return the total number of bytes copied
-  */
-size_t ucx_stream_bncopy(void *src, void *dest, read_func rfnc, write_func wfnc,
-        char* buf, size_t bufsize, size_t n);
-
-/**
- * Shorthand for an unbounded ucx_stream_bncopy call using a default 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
- * 
- * @see #UCX_STREAM_COPY_BUFSIZE
- */
-#define ucx_stream_copy(src,dest,rfnc,wfnc) ucx_stream_bncopy(\
-        src, dest, (read_func)rfnc, (write_func)wfnc, \
-        NULL, UCX_STREAM_COPY_BUFSIZE, (size_t)-1)
-
-/**
- * Shorthand for ucx_stream_bncopy using a default copy buffer.
- * 
- * @param src the source stream
- * @param dest the destination stream
- * @param rfnc the read function
- * @param wfnc the write function
- * @param n maximum number of bytes that shall be copied
- * @return total number of bytes copied
- */
-#define ucx_stream_ncopy(src,dest,rfnc,wfnc, n) ucx_stream_bncopy(\
-        src, dest, (read_func)rfnc, (write_func)wfnc, \
-        NULL, UCX_STREAM_COPY_BUFSIZE, n)
-
-/**
- * Shorthand for an unbounded ucx_stream_bncopy call using the specified buffer.
- * 
- * @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 <code>NULL</code> if a buffer
- * shall be implicitly created on the heap
- * @param bufsize the size of the copy buffer - if <code>NULL</code> was
- * provided for <code>buf</code>, this is the size of the buffer that shall be
- * implicitly created
- * @return total number of bytes copied
- */
-#define ucx_stream_bcopy(src,dest,rfnc,wfnc, buf, bufsize) ucx_stream_bncopy(\
-        src, dest, (read_func)rfnc, (write_func)wfnc, \
-        buf, bufsize, (size_t)-1)
-
-/**
- * Wraps the strcmp function.
- * @param s1 string one
- * @param s2 string two
- * @param data omitted
- * @return the result of strcmp(s1, s2)
- */
-int ucx_cmp_str(const void *s1, const void *s2, void *data);
-
-/**
- * Wraps the strncmp function.
- * @param s1 string one
- * @param s2 string two
- * @param n a pointer to the size_t containing the third strncmp parameter
- * @return the result of strncmp(s1, s2, *n)
- */
-int ucx_cmp_strn(const void *s1, const void *s2, void *n);
-
-/**
- * Wraps the sstrcmp function.
- * @param s1 sstr one
- * @param s2 sstr two
- * @param data ignored
- * @return the result of sstrcmp(s1, s2)
- */
-int ucx_cmp_sstr(const void *s1, const void *s2, void *data);
-
-/**
- * Compares two integers of type int.
- * @param i1 pointer to integer one
- * @param i2 pointer to integer two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_int(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type long int.
- * @param i1 pointer to long integer one
- * @param i2 pointer to long integer two
- * @param data omitted
- * @return -1, if *i1 is less than *i2, 0 if both are equal,
- * 1 if *i1 is greater than *i2
- */
-int ucx_cmp_longint(const void *i1, const void *i2, void *data);
-
-
-/**
- * Distance function for integers of type int.
- * @param i1 pointer to integer one
- * @param i2 pointer to integer two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_int(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type long int.
- * @param i1 pointer to long integer one
- * @param i2 pointer to long integer two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two real numbers of type float.
- * @param f1 pointer to float one
- * @param f2 pointer to float two
- * @param data if provided: a pointer to precision (default: 1e-6f)
- * @return -1, if *f1 is less than *f2, 0 if both are equal,
- * 1 if *f1 is greater than *f2
- */
-
-int ucx_cmp_float(const void *f1, const void *f2, void *data);
-
-/**
- * Compares two real numbers of type double.
- * @param d1 pointer to double one
- * @param d2 pointer to double two
- * @param data if provided: a pointer to precision (default: 1e-14)
- * @return -1, if *d1 is less than *d2, 0 if both are equal,
- * 1 if *d1 is greater than *d2
- */
-int ucx_cmp_double(const void *d1, const void *d2, void *data);
-
-/**
- * Compares two pointers.
- * @param ptr1 pointer one
- * @param ptr2 pointer two
- * @param data omitted
- * @return -1 if ptr1 is less than ptr2, 0 if both are equal,
- * 1 if ptr1 is greater than ptr2
- */
-int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data);
-
-/**
- * Compares two memory areas.
- * @param ptr1 pointer one
- * @param ptr2 pointer two
- * @param n a pointer to the size_t containing the third parameter for memcmp
- * @return the result of memcmp(ptr1, ptr2, *n)
- */
-int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n);
-
-/**
- * A <code>printf()</code> like function which writes the output to a stream by
- * using a write_func().
- * @param stream the stream the data is written to
- * @param wfc the write function
- * @param fmt format string
- * @param ... additional arguments
- * @return the total number of bytes written
- */
-int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...);
-
-/**
- * <code>va_list</code> version of ucx_fprintf().
- * @param stream the stream the data is written to
- * @param wfc the write function
- * @param fmt format string
- * @param ap argument list
- * @return the total number of bytes written
- * @see ucx_fprintf()
- */
-int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap);
-
-/**
- * A <code>printf()</code> like function which allocates space for a sstr_t
- * the result is written to.
- * 
- * <b>Attention</b>: The sstr_t data is allocated with the allocators
- * ucx_allocator_malloc() function. So it is implementation dependent, if
- * the returned sstr_t.ptr pointer must be passed to the allocators
- * ucx_allocator_free() function manually.
- * 
- * <b>Note</b>: The sstr_t.ptr of the return value will <i>always</i> be
- * <code>NULL</code>-terminated.
- * 
- * @param allocator the UcxAllocator used for allocating the result sstr_t
- * @param fmt format string
- * @param ... additional arguments
- * @return a sstr_t containing the formatted string
- */
-sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...);
-
-/**
- * <code>va_list</code> version of ucx_asprintf().
- * 
- * @param allocator the UcxAllocator used for allocating the result sstr_t
- * @param fmt format string
- * @param ap argument list
- * @return a sstr_t containing the formatted string
- * @see ucx_asprintf()
- */
-sstr_t ucx_vasprintf(UcxAllocator *allocator, const char *fmt, va_list ap);
-
-/** Shortcut for ucx_asprintf() with default allocator. */
-#define ucx_sprintf(...) \
-    ucx_asprintf(ucx_default_allocator(), __VA_ARGS__)
-
-/**
- * A <code>printf()</code> like function which writes the output to a
- * UcxBuffer.
- * 
- * @param buffer the buffer the data is written to
- * @param ... format string and additional arguments
- * @return the total number of bytes written
- * @see ucx_fprintf()
- */
-#define ucx_bprintf(buffer, ...) ucx_fprintf((UcxBuffer*)buffer, \
-        (write_func)ucx_buffer_write, __VA_ARGS__)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* UCX_UTILS_H */
-
--- a/ucx/utils.c	Sun Apr 16 14:12:24 2023 +0200
+++ b/ucx/utils.c	Fri Apr 21 21:25:32 2023 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * 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:
@@ -26,263 +26,69 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx/utils.h"
+#include "cx/utils.h"
 
-#include <math.h>
-#include <stdio.h>
-#include <limits.h>
-#include <errno.h>
+#define CX_STREAM_BCOPY_BUF_SIZE 8192
+#define CX_STREAM_COPY_BUF_SIZE 1024
 
-/* COPY FUCNTIONS */
-void* ucx_strcpy(const void* s, void* data) {
-    const char *str = (const char*) s;
-    size_t n = 1+strlen(str);
-    char *cpy = (char*) malloc(n);
-    memcpy(cpy, str, n);
-    return cpy;
-}
-
-void* ucx_memcpy(const void* m, void* n) {
-    size_t k = *((size_t*)n);
-    void *cpy = malloc(k);
-    memcpy(cpy, m, k);
-    return cpy;
-}
-
-size_t ucx_stream_bncopy(void *src, void *dest, read_func readfnc,
-        write_func writefnc, char* buf, size_t bufsize, size_t n) {
-    if(n == 0 || bufsize == 0) {
+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
+) {
+    if (n == 0) {
         return 0;
     }
-    
-    char *lbuf;    
+
+    char *lbuf;
     size_t ncp = 0;
-    
-    if(buf) {
+
+    if (buf) {
+        if (bufsize == 0) return 0;
         lbuf = buf;
     } else {
-        lbuf = (char*)malloc(bufsize);
-        if(lbuf == NULL) {
+        if (bufsize == 0) bufsize = CX_STREAM_BCOPY_BUF_SIZE;
+        lbuf = malloc(bufsize);
+        if (lbuf == NULL) {
             return 0;
         }
     }
-    
+
     size_t r;
     size_t rn = bufsize > n ? n : bufsize;
-    while((r = readfnc(lbuf, 1, rn, src)) != 0) {
-        r = writefnc(lbuf, 1, r, dest);
+    while ((r = rfnc(lbuf, 1, rn, src)) != 0) {
+        r = wfnc(lbuf, 1, r, dest);
         ncp += r;
         n -= r;
         rn = bufsize > n ? n : bufsize;
-        if(r == 0 || n == 0) {
+        if (r == 0 || n == 0) {
             break;
         }
     }
-    
+
     if (lbuf != buf) {
         free(lbuf);
     }
-    
+
     return ncp;
 }
 
-/* COMPARE FUNCTIONS */
-
-int ucx_cmp_str(const void *s1, const void *s2, void *data) {
-    return strcmp((const char*)s1, (const char*)s2);
-}
-
-int ucx_cmp_strn(const void *s1, const void *s2, void *n) {
-    return strncmp((const char*)s1, (const char*)s2, *((size_t*) n));
-}
-
-int ucx_cmp_sstr(const void *s1, const void *s2, void *data) {
-    sstr_t a = *(const sstr_t*) s1;
-    sstr_t b = *(const sstr_t*) s2;
-    return sstrcmp(a, b);
-}
-
-int ucx_cmp_int(const void *i1, const void *i2, void *data) {
-   int a = *((const int*) i1);
-   int b = *((const int*) i2);
-   if (a == b) {
-       return 0;
-   } else {
-       return a < b ? -1 : 1;
-   }
-}
-
-int ucx_cmp_longint(const void *i1, const void *i2, void *data) {
-   int a = *((const long int*) i1);
-   int b = *((const long int*) i2);
-   if (a == b) {
-       return 0;
-   } else {
-       return a < b ? -1 : 1;
-   }
-}
-
-intmax_t ucx_dist_int(const void *i1, const void *i2, void *data) {
-   intmax_t a = *((const int*) i1);
-   intmax_t b = *((const int*) i2);
-   return a - b;
-}
-
-intmax_t ucx_dist_longint(const void *i1, const void *i2, void *data) {
-   intmax_t a = *((const long int*) i1);
-   intmax_t b = *((const long int*) i2);
-   return a - b;
-}
-
-int ucx_cmp_float(const void *f1, const void *f2, void *epsilon) {
-   float a = *((const float*) f1);
-   float b = *((const float*) f2);
-   float e = !epsilon ? 1e-6f : *((float*)epsilon);
-   if (fabsf(a - b) < e) {
-       return 0;
-   } else {
-       return a < b ? -1 : 1;
-   }
-}
-
-int ucx_cmp_double(const void *d1, const void *d2, void *epsilon) {
-   double a = *((const double*) d1);
-   double b = *((const double*) d2);
-   double e = !epsilon ? 1e-14 : *((double*)epsilon);
-   if (fabs(a - b) < e) {
-       return 0;
-   } else {
-       return a < b ? -1 : 1;
-   }
-}
-
-int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data) {
-    const intptr_t p1 = (const intptr_t) ptr1;
-    const intptr_t p2 = (const intptr_t) ptr2;
-    if (p1 == p2) {
-        return 0;
-    } else {
-        return p1  < p2 ? -1 : 1;
-    }
-}
-
-int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) {
-    return memcmp(ptr1, ptr2, *((size_t*)n));
+size_t cx_stream_ncopy(
+        void *src,
+        void *dest,
+        cx_read_func rfnc,
+        cx_write_func wfnc,
+        size_t n
+) {
+    char buf[CX_STREAM_COPY_BUF_SIZE];
+    return cx_stream_bncopy(src, dest, rfnc, wfnc,
+                            buf, CX_STREAM_COPY_BUF_SIZE, n);
 }
 
-/* PRINTF FUNCTIONS */
-
-#ifdef va_copy
-#define UCX_PRINTF_BUFSIZE 256
-#else
-#pragma message("WARNING: C99 va_copy macro not supported by this platform" \
-                " - limiting ucx_*printf to 2 KiB")
-#define UCX_PRINTF_BUFSIZE 0x800
+#ifndef CX_SZMUL_BUILTIN
+#include "szmul.c"
 #endif
-
-int ucx_fprintf(void *stream, write_func wfc, const char *fmt, ...) {
-    int ret;
-    va_list ap;
-    va_start(ap, fmt);
-    ret = ucx_vfprintf(stream, wfc, fmt, ap);
-    va_end(ap);
-    return ret;
-}
-
-int ucx_vfprintf(void *stream, write_func wfc, const char *fmt, va_list ap) {
-    char buf[UCX_PRINTF_BUFSIZE];
-#ifdef va_copy
-    va_list ap2;
-    va_copy(ap2, ap);
-    int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
-    if (ret < 0) {
-        return ret;
-    } else if (ret < UCX_PRINTF_BUFSIZE) {
-        return (int)wfc(buf, 1, ret, stream);
-    } else {
-        if (ret == INT_MAX) {
-            errno = ENOMEM;
-            return -1;
-        }
-        
-        int len = ret + 1;
-        char *newbuf = (char*)malloc(len);
-        if (!newbuf) {
-            return -1;
-        }
-        
-        ret = vsnprintf(newbuf, len, fmt, ap2);
-        if (ret > 0) {
-            ret = (int)wfc(newbuf, 1, ret, stream);
-        }
-        free(newbuf);
-    }
-    return ret;
-#else
-    int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
-    if (ret < 0) {
-        return ret;
-    } else if (ret < UCX_PRINTF_BUFSIZE) {
-        return (int)wfc(buf, 1, ret, stream);
-    } else {
-        errno = ENOMEM;
-        return -1;
-    }
-#endif
-}
-
-sstr_t ucx_asprintf(UcxAllocator *allocator, const char *fmt, ...) {
-    va_list ap;
-    sstr_t ret;
-    va_start(ap, fmt);
-    ret = ucx_vasprintf(allocator, fmt, ap);
-    va_end(ap);
-    return ret;
-}
-
-sstr_t ucx_vasprintf(UcxAllocator *a, const char *fmt, va_list ap) {
-    sstr_t s;
-    s.ptr = NULL;
-    s.length = 0;
-    char buf[UCX_PRINTF_BUFSIZE];
-#ifdef va_copy
-    va_list ap2;
-    va_copy(ap2, ap);
-    int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
-    if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
-        s.ptr = (char*)almalloc(a, ret + 1);
-        if (s.ptr) {
-            s.length = (size_t)ret;
-            memcpy(s.ptr, buf, ret);
-            s.ptr[s.length] = '\0';
-        }
-    } else if (ret == INT_MAX) {
-        errno = ENOMEM;
-    } else  {
-        int len = ret + 1;
-        s.ptr = (char*)almalloc(a, len);
-        if (s.ptr) {
-            ret = vsnprintf(s.ptr, len, fmt, ap2);
-            if (ret < 0) {
-                free(s.ptr);
-                s.ptr = NULL;
-            } else {
-                s.length = (size_t)ret;
-            }
-        }
-    }
-#else
-    int ret = vsnprintf(buf, UCX_PRINTF_BUFSIZE, fmt, ap);
-    if (ret > 0 && ret < UCX_PRINTF_BUFSIZE) {
-        s.ptr = (char*)almalloc(a, ret + 1);
-        if (s.ptr) {
-            s.length = (size_t)ret;
-            memcpy(s.ptr, buf, ret);
-            s.ptr[s.length] = '\0';
-        }
-    } else {
-        errno = ENOMEM;
-    }
-#endif
-    return s;
-}

mercurial