switch from ucx 2 to 3

Sun, 06 Nov 2022 15:53:32 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sun, 06 Nov 2022 15:53:32 +0100
changeset 415
d938228c382e
parent 414
99a34860c105
child 416
e2093ca0ef90

switch from ucx 2 to 3

src/server/Makefile file | annotate | diff | comparison | revisions
src/server/config/acl.c file | annotate | diff | comparison | revisions
src/server/config/acl.h file | annotate | diff | comparison | revisions
src/server/config/conf.c file | annotate | diff | comparison | revisions
src/server/config/conf.h file | annotate | diff | comparison | revisions
src/server/config/initconf.c file | annotate | diff | comparison | revisions
src/server/config/initconf.h file | annotate | diff | comparison | revisions
src/server/config/keyfile.c file | annotate | diff | comparison | revisions
src/server/config/keyfile.h file | annotate | diff | comparison | revisions
src/server/config/mimeconf.c file | annotate | diff | comparison | revisions
src/server/config/mimeconf.h file | annotate | diff | comparison | revisions
src/server/config/objconf.c file | annotate | diff | comparison | revisions
src/server/config/objconf.h file | annotate | diff | comparison | revisions
src/server/config/serverconfig.c file | annotate | diff | comparison | revisions
src/server/config/serverconfig.h file | annotate | diff | comparison | revisions
src/server/daemon/acl.c file | annotate | diff | comparison | revisions
src/server/daemon/acldata.c file | annotate | diff | comparison | revisions
src/server/daemon/acldata.h file | annotate | diff | comparison | revisions
src/server/daemon/auth.c file | annotate | diff | comparison | revisions
src/server/daemon/auth.h file | annotate | diff | comparison | revisions
src/server/daemon/config.c file | annotate | diff | comparison | revisions
src/server/daemon/config.h file | annotate | diff | comparison | revisions
src/server/daemon/configmanager.c file | annotate | diff | comparison | revisions
src/server/daemon/configmanager.h file | annotate | diff | comparison | revisions
src/server/daemon/error.c file | annotate | diff | comparison | revisions
src/server/daemon/event.c file | annotate | diff | comparison | revisions
src/server/daemon/event.h file | annotate | diff | comparison | revisions
src/server/daemon/func.c file | annotate | diff | comparison | revisions
src/server/daemon/func.h file | annotate | diff | comparison | revisions
src/server/daemon/httplistener.c file | annotate | diff | comparison | revisions
src/server/daemon/httplistener.h file | annotate | diff | comparison | revisions
src/server/daemon/httpparser.c file | annotate | diff | comparison | revisions
src/server/daemon/httpparser.h file | annotate | diff | comparison | revisions
src/server/daemon/httprequest.c file | annotate | diff | comparison | revisions
src/server/daemon/httprequest.h file | annotate | diff | comparison | revisions
src/server/daemon/keyfile_auth.c file | annotate | diff | comparison | revisions
src/server/daemon/keyfile_auth.h file | annotate | diff | comparison | revisions
src/server/daemon/ldap_auth.c file | annotate | diff | comparison | revisions
src/server/daemon/ldap_auth.h file | annotate | diff | comparison | revisions
src/server/daemon/log.c file | annotate | diff | comparison | revisions
src/server/daemon/log.h file | annotate | diff | comparison | revisions
src/server/daemon/main.c file | annotate | diff | comparison | revisions
src/server/daemon/protocol.c file | annotate | diff | comparison | revisions
src/server/daemon/protocol.h file | annotate | diff | comparison | revisions
src/server/daemon/request.c file | annotate | diff | comparison | revisions
src/server/daemon/request.h file | annotate | diff | comparison | revisions
src/server/daemon/resourcepool.c file | annotate | diff | comparison | revisions
src/server/daemon/resourcepool.h file | annotate | diff | comparison | revisions
src/server/daemon/session.c file | annotate | diff | comparison | revisions
src/server/daemon/session.h file | annotate | diff | comparison | revisions
src/server/daemon/sessionhandler.c file | annotate | diff | comparison | revisions
src/server/daemon/srvctrl.c file | annotate | diff | comparison | revisions
src/server/daemon/srvctrl.h file | annotate | diff | comparison | revisions
src/server/daemon/threadpools.c file | annotate | diff | comparison | revisions
src/server/daemon/threadpools.h file | annotate | diff | comparison | revisions
src/server/daemon/vfs.c file | annotate | diff | comparison | revisions
src/server/daemon/vfs.h file | annotate | diff | comparison | revisions
src/server/daemon/vserver.c file | annotate | diff | comparison | revisions
src/server/daemon/vserver.h file | annotate | diff | comparison | revisions
src/server/daemon/webserver.c file | annotate | diff | comparison | revisions
src/server/daemon/websocket.c file | annotate | diff | comparison | revisions
src/server/plugins/java/jvm.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/config.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/config.h file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/pgtest.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/pgtest.h file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/webdav.c file | annotate | diff | comparison | revisions
src/server/plugins/postgresql/webdav.h file | annotate | diff | comparison | revisions
src/server/public/auth.h file | annotate | diff | comparison | revisions
src/server/safs/addlog.c file | annotate | diff | comparison | revisions
src/server/safs/auth.c file | annotate | diff | comparison | revisions
src/server/safs/cgi.c file | annotate | diff | comparison | revisions
src/server/safs/cgi.h file | annotate | diff | comparison | revisions
src/server/safs/cgiutils.c file | annotate | diff | comparison | revisions
src/server/safs/common.c file | annotate | diff | comparison | revisions
src/server/safs/init.c file | annotate | diff | comparison | revisions
src/server/safs/nametrans.c file | annotate | diff | comparison | revisions
src/server/safs/objecttype.c file | annotate | diff | comparison | revisions
src/server/safs/pathcheck.c file | annotate | diff | comparison | revisions
src/server/safs/service.c file | annotate | diff | comparison | revisions
src/server/test/main.c file | annotate | diff | comparison | revisions
src/server/test/objs.mk file | annotate | diff | comparison | revisions
src/server/test/test.c file | annotate | diff | comparison | revisions
src/server/test/test.h file | annotate | diff | comparison | revisions
src/server/test/testutils.c file | annotate | diff | comparison | revisions
src/server/test/testutils.h file | annotate | diff | comparison | revisions
src/server/test/uri.h file | annotate | diff | comparison | revisions
src/server/test/vfs.c file | annotate | diff | comparison | revisions
src/server/test/vfs.h file | annotate | diff | comparison | revisions
src/server/test/webdav.c file | annotate | diff | comparison | revisions
src/server/test/webdav.h file | annotate | diff | comparison | revisions
src/server/test/writer.c file | annotate | diff | comparison | revisions
src/server/test/writer.h file | annotate | diff | comparison | revisions
src/server/test/xml.h file | annotate | diff | comparison | revisions
src/server/util/date.c file | annotate | diff | comparison | revisions
src/server/util/date.h file | annotate | diff | comparison | revisions
src/server/util/io.c file | annotate | diff | comparison | revisions
src/server/util/io.h file | annotate | diff | comparison | revisions
src/server/util/object.c file | annotate | diff | comparison | revisions
src/server/util/pool.c file | annotate | diff | comparison | revisions
src/server/util/pool.h file | annotate | diff | comparison | revisions
src/server/util/pool_pvt.h file | annotate | diff | comparison | revisions
src/server/util/strbuf.c file | annotate | diff | comparison | revisions
src/server/util/strbuf.h file | annotate | diff | comparison | revisions
src/server/util/util.c file | annotate | diff | comparison | revisions
src/server/util/util.h file | annotate | diff | comparison | revisions
src/server/util/writer.c file | annotate | diff | comparison | revisions
src/server/util/writer.h file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.c file | annotate | diff | comparison | revisions
src/server/webdav/multistatus.h file | annotate | diff | comparison | revisions
src/server/webdav/operation.c file | annotate | diff | comparison | revisions
src/server/webdav/operation.h file | annotate | diff | comparison | revisions
src/server/webdav/requestparser.c file | annotate | diff | comparison | revisions
src/server/webdav/requestparser.h file | annotate | diff | comparison | revisions
src/server/webdav/webdav.c file | annotate | diff | comparison | revisions
src/server/webdav/webdav.h file | annotate | diff | comparison | revisions
src/server/webdav/xml.c file | annotate | diff | comparison | revisions
src/server/webdav/xml.h file | annotate | diff | comparison | revisions
src/tools/Makefile file | annotate | diff | comparison | revisions
src/tools/wstool.c file | annotate | diff | comparison | revisions
src/ucx/Makefile file | annotate | diff | comparison | revisions
src/ucx/allocator.c file | annotate | diff | comparison | revisions
src/ucx/array.c file | annotate | diff | comparison | revisions
src/ucx/avl.c file | annotate | diff | comparison | revisions
src/ucx/basic_mempool.c file | annotate | diff | comparison | revisions
src/ucx/buffer.c file | annotate | diff | comparison | revisions
src/ucx/compare.c file | annotate | diff | comparison | revisions
src/ucx/cx/allocator.h file | annotate | diff | comparison | revisions
src/ucx/cx/basic_mempool.h file | annotate | diff | comparison | revisions
src/ucx/cx/buffer.h file | annotate | diff | comparison | revisions
src/ucx/cx/common.h file | annotate | diff | comparison | revisions
src/ucx/cx/compare.h file | annotate | diff | comparison | revisions
src/ucx/cx/hash_key.h file | annotate | diff | comparison | revisions
src/ucx/cx/hash_map.h file | annotate | diff | comparison | revisions
src/ucx/cx/iterator.h file | annotate | diff | comparison | revisions
src/ucx/cx/linked_list.h file | annotate | diff | comparison | revisions
src/ucx/cx/list.h file | annotate | diff | comparison | revisions
src/ucx/cx/map.h file | annotate | diff | comparison | revisions
src/ucx/cx/mempool.h file | annotate | diff | comparison | revisions
src/ucx/cx/printf.h file | annotate | diff | comparison | revisions
src/ucx/cx/string.h file | annotate | diff | comparison | revisions
src/ucx/cx/tree.h file | annotate | diff | comparison | revisions
src/ucx/cx/utils.h file | annotate | diff | comparison | revisions
src/ucx/hash_key.c file | annotate | diff | comparison | revisions
src/ucx/hash_map.c file | annotate | diff | comparison | revisions
src/ucx/linked_list.c file | annotate | diff | comparison | revisions
src/ucx/list.c file | annotate | diff | comparison | revisions
src/ucx/logging.c file | annotate | diff | comparison | revisions
src/ucx/map.c file | annotate | diff | comparison | revisions
src/ucx/mempool.c file | annotate | diff | comparison | revisions
src/ucx/printf.c file | annotate | diff | comparison | revisions
src/ucx/properties.c file | annotate | diff | comparison | revisions
src/ucx/stack.c file | annotate | diff | comparison | revisions
src/ucx/string.c file | annotate | diff | comparison | revisions
src/ucx/test.c file | annotate | diff | comparison | revisions
src/ucx/tree.c file | annotate | diff | comparison | revisions
src/ucx/ucx.c file | annotate | diff | comparison | revisions
src/ucx/ucx/allocator.h file | annotate | diff | comparison | revisions
src/ucx/ucx/array.h file | annotate | diff | comparison | revisions
src/ucx/ucx/avl.h file | annotate | diff | comparison | revisions
src/ucx/ucx/buffer.h file | annotate | diff | comparison | revisions
src/ucx/ucx/list.h file | annotate | diff | comparison | revisions
src/ucx/ucx/logging.h file | annotate | diff | comparison | revisions
src/ucx/ucx/map.h file | annotate | diff | comparison | revisions
src/ucx/ucx/mempool.h file | annotate | diff | comparison | revisions
src/ucx/ucx/properties.h file | annotate | diff | comparison | revisions
src/ucx/ucx/stack.h file | annotate | diff | comparison | revisions
src/ucx/ucx/string.h file | annotate | diff | comparison | revisions
src/ucx/ucx/test.h file | annotate | diff | comparison | revisions
src/ucx/ucx/ucx.h file | annotate | diff | comparison | revisions
src/ucx/ucx/utils.h file | annotate | diff | comparison | revisions
src/ucx/utils.c file | annotate | diff | comparison | revisions
--- a/src/server/Makefile	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/Makefile	Sun Nov 06 15:53:32 2022 +0100
@@ -71,13 +71,13 @@
 	mkdir -p $@
 
 $(MAIN_TARGET): preparation $(MAINOBJS) $(DAEMON_STARTOBJ)
-	$(CXX) -o $(MAIN_TARGET) $(MAINOBJS) $(DAEMON_STARTOBJ) -L$(BUILD_ROOT)/build/lib $(LDFLAGS)
+	$(CC) -o $(MAIN_TARGET) $(MAINOBJS) $(DAEMON_STARTOBJ) -L$(BUILD_ROOT)/build/lib $(LDFLAGS)
 
 $(LIB_WSCFG): $(CONFOBJS)
 	$(CC) $(SHLIB_LDFLAGS) -o $@ $(CONFOBJS)
 
 $(TEST_TARGET): $(TESTOBJS) $(PLUGINS)
-	$(CXX) -o $(TEST_TARGET) $(TESTOBJS) -L$(BUILD_ROOT)/build/lib $(LDFLAGS) $(TEST_PLUGIN_LDFLAGS)
+	$(CC) -o $(TEST_TARGET) $(TESTOBJS) -L$(BUILD_ROOT)/build/lib $(LDFLAGS) $(TEST_PLUGIN_LDFLAGS)
 
 
 $(PLUGINS): $(MAIN_TARGET) FORCE
--- a/src/server/config/acl.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/acl.c	Sun Nov 06 15:53:32 2022 +0100
@@ -32,7 +32,7 @@
 
 #include "acl.h"
 
-ACLFile* load_acl_file(char *file) {
+ACLFile* load_acl_file(const char *file) {
     FILE *in = fopen(file, "r");
     if(in == NULL) {
         return NULL;
@@ -40,9 +40,9 @@
     
     ACLFile *conf = malloc(sizeof(ACLFile));
     conf->parser.parse = acl_parse;
-    conf->namedACLs = NULL;
-    conf->uriACLs = NULL;
-    conf->pathACLs = NULL;
+    conf->namedACLs = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr);
+    conf->uriACLs = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr);
+    conf->pathACLs = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr);
     
     int r = cfg_parse_basic_file((ConfigParser*)conf, in);
     if(r != 0) {
@@ -56,45 +56,45 @@
 }
 
 void free_acl_file(ACLFile *conf) {
-    ucx_mempool_destroy(conf->parser.mp->pool);
+    //ucx_mempool_destroy(conf->parser.mp->pool);
     free(conf);
 }
 
-int acl_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) {
+int acl_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) {
     ACLFile *aclf = p;
-    UcxAllocator *mp = aclf->parser.mp;
+    CxAllocator *mp = aclf->parser.mp;
     
-    if(sstrprefix(line, sstr("ACL "))) {
-        sstr_t param = sstrsubs(line, 4);
-        UcxList *plist = cfg_param_list(param, mp); 
+    if(cx_strprefix(cx_strcast(line), cx_str("ACL "))) {
+        cxmutstr param = cx_strsubs_m(line, 4);
+        ConfigParam *plist = cfg_param_list(param, mp); 
         ACLConfig *acl = OBJ_NEW(mp, ACLConfig);
         acl->type.ptr = NULL;
         acl->authparam = NULL;
         acl->entries = NULL;
         aclf->cur = acl;
         
-        sstr_t type = cfg_param_get(plist, sstr("type"));
-        sstr_t name = cfg_param_get(plist, sstr("name"));
-        sstr_t path = cfg_param_get(plist, sstr("path"));
-        sstr_t uri  = cfg_param_get(plist, sstr("uri"));
+        cxmutstr type = cfg_param_get(plist, cx_str("type"));
+        cxmutstr name = cfg_param_get(plist, cx_str("name"));
+        cxmutstr path = cfg_param_get(plist, cx_str("path"));
+        cxmutstr uri  = cfg_param_get(plist, cx_str("uri"));
         
         if(name.ptr) {
             acl->id = name;
-            aclf->namedACLs = ucx_list_append_a(mp, aclf->namedACLs, acl);
+            cxListAdd(aclf->namedACLs, acl);
         } else if(path.ptr) {
             acl->id = path;
-            aclf->pathACLs = ucx_list_append_a(mp, aclf->pathACLs, acl);
+            cxListAdd(aclf->pathACLs, acl);
         } else if(uri.ptr) {
             acl->id = uri;
-            aclf->uriACLs = ucx_list_append_a(mp, aclf->uriACLs, acl);
+            cxListAdd(aclf->uriACLs, acl);
         }
         
         if(type.ptr) {
             acl->type = type;
         }
-    } else if(sstrprefix(line, sstr("Authenticate "))) {
-        sstr_t param = sstrsubs(line, 13);
-        UcxList *plist = cfg_param_list(param, mp); 
+    } else if(cx_strprefix(cx_strcast(line), cx_str("Authenticate "))) {
+        cxmutstr param = cx_strsubs_m(line, 13);
+        ConfigParam *plist = cfg_param_list(param, mp); 
         aclf->cur->authparam = plist;
     } else {
         if(parse_ace(aclf, line)) {
@@ -106,14 +106,16 @@
     return 0;
 }
 
-int parse_ace(ACLFile *f, sstr_t line) {
+#define ACE_MAX_TOKENS 2048
+
+int parse_ace(ACLFile *f, cxmutstr line) {
     ACLConfig *cur = f->cur;
-    UcxAllocator *mp = f->parser.mp;
+    CxAllocator *mp = f->parser.mp;
     
-    ssize_t tkn = 0;
-    sstr_t *tk = sstrsplit(line, sstr(":"), &tkn);
+    cxstring *tk = NULL; 
+    ssize_t tkn = cx_strsplit_a(mp, cx_strcast(line), cx_str(":"), ACE_MAX_TOKENS, &tk);
     if(!tk || tkn < 3) {
-        log_ereport(LOG_FAILURE, "parse_ace: to few tokens");
+        log_ereport(LOG_FAILURE, "parse_ace: to few tokens: %.*s", (int)line.length, line.ptr);
         return 1;
     }
     
@@ -124,28 +126,28 @@
      * first step: determine who is affected by this ace
      */
     int n = 0;
-    sstr_t s = tk[0];
+    cxstring s = tk[0];
     
-    if(!sstrcmp(s, sstr("user"))) {
+    if(!cx_strcmp(s, cx_str("user"))) {
         // next token is the user name
         s = tk[1];
         n++;
-        ace->who = sstrdup_a(mp, s);
-    } else if(!sstrcmp(s, sstr("group"))) {
+        ace->who = cx_strdup_a(mp, s);
+    } else if(!cx_strcmp(s, cx_str("group"))) {
         // next token is the group name
         s = tk[1];
         n++;
-        ace->who = sstrdup_a(mp, s);
+        ace->who = cx_strdup_a(mp, s);
         ace->flags = ACLCFG_IDENTIFIER_GROUP;
-    } else if(!sstrcmp(s, sstr("owner@"))) {
+    } else if(!cx_strcmp(s, cx_str("owner@"))) {
         ace->flags = ACLCFG_OWNER;
-    } else if(!sstrcmp(s, sstr("group@"))) {
+    } else if(!cx_strcmp(s, cx_str("group@"))) {
         ace->flags = ACLCFG_GROUP;
-    } else if(!sstrcmp(s, sstr("everyone@"))) {
+    } else if(!cx_strcmp(s, cx_str("everyone@"))) {
         ace->flags = ACLCFG_EVERYONE;
     } else {
         // you can specify only the user name in the ace
-        ace->who = sstrdup_a(mp, s);
+        ace->who = cx_strdup_a(mp, s);
     }
     
     n++; //next token
@@ -161,13 +163,13 @@
     }
     s = tk[n];
     
-    ssize_t maskn = 0;
-    sstr_t *accessmask = sstrsplit(s, sstr(","), &maskn);
+    cxstring *accessmask = NULL;
+    ssize_t maskn = cx_strsplit_a(mp, s, cx_str(","), ACE_MAX_TOKENS, &accessmask);
     for(int i=0;i<maskn;i++) {
-        sstr_t access = accessmask[i];
+        cxstring access = accessmask[i];
         ace->access_mask = ace->access_mask | accstr2int(access);
     }
-    free(accessmask);
+    cxFree(mp, accessmask);
     n++; // next token
     
     /*
@@ -177,31 +179,31 @@
     int complete = 0;
     while(n < tkn) {
         s = tk[n];
-        if(!sstrcmp(s, sstr("allow"))) {
+        if(!cx_strcmp(s, cx_str("allow"))) {
             ace->type = ACLCFG_TYPE_ALLOWED;
             complete = 1;
             break;
-        } else if(!sstrcmp(s, sstr("deny"))) {
+        } else if(!cx_strcmp(s, cx_str("deny"))) {
             ace->type = ACLCFG_TYPE_DENIED;
             complete = 1;
             break;
-        } else if(!sstrcmp(s, sstr("audit"))) {
+        } else if(!cx_strcmp(s, cx_str("audit"))) {
             ace->type = ACLCFG_TYPE_AUDIT;
             complete = 1;
             break;
-        } else if(!sstrcmp(s, sstr("alarm"))) {
+        } else if(!cx_strcmp(s, cx_str("alarm"))) {
             ace->type = ACLCFG_TYPE_ALARM;
             complete = 1;
             break;
         } else {
             // set flags
-            ssize_t fln = 0;
-            sstr_t *flags = sstrsplit(s, sstr(","), &fln);
+            cxstring *flags = NULL;
+            ssize_t fln = cx_strsplit_a(mp, s, cx_str(","), ACE_MAX_TOKENS, &flags);
             for(int i=0;i<fln;i++) {
-                sstr_t flag = flags[i];
-                if(!sstrcmp(flag, sstr("successful_access_flag"))) {
+                cxstring flag = flags[i];
+                if(!cx_strcmp(flag, cx_str("successful_access_flag"))) {
                     ace->flags = ace->flags | ACLCFG_SUCCESSFUL_ACCESS_FLAG;
-                } else if(!sstrcmp(flag, sstr("failed_access_flag"))) {
+                } else if(!cx_strcmp(flag, cx_str("failed_access_flag"))) {
                     ace->flags = ace->flags | ACLCFG_FAILED_ACCESS_ACE_FLAG;
                 }
                 // TODO: other flags
@@ -216,52 +218,52 @@
         return 1;
     }
     
-    cur->entries = ucx_list_append_a(mp, cur->entries, ace);
+    CFG_ACE_ADD(&cur->entries, ace);
     
     return 0;
 }
 
-uint32_t accstr2int(sstr_t access) {
+uint32_t accstr2int(cxstring access) {
     uint32_t val = 0;
-    if(!sstrcmp(access, sstr("read"))) {
+    if(!cx_strcmp(access, cx_str("read"))) {
         val = ACLCFG_READ;
-    } else if(!sstrcmp(access, sstr("write"))) {
+    } else if(!cx_strcmp(access, cx_str("write"))) {
         val = ACLCFG_WRITE;
-    } else if(!sstrcmp(access, sstr("read_data"))) {
+    } else if(!cx_strcmp(access, cx_str("read_data"))) {
         val = ACLCFG_READ_DATA;
-    } else if(!sstrcmp(access, sstr("write_data"))) {
+    } else if(!cx_strcmp(access, cx_str("write_data"))) {
         val = ACLCFG_WRITE_DATA;
-    } else if(!sstrcmp(access, sstr("append"))) {
+    } else if(!cx_strcmp(access, cx_str("append"))) {
         val = ACLCFG_APPEND;
-    } else if(!sstrcmp(access, sstr("add"))) {
+    } else if(!cx_strcmp(access, cx_str("add"))) {
         val = ACLCFG_ADD_FILE;
-    } else if(!sstrcmp(access, sstr("add_file"))) {
+    } else if(!cx_strcmp(access, cx_str("add_file"))) {
         val = ACLCFG_ADD_FILE;
-    } else if(!sstrcmp(access, sstr("add_subdirectory"))) {
+    } else if(!cx_strcmp(access, cx_str("add_subdirectory"))) {
         val = ACLCFG_ADD_SUBDIRECTORY;
-    } else if(!sstrcmp(access, sstr("read_xattr"))) {
+    } else if(!cx_strcmp(access, cx_str("read_xattr"))) {
         val = ACLCFG_READ_XATTR;
-    } else if(!sstrcmp(access, sstr("write_xattr"))) {
+    } else if(!cx_strcmp(access, cx_str("write_xattr"))) {
         val = ACLCFG_WRITE_XATTR;
-    } else if(!sstrcmp(access, sstr("execute"))) {
+    } else if(!cx_strcmp(access, cx_str("execute"))) {
         val = ACLCFG_EXECUTE;
-    } else if(!sstrcmp(access, sstr("delete_child"))) {
+    } else if(!cx_strcmp(access, cx_str("delete_child"))) {
         val = ACLCFG_DELETE_CHILD;
-    } else if(!sstrcmp(access, sstr("delete"))) {
+    } else if(!cx_strcmp(access, cx_str("delete"))) {
         val = ACLCFG_DELETE;
-    } else if(!sstrcmp(access, sstr("read_attributes"))) {
+    } else if(!cx_strcmp(access, cx_str("read_attributes"))) {
         val = ACLCFG_READ_ATTRIBUTES;
-    } else if(!sstrcmp(access, sstr("write_attributes"))) {
+    } else if(!cx_strcmp(access, cx_str("write_attributes"))) {
         val = ACLCFG_WRITE_ATTRIBUTES;
-    } else if(!sstrcmp(access, sstr("list"))) {
+    } else if(!cx_strcmp(access, cx_str("list"))) {
         val = ACLCFG_LIST;
-    } else if(!sstrcmp(access, sstr("read_acl"))) {
+    } else if(!cx_strcmp(access, cx_str("read_acl"))) {
         val = ACLCFG_READ_ACL;
-    } else if(!sstrcmp(access, sstr("write_acl"))) {
+    } else if(!cx_strcmp(access, cx_str("write_acl"))) {
         val = ACLCFG_WRITE_ACL;
-    } else if(!sstrcmp(access, sstr("write_owner"))) {
+    } else if(!cx_strcmp(access, cx_str("write_owner"))) {
         val = ACLCFG_WRITE_OWNER;
-    } else if(!sstrcmp(access, sstr("synchronize"))) {
+    } else if(!cx_strcmp(access, cx_str("synchronize"))) {
         val = ACLCFG_SYNCHRONIZE;
     }
     return val;
--- a/src/server/config/acl.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/acl.h	Sun Nov 06 15:53:32 2022 +0100
@@ -36,30 +36,38 @@
 extern "C" {
 #endif
 
+#define CFG_ACE_ADD(list_begin, elm) \
+    cx_linked_list_add((void**)list_begin, NULL, -1, offsetof(ACEConfig, next), elm)
+    
+#define CFG_ACE_LIST_SIZE(list) \
+    cx_linked_list_size(list, offsetof(ACEConfig, next))
+    
 typedef struct _acl_conf ACLConfig;
+typedef struct _ace_conf ACEConfig;
     
 typedef struct _acl_file {
     ConfigParser parser;
-    UcxList      *namedACLs; // ACLConfig list
-    UcxList      *uriACLs;   // ACLConfig list
-    UcxList      *pathACLs;  // ACLConfig list 
+    CxList      *namedACLs; // ACLConfig list
+    CxList      *uriACLs;   // ACLConfig list
+    CxList      *pathACLs;  // ACLConfig list 
     // temp data
     ACLConfig    *cur;
 } ACLFile;
 
 struct _acl_conf {
-    sstr_t   id; // name, uri or path
-    sstr_t   type; // webserver ACL or file system ACL
-    UcxList  *authparam; // authentication parameters
-    UcxList  *entries; // ACEConfig list
+    cxmutstr    id; // name, uri or path
+    cxmutstr    type; // webserver ACL or file system ACL
+    ConfigParam *authparam; // authentication parameters
+    ACEConfig   *entries; // ACEConfig list
 };
 
-typedef struct _ace_conf {
-    sstr_t   who;
-    uint32_t access_mask;
-    uint16_t flags;
-    uint16_t type;
-} ACEConfig;
+struct _ace_conf {
+    cxmutstr  who;
+    uint32_t  access_mask;
+    uint16_t  flags;
+    uint16_t  type;
+    ACEConfig *next;
+};
 
 
 /*
@@ -115,18 +123,18 @@
 #define ACLCFG_TYPE_ALARM   0x04
 
 
-ACLFile* load_acl_file(char *file);
+ACLFile* load_acl_file(const char *file);
 
 void free_acl_file(ACLFile *aclfile);
 
 
-int acl_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line);
-int parse_ace(ACLFile *f, sstr_t line);
+int acl_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line);
+int parse_ace(ACLFile *f, cxmutstr line);
 
 /*
  * converts a access right string to an integer value
  */
-uint32_t accstr2int(sstr_t access);
+uint32_t accstr2int(cxstring access);
 
 #ifdef	__cplusplus
 }
--- a/src/server/config/conf.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/conf.c	Sun Nov 06 15:53:32 2022 +0100
@@ -31,32 +31,30 @@
 #include <string.h>
 
 int cfg_parse_basic_file(ConfigParser *parser, FILE *in) {
-    parser->lines = NULL;
-    UcxMempool *mp = ucx_mempool_new(512);
-    parser->mp = mp->allocator;
+    parser->lines_begin = NULL;
+    parser->lines_end = NULL;
+    CxMempool *mp = cxBasicMempoolCreate(512);
+    CxAllocator *a = (CxAllocator*)mp->allocator;
+    parser->mp = a;
 
     // one logical line over many lines
-    sstr_t mline;
+    cxmutstr mline;
     mline.ptr = NULL;
     mline.length = 0;
-    ConfigLine *start_line;
-    ConfigLine *end_line;
+    ConfigLine *start_line = NULL;
+    ConfigLine *end_line = NULL;
     
     // read file
-    sstr_t l;
+    cxmutstr l;
     while((l = cfg_readln(in)).ptr != NULL) {
         void *org_ptr = l.ptr;
         
         // put the line to the list
         ConfigLine *line = OBJ_NEW(parser->mp, ConfigLine);
-        line->line = sstrdup_a(parser->mp, l); // TODO: check for 0-len str
+        line->line = cx_strdup_a(parser->mp, cx_strcast(l)); // TODO: check for 0-len str
         line->object = NULL;
         line->type = LINE_OTHER;
-        if(parser->lines) {
-            parser->lines = ucx_list_append_a(parser->mp, parser->lines, line);
-        } else {
-            parser->lines = ucx_list_append_a(parser->mp, parser->lines, line);
-        }
+        CFG_LINE_ADD(&parser->lines_begin, &parser->lines_end, line);
 
         // check if the line contains something
         l = cfg_trim_comment(l);
@@ -66,10 +64,8 @@
             // check for multi line
             if(mline.ptr != NULL) {
                 // concate lines
-                char *ptr = ucx_mempool_malloc(
-                        mp,
-                        mline.length + l.length + 1);
-                
+                char *ptr = cxMalloc(a, mline.length + l.length + 1);
+                // TODO: maybe we can use cx_strcat
                 memcpy(ptr, mline.ptr, mline.length);
                 memcpy(ptr + mline.length - 1, l.ptr, l.length);
                 mline.length += l.length;
@@ -83,12 +79,12 @@
             }
             if(l.ptr[l.length - 1] == '\\') {
                 if(mline.ptr == NULL) {
-                    mline = sstrdup_a(parser->mp, l);
+                    mline = cx_strdup_a(parser->mp, cx_strcast(l));
                     start_line = line;
                 }
             } else {
                 // this line is complete so we can parse it
-                sstr_t ll; // we parse this line
+                cxmutstr ll; // we parse this line
 
                 if(mline.ptr == NULL) {
                     // single line
@@ -124,8 +120,8 @@
     return 0;
 }
 
-sstr_t cfg_readln(FILE *file) {
-    sstr_t ns;
+cxmutstr cfg_readln(FILE *file) {
+    cxmutstr ns;
     ns.ptr = NULL;
     ns.length = 0;
 
@@ -148,11 +144,11 @@
             ptr[0] = 0;
         }
 
-        sstr_t line = sstr(buf);
-        return sstrdup(line);
+        cxmutstr line = cx_mutstr(buf);
+        return cx_strdup(cx_strcast(line));
     }
 
-    sstr_t s;
+    cxmutstr s;
     s.ptr = NULL;
     s.length = 0;
     return s;
@@ -162,8 +158,8 @@
 /*
  * removes a comment from the line
  */
-sstr_t cfg_trim_comment(sstr_t line) {
-    sstr_t nl = line;
+cxmutstr cfg_trim_comment(cxmutstr line) {
+    cxmutstr nl = line;
     for(int i=0;i<line.length;i++) {
         if(line.ptr[i] == '#') {
             if(i > 0) {
@@ -177,7 +173,7 @@
             }
         }
     }
-    return sstrtrim(nl);
+    return cx_strtrim_m(nl);
 }
 
 /*
@@ -185,7 +181,7 @@
  * containing the other parameters or an empty string, if there are no more
  * parameters
  */
-sstr_t cfg_param(sstr_t params, sstr_t *name, sstr_t *value) {
+cxmutstr cfg_param(cxmutstr params, cxmutstr *name, cxmutstr *value) {
     name->ptr = NULL;
     name->length = 0;
     value->ptr = NULL;
@@ -204,7 +200,7 @@
 
             params.ptr = params.ptr + i;
             params.length -= i;
-            return sstrtrim(params);
+            return cx_strtrim_m(params);
         }
     }
 
@@ -214,7 +210,7 @@
     
     // get value
     if(i>=params.length) {
-        sstr_t ns;
+        cxmutstr ns;
         ns.ptr = NULL;
         ns.length = 0;
         return ns;
@@ -253,37 +249,33 @@
     // create new params string
     params.ptr += i;
     params.length -= i;
-    return sstrtrim(params);
+    return cx_strtrim_m(params);
 }
 
 /*
  * gets a value from a parameter
  */
-sstr_t cfg_param_get(UcxList *list, sstr_t name) {
-    while(list != NULL) {
-        ConfigParam *param = list->data;
-        if(!sstrcmp(param->name, name)) {
+cxmutstr cfg_param_get(ConfigParam *param, cxstring name) {
+    while(param != NULL) {
+        if(!cx_strcmp((cxstring){param->name.ptr, param->name.length}, name)) {
             return param->value;
         }
-        list = list->next;
+        param = param->next;
     }
-    sstr_t ns;
-    ns.ptr = NULL;
-    ns.length = 0;
-    return ns;
+    return (cxmutstr){ NULL, 0 };
 }
 
 /*
  * parses a line containing a directive and returns a ConfigDirective object
  * or NULL if an error occurs
  */
-ConfigDirective* cfg_parse_directive(sstr_t line, UcxAllocator *mp) {
+ConfigDirective* cfg_parse_directive(cxmutstr line, CxAllocator *mp) {
     if(line.length < 6) {
         log_ereport(LOG_FAILURE, "cfg_parse_directive: line too short");
         return NULL; // line too short
     }
 
-    sstr_t name;
+    cxstring name;
 
     int i;
     for(i=0;i<line.length;i++) {
@@ -296,20 +288,20 @@
 
     // create directive object
     ConfigDirective *directive = OBJ_NEW(mp, ConfigDirective);
-    directive->directive_type = sstrdup_a(mp, name);
+    directive->directive_type = cx_strdup_a(mp, name);
     directive->type_num = cfg_get_directive_type_num(name);
     directive->condition = NULL; // set later by main parsing function
     //directive->param = NULL;
 
-    sstr_t param_str;
+    cxstring param_str;
     param_str.ptr = name.ptr + i;
     param_str.length = line.length - i;
-    param_str = sstrtrim(param_str);
-    directive->value = sstrdup_a(mp, param_str);
+    param_str = cx_strtrim(param_str);
+    directive->value = cx_strdup_a(mp, param_str);
     
     /*
-    sstr_t pname;
-    sstr_t pvalue;
+    cxmutstr pname;
+    cxmutstr pvalue;
     for(;;) {
         param_str = cfg_param(param_str, &pname, &pvalue);
         if(pname.length <= 0) {
@@ -319,10 +311,10 @@
 
         // create param object
         ConfigParam *param = OBJ_NEW(mp, ConfigParam);
-        param->name = sstrdup_mp(mp, pname);
+        param->name = cx_strdup_mp(mp, pname);
 
         if(pvalue.length > 0) {
-            param->value = sstrdup_mp(mp, pvalue);
+            param->value = cx_strdup_mp(mp, pvalue);
         } else {
             param->value.ptr = NULL;
             param->value.length = 0;
@@ -337,10 +329,11 @@
     return directive;
 }
 
-UcxList* cfg_param_list(sstr_t param_str, UcxAllocator *mp) {
-    sstr_t pname;
-    sstr_t pvalue;
-    UcxList *plist = NULL;
+ConfigParam* cfg_param_list(cxmutstr param_str, CxAllocator *mp) {
+    cxmutstr pname;
+    cxmutstr pvalue;
+    ConfigParam *plist_begin = NULL;
+    ConfigParam *plist_end = NULL;
     for(;;) {
         param_str = cfg_param(param_str, &pname, &pvalue);
         if(pname.length <= 0) {
@@ -349,19 +342,20 @@
 
         // create param object
         ConfigParam *param = OBJ_NEW(mp, ConfigParam);
-        param->name = sstrdup_a(mp, pname);
+        param->name = cx_strdup_a(mp, cx_strcast(pname));
+        param->next = NULL;
 
         if(pvalue.length > 0) {
-            param->value = sstrdup_a(mp, pvalue);
+            param->value = cx_strdup_a(mp, cx_strcast(pvalue));
         } else {
             param->value.ptr = NULL;
             param->value.length = 0;
         }
 
         // add param to list
-        plist = ucx_list_append_a(mp, plist, param);
+        CFG_PARAM_ADD(&plist_begin, &plist_end, param);
     }
-    return plist;
+    return plist_begin;
 }
 
 
@@ -377,26 +371,26 @@
  *   AddLog         5
  *   Init           6
  */
-int cfg_get_directive_type_num(sstr_t type) {
+int cfg_get_directive_type_num(cxstring type) {
     /* get nsapi function type */
     
     // TODO: replace hard coded numbers
     int dt = -1;
-    if(sstrcmp(type, sstr("AuthTrans")) == 0) {
+    if(cx_strcmp(type, cx_str("AuthTrans")) == 0) {
         dt = NSAPIAuthTrans;
-    } else if(sstrcmp(type, sstr("NameTrans")) == 0) {
+    } else if(cx_strcmp(type, cx_str("NameTrans")) == 0) {
         dt = NSAPINameTrans;
-    } else if(sstrcmp(type, sstr("PathCheck")) == 0) {
+    } else if(cx_strcmp(type, cx_str("PathCheck")) == 0) {
         dt = NSAPIPathCheck;
-    } else if(sstrcmp(type, sstr("ObjectType")) == 0) {
+    } else if(cx_strcmp(type, cx_str("ObjectType")) == 0) {
         dt = NSAPIObjectType;
-    } else if(sstrcmp(type, sstr("Service")) == 0) {
+    } else if(cx_strcmp(type, cx_str("Service")) == 0) {
         dt = NSAPIService;
-    } else if(sstrcmp(type, sstr("Error")) == 0) {
+    } else if(cx_strcmp(type, cx_str("Error")) == 0) {
         dt = NSAPIError;
-    } else if(sstrcmp(type, sstr("AddLog")) == 0) {
+    } else if(cx_strcmp(type, cx_str("AddLog")) == 0) {
         dt = NSAPIAddLog;
-    } else if(sstrcmp(type, sstr("Init")) == 0) {
+    } else if(cx_strcmp(type, cx_str("Init")) == 0) {
         dt = INIT_DIRECTIVE;
     }
     return dt;
@@ -405,7 +399,7 @@
 /*
  * checks if the line contains only a comment or space
  */
-int cfg_get_basic_type(sstr_t line) {
+int cfg_get_basic_type(cxmutstr line) {
     if(line.length == 0) {
         return LINE_NOCONTENT;
     } else if(line.ptr[0] == '#') {
@@ -417,7 +411,7 @@
 /*
  * checks if the line contains a begin/end tag or a directive
  */
-int cfg_get_line_type(sstr_t line) {
+int cfg_get_line_type(cxmutstr line) {
     if(line.length < 3) {
         // this line is to short to be correct
         return LINE_ERROR;
@@ -436,16 +430,16 @@
     }
 }
 
-int cfg_get_tag_type(sstr_t tag) {
-    if(!sstrcmp(tag, sstr("Object"))) {
+int cfg_get_tag_type(cxstring tag) {
+    if(!cx_strcmp(tag, cx_str("Object"))) {
         return TAG_OBJECT;
-    } else if(!sstrcmp(tag, sstr("If"))) {
+    } else if(!cx_strcmp(tag, cx_str("If"))) {
         return TAG_IF;
-    } else if(!sstrcmp(tag, sstr("ElseIf"))) {
+    } else if(!cx_strcmp(tag, cx_str("ElseIf"))) {
         return TAG_ELSEIF;
-    } else if(!sstrcmp(tag, sstr("Else"))) {
+    } else if(!cx_strcmp(tag, cx_str("Else"))) {
         return TAG_ELSE;
-    } else if(!sstrcmp(tag, sstr("Client"))) {
+    } else if(!cx_strcmp(tag, cx_str("Client"))) {
         return TAG_CLIENT;
     }
     return -1;
@@ -455,8 +449,8 @@
  * returns the name of the ending tag
  * on error, this functions returns a zero length string
  */
-sstr_t cfg_get_end_tag_name(sstr_t line) {
-    sstr_t ns;
+cxmutstr cfg_get_end_tag_name(cxmutstr line) {
+    cxmutstr ns;
     ns.ptr = NULL;
     ns.length = 0;
 
@@ -465,7 +459,7 @@
         return ns;
     }
 
-    sstr_t name;
+    cxmutstr name;
     name.ptr = line.ptr + 2;
     name.length = line.length - 3;
 
@@ -477,10 +471,10 @@
         return ns;
     }
 
-    return sstrtrim(name);
+    return cx_strtrim_m(name);
 }
 
-ConfigTag* cfg_parse_begin_tag(sstr_t line, UcxAllocator *mp) {
+ConfigTag* cfg_parse_begin_tag(cxmutstr line, CxAllocator *mp) {
     if(line.length < 4) {
         return NULL; // this line can't contain a valid tag
     }
@@ -489,7 +483,7 @@
         return NULL; // syntax error
     }
 
-    sstr_t name;
+    cxmutstr name;
     name.ptr = line.ptr + 1;
     int i;
     for(i=1;i<line.length - 1;i++) {
@@ -504,21 +498,21 @@
 
     // create tag object
     ConfigTag *tag = OBJ_NEW(mp, ConfigTag);
-    tag->name = sstrdup_a(mp, name);
+    tag->name = cx_strdup_a(mp, cx_strcast(name));
     tag->param = NULL;
 
     // parse parameters
-    sstr_t param_str;
+    cxmutstr param_str;
     param_str.ptr = line.ptr + i;
     param_str.length = line.length - name.length - 2;
-    param_str = sstrtrim(param_str);
+    param_str = cx_strtrim_m(param_str);
     if(param_str.length == 0) {
         return tag; // no parameters
     }
-    tag->param_str = sstrdup_a(mp, param_str);
+    tag->param_str = cx_strdup_a(mp, cx_strcast(param_str));
 
-    sstr_t pname;
-    sstr_t pvalue;
+    cxmutstr pname;
+    cxmutstr pvalue;
     for(;;) {
         param_str = cfg_param(param_str, &pname, &pvalue);
         if(pname.length == 0) {
@@ -527,16 +521,17 @@
 
         // create param object
         ConfigParam *param = OBJ_NEW(mp, ConfigParam);
-        param->name = sstrdup_a(mp, pname);
+        param->next = NULL;
+        param->name = cx_strdup_a(mp, cx_strcast(pname));
         if(pvalue.length > 0) {
-            param->value = sstrdup_a(mp, pvalue);
+            param->value = cx_strdup_a(mp, cx_strcast(pvalue));
         } else {
             param->value.ptr = NULL;
             param->value.length = 0;
         }
 
         // add param to list
-        tag->param = ucx_list_append_a(mp, tag->param, param);
+        CFG_PARAM_ADD(&tag->param, NULL, param);
     }
 
     return tag;
@@ -549,11 +544,13 @@
  * gets a ConfigDirective with a specific name from a List of directives
  * returns a directive or NULL, if the directive cannot be found
  */
-ConfigDirective* cfg_directivelist_get(UcxList *dirs, sstr_t name) {
+// TODO: remove
+/*
+ConfigDirective* cfg_directivelist_get(UcxList *dirs, cxmutstr name) {
     while(dirs != NULL) {
         ConfigDirective *d = dirs->data;
         if(d != NULL) {
-            if(!sstrcmp(d->directive_type, name)) {
+            if(!cx_strcmp(d->directive_type, name)) {
                 return d;
             }
         }
@@ -562,10 +559,10 @@
     return NULL;
 }
 
-sstr_t cfg_directivelist_get_str(UcxList *dirs, sstr_t name) {
+cxmutstr cfg_directivelist_get_str(UcxList *dirs, cxmutstr name) {
     ConfigDirective *d = cfg_directivelist_get(dirs, name);
     if(d == NULL) {
-        sstr_t n;
+        cxmutstr n;
         n.ptr = NULL;
         n.length = 0;
         return n;
@@ -573,16 +570,17 @@
     //return cfg_directive_pstr1(d);
     return d->value;
 }
+*/
 
 /*
  * returns the name of the first parameter of the directive
  * useful for 'name value' directives
  */
 /*
-sstr_t cfg_directive_pstr1(ConfigDirective *dir) {
+cxmutstr cfg_directive_pstr1(ConfigDirective *dir) {
     if(dir->param == NULL) {
         fprintf(stderr, "%s", "Error: cfg_directive_pstr1: param is NULL\n");
-        sstr_t n;
+        cxmutstr n;
         n.ptr = NULL;
         n.length = 0;
         return n;
@@ -591,7 +589,7 @@
     ConfigParam *p = dir->param->data;
     return p->name;
 }
-*/
+
 
 static void cfg_list_free(void *list) {
     ucx_list_free(list);
@@ -600,14 +598,8 @@
 static void cfg_map_free(void *map) {
     ucx_map_free(map);
 }
+*/
 
 
 
-void cfg_map_destr(UcxMempool *mp, UcxMap *map) {
-    if(map) {
-        ucx_mempool_reg_destr(mp, map, cfg_map_free);
-    }
-}
 
-
-
--- a/src/server/config/conf.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/conf.h	Sun Nov 06 15:53:32 2022 +0100
@@ -32,10 +32,13 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <ucx/list.h>
-#include <ucx/map.h>
-#include <ucx/mempool.h>
-#include <ucx/string.h>
+#include <cx/linked_list.h>
+#include <cx/hash_map.h>
+#include <cx/mempool.h>
+#include <cx/basic_mempool.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/compare.h>
 
 #include "../util/object.h"
 
@@ -44,8 +47,8 @@
 #endif
    
 // mempool malloc macro
-#define OBJ_NEW(p, type) (type*)(p)->malloc((p)->pool, sizeof(type))
-#define OBJ_NEW_N(p, type) (type*)(p)->calloc((p)->pool, 1, sizeof(type))
+#define OBJ_NEW(p, type) (type*)cxMalloc(p, sizeof(type))
+#define OBJ_NEW_N(p, type) (type*)cxCalloc(p, 1, sizeof(type))
 
 // line types
 #define LINE_OTHER      0
@@ -65,85 +68,111 @@
 
     
 #define INIT_DIRECTIVE  16    
+    
+#define CFG_LINE_ADD(list_begin, list_end, elm) \
+    cx_linked_list_add((void**)list_begin, (void**)list_end, offsetof(ConfigLineList, prev), offsetof(ConfigLineList, next), elm)
+    
+#define CFG_PARAM_ADD(list_begin, list_end, elm) \
+    cx_linked_list_add((void**)list_begin, (void**)list_end, -1, offsetof(ConfigParam, next), elm)
 
+#define CFG_DIRECTIVES_ADD(list, dir) \
+    cx_linked_list_add((void**)list, NULL, -1, offsetof(ConfigDirectiveList, next), dir)
+    
+#define CFG_NUM_PARAMS(param) cx_linked_list_size(param, offsetof(ConfigParam, next))
+    
 typedef struct _cfg_line {
-    sstr_t line;    // raw line string
+    cxmutstr line;    // raw line string
     void   *object; // pointer to data struct
     int    type;    // type, see line types
 } ConfigLine;
 
-typedef int (*cfg_parse_f)(void *, ConfigLine *, ConfigLine *, sstr_t);
+typedef int (*cfg_parse_f)(void *, ConfigLine *, ConfigLine *, cxmutstr);
 
-typedef struct _cfg_param {
-    sstr_t     name;
-    sstr_t     value;
-} ConfigParam;
+typedef struct _cfg_param ConfigParam;
+struct _cfg_param {
+    cxmutstr     name;
+    cxmutstr     value;
+    ConfigParam  *next;
+};
+
+typedef struct ConfigLineList ConfigLineList;
+struct ConfigLineList {
+    ConfigLine *line;
+    ConfigLineList *prev;
+    ConfigLineList *next;
+};
 
 typedef struct _cfg_parser {
-    UcxAllocator  *mp;
-    UcxList       *lines;
-    cfg_parse_f   parse;
+    CxAllocator    *mp;
+    ConfigLineList *lines_begin;
+    ConfigLineList *lines_end;
+    cfg_parse_f    parse;
 } ConfigParser;
 
 
 typedef struct _conf_tag ConfigTag;
 struct _conf_tag {
-    ConfigLine *begin;
-    ConfigLine *end;
+    ConfigLine      *begin;
+    ConfigLine      *end;
 
-    sstr_t     name;
-    UcxList    *param;
-    sstr_t     param_str;
-    ConfigTag  *parent;
-    ConfigTag  *iftag; // only used by <ElseIf> and <Else>
-    int        type_num;
+    cxmutstr        name;
+    ConfigParam     *param;
+    cxmutstr        param_str;
+    ConfigTag       *parent;
+    ConfigTag       *iftag; // only used by <ElseIf> and <Else>
+    int             type_num;
 };
 
 typedef struct _conf_directive {
     ConfigLine *begin;
     ConfigLine *end;
 
-    sstr_t     directive_type;
-    sstr_t     value;
+    cxmutstr     directive_type;
+    cxmutstr     value;
     //UcxList    *param;
     ConfigTag  *condition;
     int        type_num;
 } ConfigDirective;
 
+typedef struct ConfigDirectiveList ConfigDirectiveList;
+struct ConfigDirectiveList {
+    ConfigDirective *directive;
+    ConfigDirectiveList *next;
+};
 
 int cfg_parse_basic_file(ConfigParser *parser, FILE *in);
 
-sstr_t cfg_readln(FILE *file);
+cxmutstr cfg_readln(FILE *file);
 
-sstr_t cfg_trim_comment(sstr_t line);
+cxmutstr cfg_trim_comment(cxmutstr line);
 
-sstr_t cfg_param(sstr_t params, sstr_t *name, sstr_t *value);
+cxmutstr cfg_param(cxmutstr params, cxmutstr *name, cxmutstr *value);
 
-sstr_t cfg_param_get(UcxList *list, sstr_t name);
+cxmutstr cfg_param_get(ConfigParam *list, cxstring name);
 
-ConfigDirective* cfg_parse_directive(sstr_t line, UcxAllocator *mp);
+ConfigDirective* cfg_parse_directive(cxmutstr line, CxAllocator *mp);
 
-UcxList* cfg_param_list(sstr_t param_str, UcxAllocator *mp);
+ConfigParam* cfg_param_list(cxmutstr param_str, CxAllocator *mp);
 
-int cfg_get_directive_type_num(sstr_t type);
+int cfg_get_directive_type_num(cxstring type);
 
-int cfg_get_basic_type(sstr_t line);
+int cfg_get_basic_type(cxmutstr line);
 
-int cfg_get_line_type(sstr_t line);
+int cfg_get_line_type(cxmutstr line);
 
-int cfg_get_tag_type(sstr_t tag);
+int cfg_get_tag_type(cxstring tag);
 
-sstr_t cfg_get_end_tag_name(sstr_t line);
+cxmutstr cfg_get_end_tag_name(cxmutstr line);
 
-ConfigTag* cfg_parse_begin_tag(sstr_t line, UcxAllocator *mp);
+ConfigTag* cfg_parse_begin_tag(cxmutstr line, CxAllocator *mp);
 
-ConfigDirective* cfg_directivelist_get(UcxList *dirs, sstr_t name);
+//ConfigDirective* cfg_directivelist_get(UcxList *dirs, cxmutstr name);
 
-sstr_t cfg_directivelist_get_str(UcxList *dirs, sstr_t name);
+//cxmutstr cfg_directivelist_get_str(UcxList *dirs, cxmutstr name);
 
-sstr_t cfg_directive_pstr1(ConfigDirective *dir);
+cxmutstr cfg_directive_pstr1(ConfigDirective *dir);
 
-void cfg_map_destr(UcxMempool *mp, UcxMap *map);
+
 
 #ifdef	__cplusplus
 }
--- a/src/server/config/initconf.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/initconf.c	Sun Nov 06 15:53:32 2022 +0100
@@ -54,11 +54,12 @@
 }
 
 void free_init_config(InitConfig *conf) {
-    ucx_mempool_destroy(conf->parser.mp->pool);
+    // TODO: fix
+    //ucx_mempool_destroy(conf->parser.mp->pool);
     free(conf);
 }
 
-int initconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) {
+int initconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) {
     InitConfig *conf = p;
     
     // parse directive
@@ -70,7 +71,11 @@
     d->begin = begin;
     d->end = end;
     if(d->type_num == INIT_DIRECTIVE) {
-        conf->directives = ucx_list_append(conf->directives, d);
+        //conf->directives = ucx_list_append(conf->directives, d);
+        ConfigDirectiveList *dir_entry = cxMalloc(conf->parser.mp, sizeof(ConfigDirectiveList));
+        dir_entry->directive = d;
+        dir_entry->next = NULL;
+        CFG_DIRECTIVES_ADD(&conf->directives, dir_entry);
     } else {
         log_ereport(LOG_WARN, "Non Init directive in init.conf");
     }
--- a/src/server/config/initconf.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/initconf.h	Sun Nov 06 15:53:32 2022 +0100
@@ -36,16 +36,16 @@
 #endif
 
 typedef struct _init_conf {
-    ConfigParser parser;
-    char         *file;
-    UcxList      *directives;
+    ConfigParser        parser;
+    char                *file;
+    ConfigDirectiveList *directives;
 } InitConfig;
 
 InitConfig *load_init_config(char *file);
 
 void free_init_config(InitConfig *conf);
 
-int initconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line);
+int initconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line);
 
 #ifdef	__cplusplus
 }
--- a/src/server/config/keyfile.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/keyfile.c	Sun Nov 06 15:53:32 2022 +0100
@@ -31,6 +31,8 @@
 
 #include "keyfile.h"
 
+#define KEYFILE_MAX_TOKENS 4096
+
 KeyfileConfig *load_keyfile_config(const char *file) {
     FILE *in = fopen(file, "r");
     if(in == NULL) {
@@ -40,7 +42,8 @@
     KeyfileConfig *conf = malloc(sizeof(KeyfileConfig));
     conf->parser.parse = keyfile_parse;
     conf->file = strdup(file);
-    conf->users = NULL;
+    conf->users_begin = NULL;
+    conf->users_end = NULL;
 
     int r = cfg_parse_basic_file((ConfigParser*)conf, in);
     if(r != 0) {
@@ -56,33 +59,34 @@
 }
 
 void free_keyfile_config(KeyfileConfig *conf) {
+    /*
     if(conf->users) {
         ucx_list_free_a(conf->parser.mp, conf->users);
     }
     ucx_mempool_destroy(conf->parser.mp->pool);
+    */
     free(conf);
 }
 
-int keyfile_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) {
+int keyfile_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) {
     KeyfileConfig *conf = p;
-    UcxAllocator *mp = conf->parser.mp;
+    CxAllocator *mp = conf->parser.mp;
     
-    ssize_t tkn = 0;
-    sstr_t *tk = sstrsplit(line, sstrn(";", 1), &tkn);
+    cxstring *tk = NULL;
+    ssize_t tkn = cx_strsplit_a(mp, cx_strcast(line), cx_strn(";", 1), KEYFILE_MAX_TOKENS, &tk);
     
     if(tkn < 2) {
         return 1;
     }
     
     KeyfileEntry *entry = OBJ_NEW(mp, KeyfileEntry);
-    entry->groups = NULL;
-    entry->numgroups = 0;
+    ZERO(entry, sizeof(KeyfileEntry));
     
     // get user name
-    entry->name = sstrdup_a(mp, tk[0]);
+    entry->name = cx_strdup_a(mp, tk[0]);
     
     // get hash
-    sstr_t hash = sstrtrim(tk[1]);
+    cxstring hash = cx_strtrim(tk[1]);
     if(hash.length < 4) {
         // to short
         return 1;
@@ -93,56 +97,52 @@
     }
     
     // get hash type and data
-    sstr_t hash_type;
-    sstr_t hash_data;
+    cxstring hash_type;
+    cxstring hash_data;
     for(int i=1;i<hash.length;i++) {
         if(hash.ptr[i] == '}') {
-            hash_type = sstrsubsl(hash, 1, i-1);
-            hash_data = sstrsubs(hash, i+1);
+            hash_type = cx_strsubsl(hash, 1, i-1);
+            hash_data = cx_strsubs(hash, i+1);
         }
     }
     
-    if(!sstrcmp(hash_type, sstr("SSHA"))) {
+    if(!cx_strcmp(hash_type, cx_str("SSHA"))) {
         entry->hashtype = KEYFILE_SSHA;
-    } else if(!sstrcmp(hash_type, sstr("SSHA256"))) {
+    } else if(!cx_strcmp(hash_type, cx_str("SSHA256"))) {
         entry->hashtype = KEYFILE_SSHA256;
-    } else if(!sstrcmp(hash_type, sstr("SSHA512"))) {
+    } else if(!cx_strcmp(hash_type, cx_str("SSHA512"))) {
         entry->hashtype = KEYFILE_SSHA512;
     } else {
         // unkown hash type
         log_ereport(
                 LOG_FAILURE,
                 "keyfile_parse: unknown hash type: %s",
-                sstrdup_a(mp, hash_type).ptr);
+                cx_strdup_a(mp, hash_type).ptr);
         return 1;
     }
     
-    entry->hashdata = sstrdup_a(mp, hash_data);
+    entry->hashdata = cx_strdup_a(mp, hash_data);
     
     // get groups
     if(tkn == 3) {
-        sstr_t groups_str = sstrtrim(tk[2]);
-        ssize_t ngroups = 0;
-        sstr_t *groups = sstrsplit(groups_str, sstrn(",", 1), &ngroups);
+        cxstring groups_str = cx_strtrim(tk[2]);
+        cxstring *groups = NULL;
+        ssize_t ngroups = cx_strsplit_a(mp, groups_str, cx_strn(",", 1), KEYFILE_MAX_TOKENS, &groups);
         if(ngroups > 0) {
-            entry->groups = mp->calloc(mp->pool, ngroups, sizeof(sstr_t));
+            entry->groups = cxCalloc(mp, ngroups, sizeof(cxmutstr));
             entry->numgroups = ngroups;
             for(int i=0;i<ngroups;i++) {
-                entry->groups[i] = sstrdup_a(mp, sstrtrim(groups[i]));
-                free(groups[i].ptr);
+                entry->groups[i] = cx_strdup_a(mp, cx_strtrim(groups[i]));
             }
-            free(groups);
+            cxFree(mp, groups);
         }
     }
     
     // add user
-    conf->users = ucx_list_append_a(mp, conf->users, entry);
+    CFG_KEYFILE_ADD(&conf->users_begin, &conf->users_end, entry);
     
     // free tokens
-    for(int i=0;i<tkn;i++) {
-        free(tk[i].ptr);
-    }
-    free(tk);
+    cxFree(mp, tk);
     
     return 0;
 }
--- a/src/server/config/keyfile.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/keyfile.h	Sun Nov 06 15:53:32 2022 +0100
@@ -36,23 +36,30 @@
 extern "C" {
 #endif
 
+#define CFG_KEYFILE_ADD(list_begin, list_end, elm) \
+    cx_linked_list_add((void**)list_begin, (void**)list_end, -1, offsetof(KeyfileEntry, next), elm)
+    
+typedef struct _keyfile_entry KeyfileEntry;
+    
 typedef struct _keyfile_conf {
     ConfigParser parser;
     char         *file;
-    UcxList      *users; // KeyfileEntry list
+    KeyfileEntry *users_begin; // KeyfileEntry list
+    KeyfileEntry *users_end;
 } KeyfileConfig;
 
-typedef struct _keyfile_entry {
-    sstr_t               name;
+struct _keyfile_entry {
+    cxmutstr             name;
     enum KeyfileHashType hashtype;
-    sstr_t               hashdata;
-    sstr_t               *groups;
+    cxmutstr             hashdata;
+    cxmutstr             *groups;
     size_t               numgroups;
-} KeyfileEntry;
+    KeyfileEntry         *next;
+};
 
 KeyfileConfig *load_keyfile_config(const char *file);
 void free_keyfile_config(KeyfileConfig *conf);
-int keyfile_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line);
+int keyfile_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line);
 
 #ifdef	__cplusplus
 }
--- a/src/server/config/mimeconf.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/mimeconf.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,13 +30,10 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <ucx/mempool.h>
+#include <cx/mempool.h>
 #include "mimeconf.h"
 
-typedef struct {
-    ucx_destructor destructor;
-    void           *ptr;
-} ucx_regdestr;
+#define MIMECONFIG_MAX_TOKENS 4096
 
 MimeConfig *load_mime_config(const char *file) {
     FILE *in = fopen(file, "r");
@@ -47,7 +44,8 @@
     
     MimeConfig *conf = malloc(sizeof(MimeConfig));
     conf->parser.parse = mimeconf_parse;
-    conf->directives = NULL;
+    conf->directives_begin = NULL;
+    conf->directives_end = NULL;
     conf->ntypes = 0;
     int r = cfg_parse_basic_file((ConfigParser*)conf, in);
     if(r != 0) {
@@ -64,38 +62,29 @@
 }
 
 void free_mime_config(MimeConfig *conf) {
-    ucx_mempool_destroy(conf->parser.mp->pool);
+    //ucx_mempool_destroy(conf->parser.mp->pool);
     free(conf);
 }
 
-int mimeconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) {
+int mimeconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) {
     MimeConfig *conf = p;
-    UcxAllocator *mp = conf->parser.mp;
+    CxAllocator *mp = conf->parser.mp;
     
     // parse mime directive
     MimeDirective *dir = OBJ_NEW_N(mp, MimeDirective);
     
-    UcxList *params = cfg_param_list(line, mp);
-    UCX_FOREACH(pl, params) {
-        ConfigParam *param = pl->data;
-        
-        if(!sstrcmp(param->name, sstr("type"))) {
+    ConfigParam *params = cfg_param_list(line, mp);
+    for(ConfigParam *param=params;param;param=param->next) { 
+        if(!cx_strcmp(cx_strcast(param->name), cx_str("type"))) {
             dir->type = param->value;
-        } else if(!sstrcmp(param->name, sstr("exts"))) {
+        } else if(!cx_strcmp(cx_strcast(param->name), cx_str("exts"))) {
             // comma-separated file extensions
-            
-            ssize_t nx = 0;
-            sstr_t *exts = sstrsplit(param->value, sstrn(",", 1), &nx);
-            for(int i=0;i<nx;i++) {
-                sstr_t extstr = sstrdup_a(mp, exts[i]);
-                dir->exts = ucx_list_append_a(mp, dir->exts, extstr.ptr);
-                free(exts[i].ptr);
-            }
-            free(exts);
+            dir->nextensions = cx_strsplit_a(mp, cx_strcast(param->value), cx_strn(",", 1), MIMECONFIG_MAX_TOKENS, &dir->extensions);
         }
     }
     
-    conf->directives = ucx_list_append_a(mp, conf->directives, dir);
+    CFG_MIME_ADD(&conf->directives_begin, &conf->directives_end, dir);
+    
     conf->ntypes++;
 
     return 0;
--- a/src/server/config/mimeconf.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/mimeconf.h	Sun Nov 06 15:53:32 2022 +0100
@@ -35,22 +35,32 @@
 extern "C" {
 #endif
 
+#define CFG_MIME_ADD(list_begin, list_end, directive) \
+    cx_linked_list_add((void**)list_begin, (void**)list_end, -1, offsetof(MimeDirective, next), directive)
+    
+typedef struct _mime_dir MimeDirective;
+    
 typedef struct _mime_conf {
-    ConfigParser parser;
-    UcxList      *directives; // MimeDirective list
-    int          ntypes;
+    ConfigParser  parser;
+    MimeDirective *directives_begin;
+    MimeDirective *directives_end;
+    int           ntypes;
 } MimeConfig;
 
-typedef struct _mime_dir {
-    sstr_t  type;
-    UcxList *exts; // char*
-} MimeDirective;
+struct _mime_dir {
+    cxmutstr  type;
+    //UcxList *exts; // char*
+    ssize_t  nextensions;
+    cxstring *extensions;
+    
+    MimeDirective *next;
+};
 
 MimeConfig *load_mime_config(const char *file);
 
 void free_mime_config(MimeConfig *conf);
 
-int mimeconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line);
+int mimeconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line);
 
 #ifdef	__cplusplus
 }
--- a/src/server/config/objconf.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/objconf.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,6 +30,8 @@
 
 #include <string.h>
 
+#include <cx/utils.h>
+
 /* dev notes:
  *
  * to free ObjectConfig, free:
@@ -47,9 +49,9 @@
     ObjectConfig *conf = malloc(sizeof(ObjectConfig));
     conf->parser.parse = objconf_parse;
     conf->file = file;
-    conf->conditions = NULL;
+    //conf->conditions = NULL;
     conf->levels = NULL;
-    conf->objects = NULL;
+    conf->objects = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr);
     //conf->lines = NULL;
 
     int r = cfg_parse_basic_file((ConfigParser*)conf, in);
@@ -70,13 +72,13 @@
     }
 
     // free mempool
-    ucx_mempool_destroy(conf->parser.mp->pool);
+    //ucx_mempool_destroy(conf->parser.mp->pool);
     free(conf);
 }
 
 
 
-int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line) {
+int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line) {
     ObjectConfig *conf = p;
 
     begin->type = cfg_get_line_type(line);
@@ -89,8 +91,8 @@
             }
             tag->begin = begin;
             tag->end = end;
-            tag->type_num = cfg_get_tag_type(tag->name);
-            //printf("line {%s}\n", sstrdub(ll).ptr);
+            tag->type_num = cfg_get_tag_type(cx_strcast(tag->name));
+            //printf("line {%s}\n", cx_strdub(ll).ptr);
             if(objconf_on_begin_tag(conf, tag) != 0) {
                 fprintf(stderr, "1error\n");
                 exit(-1);
@@ -98,7 +100,7 @@
             break;
         }
         case LINE_END_TAG: {
-            sstr_t tag = cfg_get_end_tag_name(line);
+            cxmutstr tag = cfg_get_end_tag_name(line);
             if(objconf_on_end_tag(conf, tag) != 0) {
                 fprintf(stderr, "2error\n");
                 exit(-1);
@@ -122,9 +124,9 @@
 }
 
 int objconf_on_begin_tag(ObjectConfig *conf, ConfigTag *tag) {
-    UcxAllocator *mp = conf->parser.mp;
+    CxAllocator *mp = conf->parser.mp;
     if(tag->type_num != TAG_OBJECT) {
-        ConfigParserLevel *l = conf->levels->data;
+        ConfigParserLevel *l = conf->levels;
         if(l->tag->type_num != TAG_OBJECT) {
             tag->parent = l->tag;
         }
@@ -137,31 +139,35 @@
             obj->begin = tag->begin;
             obj->end = tag->end;
             
-            obj->name = cfg_param_get(tag->param, sstr("name"));
-            obj->ppath = cfg_param_get(tag->param, sstr("ppath"));
+            obj->name = cfg_param_get(tag->param, cx_str("name"));
+            obj->ppath = cfg_param_get(tag->param, cx_str("ppath"));
 
             conf->obj = obj;
-            conf->objects = ucx_list_append_a(mp, conf->objects, obj);
+            //conf->objects = ucx_list_append_a(mp, conf->objects, obj);
+            cxListAdd(conf->objects, obj);
 
             // create tree level object
             ConfigParserLevel *lvl = OBJ_NEW(mp, ConfigParserLevel);
             lvl->iftag = NULL;
             lvl->levelnum = 1;
             lvl->tag = tag;
-            conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl);
+            lvl->next = NULL;
+            //conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl);
+            CFG_LEVEL_PREPEND(&conf->levels, lvl);
 
             break;
         }
         case TAG_IF: {
             // create tree level object
-            ConfigParserLevel *last_lvl = conf->levels->data;
+            ConfigParserLevel *last_lvl = conf->levels;
 
             ConfigParserLevel *lvl = OBJ_NEW(mp, ConfigParserLevel);
             
             lvl->iftag = NULL;
             lvl->levelnum = last_lvl->levelnum + 1;
             lvl->tag = tag;
-            conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl);
+            //conf->levels = ucx_list_prepend_a(mp, conf->levels, lvl);
+            CFG_LEVEL_PREPEND(&conf->levels, lvl);
             last_lvl->iftag = tag;
 
             break;
@@ -170,7 +176,7 @@
         }
         case TAG_ELSE: {
             // create tree level object
-            ConfigParserLevel *last_lvl = conf->levels->data;
+            ConfigParserLevel *last_lvl = conf->levels;
             tag->iftag = last_lvl->iftag;
 
             ConfigParserLevel *lvl = OBJ_NEW(
@@ -180,7 +186,8 @@
             lvl->iftag = last_lvl->tag;
             lvl->levelnum = last_lvl->levelnum + 1;
             lvl->tag = tag;
-            conf->levels = ucx_list_prepend(conf->levels, lvl);
+            //conf->levels = ucx_list_prepend(conf->levels, lvl);
+            CFG_LEVEL_PREPEND(&conf->levels, lvl);
 
             break;
         }
@@ -200,8 +207,8 @@
     return 0;
 }
 
-int objconf_on_end_tag(ObjectConfig *conf, sstr_t tagname) {
-    int type = cfg_get_tag_type(tagname);
+int objconf_on_end_tag(ObjectConfig *conf, cxmutstr tagname) {
+    int type = cfg_get_tag_type(cx_strcast(tagname));
     if(type == -1) {
         log_ereport(LOG_FAILURE, "objconf: unknown tag");
         return 1;
@@ -211,17 +218,20 @@
         }
 
         // remove level
+        /*
         conf->levels = ucx_list_remove_a(
                 conf->parser.mp,
                 conf->levels,
                 conf->levels);
+        */
+        conf->levels = conf->levels->next;
     }
     
     return 0;
 }
 
 int objconf_on_directive(ObjectConfig *conf, ConfigDirective *dir) {
-    ConfigParserLevel *lvl = conf->levels->data;
+    ConfigParserLevel *lvl = conf->levels;
 
     // check if we have a condition for the directive
     // if the level tag is not an object tag, use it as condition
@@ -230,10 +240,17 @@
     }
 
     // add directive to current object
+    /*
     conf->obj->directives[dir->type_num] = ucx_list_append_a(
             conf->parser.mp,
             conf->obj->directives[dir->type_num],
             dir);
+    */
+    
+    ConfigDirectiveList *dir_entry = cxMalloc(conf->parser.mp, sizeof(ConfigDirectiveList));
+    dir_entry->directive = dir;
+    dir_entry->next = NULL;
+    CFG_DIRECTIVES_ADD(&conf->obj->directives[dir->type_num], dir_entry);
 
     return 0;
 }
--- a/src/server/config/objconf.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/objconf.h	Sun Nov 06 15:53:32 2022 +0100
@@ -34,37 +34,42 @@
 #ifdef	__cplusplus
 extern "C" {
 #endif
-
+    
+#define CFG_LEVEL_PREPEND(list, level) \
+    cx_linked_list_prepend((void**)list, NULL, -1, offsetof(ConfigParserLevel, next), level)
+    
 typedef struct _conf_object {
     ConfigLine *begin;
     ConfigLine *end;
     
-    sstr_t     name;
-    sstr_t     ppath;
+    cxmutstr     name;
+    cxmutstr     ppath;
     // directives
-    UcxList    *directives[7];
+    ConfigDirectiveList *directives[7];
 } ConfigObject;
 
 /*
  * representing a xml like level in the obj.conf tree
  */
-typedef struct _conf_parser_level {
+typedef struct ConfigParserLevel ConfigParserLevel;
+struct ConfigParserLevel {
     ConfigTag  *iftag;   // last if tag
     ConfigTag  *tag;     // root of this level
     int        levelnum;
-} ConfigParserLevel;
+    ConfigParserLevel *next;
+};
 
 typedef struct _obj_conf {
     ConfigParser parser;
     char         *file;
     //UcxDlist     *lines;
-    UcxList      *conditions;
-    UcxList      *objects;
+    //UcxList      *conditions;
+    CxList      *objects;
 
     // private parser temp var
     ConfigObject *obj;     // add directives to this object
     // private parser temp var
-    UcxList      *levels;  // tree levels (stack)
+    ConfigParserLevel     *levels;  // tree levels (stack)
     
 } ObjectConfig;
 
@@ -72,11 +77,11 @@
 
 void free_object_config(ObjectConfig *conf);
 
-int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line);
+int objconf_parse(void *p, ConfigLine *begin, ConfigLine *end, cxmutstr line);
 
 int objconf_on_begin_tag(ObjectConfig *conf, ConfigTag *tag);
 
-int objconf_on_end_tag(ObjectConfig *conf, sstr_t tagname);
+int objconf_on_end_tag(ObjectConfig *conf, cxmutstr tagname);
 
 int objconf_on_directive(ObjectConfig *conf, ConfigDirective *dir);
 
--- a/src/server/config/serverconfig.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/serverconfig.c	Sun Nov 06 15:53:32 2022 +0100
@@ -28,14 +28,15 @@
 
 
 #include "serverconfig.h"
+#include "conf.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
 
-#include <ucx/buffer.h>
-#include <ucx/utils.h>
+#include <cx/buffer.h>
+#include <cx/utils.h>
 
 ServerConfig* serverconfig_load(const char *file) {
     FILE *in = fopen(file, "r");
@@ -43,23 +44,25 @@
         return NULL;
     }
     
-    UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
-    if(!buf) {
-        fclose(in);
-        return NULL;
+    CxBuffer buf;
+    cxBufferInit(&buf, NULL, 16384, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
+    
+    //ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write);
+    char readbuf[2048];
+    size_t r;
+    while((r = fread(readbuf, 1, 2048, in)) > 0) {
+        cxBufferWrite(readbuf, 1, r, &buf);
     }
-    
-    ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write);
     fclose(in);
     
-    ServerConfig *scfg = serverconfig_parse(scstrn(buf->space, buf->size));
+    ServerConfig *scfg = serverconfig_parse(cx_strn(buf.space, buf.size));
     
-    ucx_buffer_free(buf);
+    cxBufferDestroy(&buf);
     return scfg;
 }
 
 
-static CFGToken get_next_token(scstr_t content, int *pos) {
+static CFGToken get_next_token(cxstring content, int *pos) {
     CFGToken token = { {NULL, 0}, CFG_NO_TOKEN };
     CFGTokenType type = CFG_TOKEN;
     
@@ -130,7 +133,7 @@
     }
     
     token.type = type;
-    token.content = scstrsubsl(content, token_begin, token_end - token_begin);
+    token.content = cx_strsubsl(content, token_begin, token_end - token_begin);
     return token;
 }
 
@@ -156,16 +159,16 @@
 }
 */
 
-static void config_arg_set_value(UcxAllocator *a, ConfigArg *arg, CFGToken token) {
-    scstr_t nv = scstrchr(token.content, '=');
+static void config_arg_set_value(CxAllocator *a, ConfigParam *arg, CFGToken token) {
+    cxstring nv = cx_strchr(token.content, '=');
     if(!nv.ptr) {
-        arg->value = sstrdup_a(a, token.content);
+        arg->value = cx_strdup_a(a, token.content);
     } else {
         intptr_t eq = (intptr_t)(nv.ptr - token.content.ptr);
-        scstr_t name = token.content;
+        cxstring name = token.content;
         name.length = (size_t)eq;
         
-        scstr_t value = nv;
+        cxstring value = nv;
         value.ptr++;
         value.length--;
         if(value.length > 1 && value.ptr[0] == '"' && value.ptr[value.length-1] == '"') {
@@ -173,19 +176,28 @@
             value.length -= 2; // remove quote
         }
         
-        arg->name = sstrdup_a(a, name);
-        arg->value = sstrdup_a(a, value);
+        arg->name = cx_strdup_a(a, name);
+        arg->value = cx_strdup_a(a, value);
     }
 }
 
-ServerConfig* serverconfig_parse(scstr_t content) {
-    UcxMempool *mp = ucx_mempool_new(512);
+static int nodestack_prepend(CxAllocator *a, ConfigNodeStack **stack, ConfigNode *node) {
+    ConfigNodeStack *elm = cxMalloc(a, sizeof(ConfigNodeStack));
+    if(!elm) return 1;
+    elm->node = node;
+    elm->next = NULL;
+    cx_linked_list_prepend((void**)stack, NULL, -1, offsetof(ConfigNodeStack, next), elm);
+    return 0;
+}
+
+ServerConfig* serverconfig_parse(cxstring content) {
+    CxMempool *mp = cxBasicMempoolCreate(512);
     if(!mp) return NULL;
-    UcxAllocator *a = mp->allocator;
+    CxAllocator *a = (CxAllocator*)mp->allocator;
     
-    ServerConfig *config = ucx_mempool_malloc(mp, sizeof(ServerConfig));
+    ServerConfig *config = cxMalloc(a, sizeof(ServerConfig));
     if(!config) {
-        ucx_mempool_destroy(mp);
+        cxMempoolDestroy(mp);
         return NULL;
     }
     config->mp = mp;
@@ -199,12 +211,14 @@
     int pos = 0; // needed for tokenizer
     CFGToken token;
     
-    ConfigNode *root_obj = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
+    ConfigNode *root_obj = cxCalloc(a, 1, sizeof(ConfigNode));
     root_obj->type = CONFIG_NODE_OBJECT;
     
-    UcxList *node_stack = ucx_list_prepend(NULL, root_obj);
+    ConfigNodeStack *node_stack = cxMalloc(a, sizeof(ConfigNodeStack));
+    node_stack->node = root_obj;
+    node_stack->next = NULL;
     
-    ConfigNode *current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
+    ConfigNode *current = cxCalloc(a, 1, sizeof(ConfigNode));
     current->type = CONFIG_NODE_SPACE;
     ConfigNode *obj = NULL;
     int obj_closed = 0;
@@ -213,7 +227,7 @@
     int err = 0;
     while((token = get_next_token(content, &pos)).type != CFG_NO_TOKEN) {
         //printf("%s [%.*s]\n", token_type_str(token.type), (int)token.content.length, token.content.ptr);
-        
+
         switch(token.type) {
             case CFG_NO_TOKEN: break;
             case CFG_TOKEN_COMMENT: {
@@ -224,39 +238,56 @@
             }
             case CFG_TOKEN_SPACE: break;
             case CFG_TOKEN_NEWLINE: {
-                scstr_t line = scstrsubsl(content, text_start, pos - text_start);
+                cxstring line = cx_strsubsl(content, text_start, pos - text_start);
                 text_start = pos;
                 
-                sstr_t line_cp = sstrdup_a(a, line);
+                cxmutstr line_cp = cx_strdup_a(a, line);
 
-                ConfigNode *parent = node_stack->data;
+                ConfigNode *parent = node_stack->node;
                 if(current->type == CONFIG_NODE_CLOSE_OBJECT) {
+                    // this is a newline after a object is closed with '}'
+                    // the line containing "}\n" should be added to the object
                     parent->text_end = line_cp;
-                    node_stack = ucx_list_remove_a(a, node_stack, node_stack);
+                    // done with this object, remove it from the stack
+                    ConfigNodeStack *remove_item = node_stack;
+                    node_stack = node_stack->next;
+                    cxFree(a, remove_item);
                 } else if(current->type == CONFIG_NODE_OPEN_OBJECT) {
-                    sstr_t new_textbegin = sstrcat_a(a, 2, obj->text_begin, line_cp);
-                    alfree(a, obj->text_begin.ptr);
-                    alfree(a, line_cp.ptr);
+                    // newline after a object is opened with '{'
+                    // append '{' to the object text
+                    cxmutstr new_textbegin = cx_strcat_a(a, 2, obj->text_begin, line_cp);
+                    cxFree(a, obj->text_begin.ptr);
+                    cxFree(a, line_cp.ptr);
                     obj->text_begin = new_textbegin;
                 } else {
+                    // normal line containing a directive, space or comment
+                    // add it to parent node
                     current->text_begin = line_cp;
-                    ConfigNode *parent = node_stack->data;
-                    parent->children = ucx_list_append_a(a, parent->children, current);
+                    CFG_NODE_ADD(&parent->children_begin, &parent->children_end, current);
                 }
                 
+                // obj points to the previous node that started as a directive
+                // the type is set to CONFIG_NODE_OBECT if it was followed by
+                // a '{' character
                 if(obj && obj->type == CONFIG_NODE_OBJECT) {
-                    node_stack = ucx_list_prepend_a(a, node_stack, obj);
+                    // new object started, add it to the stack
+                    nodestack_prepend(a, &node_stack, obj);
                     obj = NULL;
                 }
 
-                current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
+                current = cxCalloc(a, 1, sizeof(ConfigNode));
                 current->type = CONFIG_NODE_SPACE;
                 
                 obj_closed = 0;
                 break;
             }
             case CFG_TOKEN: {
-                if(!sstrcmp(token.content, S("{"))) {
+                // normal text token
+                // either a directive/obj name, parameter or { }
+                
+                if(!cx_strcmp(token.content, cx_str("{"))) {
+                    // obj is pointing to the previous node that started
+                    // a directive
                     if(!obj) {
                         err = 1;
                         break;
@@ -265,7 +296,7 @@
                     if(current != obj) {
                         current->type = CONFIG_NODE_OPEN_OBJECT;
                     }
-                } else if(!sstrcmp(token.content, S("}"))) {
+                } else if(!cx_strcmp(token.content, cx_str("}"))) {
                     obj_closed = 1; // force newline before next directive
                     obj = NULL;
                     current->type = CONFIG_NODE_CLOSE_OBJECT;
@@ -276,13 +307,16 @@
                     }
                     
                     if(!current->name.ptr) {
-                        current->name = sstrdup_a(a, token.content);
+                        // currently this could be a directive or object
+                        current->name = cx_strdup_a(a, token.content);
                         current->type = CONFIG_NODE_DIRECTIVE;
-                        obj = current;
+                        obj = current; // potential object
                     } else {
-                        ConfigArg *arg = ucx_mempool_calloc(mp, 1, sizeof(ConfigArg));
+                        // name already set, therefore this token must
+                        // be a parameter
+                        ConfigParam *arg = cxCalloc(a, 1, sizeof(ConfigParam));
                         config_arg_set_value(a, arg, token);
-                        current->args = ucx_list_append_a(a, current->args, arg);
+                        CFG_PARAM_ADD(&current->args, NULL, arg);
                     }
                 }
                 break;
@@ -296,54 +330,52 @@
     
     if(pos < content.length || err) {
         // content not fully parsed because of an error
-        ucx_mempool_destroy(mp);
+        cxMempoolDestroy(mp);
         return NULL;
     }
     
     //test_print_config(&root_obj);
     config->root = root_obj;
-    config->tab = sstrdup_a(a, SC("\t"));
+    config->tab = cx_strdup_a(a, cx_str("\t"));
     
     return config;
 }
 
 void serverconfig_free(ServerConfig *cfg) {
-    ucx_mempool_destroy(cfg->mp);
+    cxMempoolDestroy(cfg->mp);
 }
 
-ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, scstr_t name) {
-    UCX_FOREACH(elm, parent->children) {
-        ConfigNode *node = elm->data;
-        if(node->type == type && !sstrcasecmp(node->name, name)) {
+ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, cxstring name) {
+    for(ConfigNode *node=parent->children_begin;node;node=node->next) {
+        if(node->type == type && !cx_strcasecmp(cx_strcast(node->name), name)) {
             return node;
         }
     }
     return NULL;
 }
 
-UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name) {
-    UcxList *nodes = NULL;
+CxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, cxstring name) {
+    CxList *nodes = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr);
     
-    UCX_FOREACH(elm, parent->children) {
-        ConfigNode *node = elm->data;
-        if(node->type == type && !sstrcasecmp(node->name, name)) {
-            nodes = ucx_list_append(nodes, node);
+    for(ConfigNode *node=parent->children_begin;node;node=node->next) {
+        if(node->type == type && !cx_strcasecmp(cx_strcast(node->name), name)) {
+            cxListAdd(nodes, node);
         }
     }
     
     return nodes;
 }
 
-scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t name) {
+cxstring serverconfig_directive_value(ConfigNode *obj, cxstring name) {
     ConfigNode *node = serverconfig_get_node(obj, CONFIG_NODE_DIRECTIVE, name);
-    if(node && ucx_list_size(node->args) == 1) {
-        ConfigArg *arg = node->args->data;
-        return SCSTR(arg->value);
+    if(node && CFG_NUM_PARAMS(node->args) == 1) {
+        ConfigParam *arg = node->args;
+        return (cxstring){ arg->value.ptr, arg->value.length };
     }
-    return scstrn(NULL, 0);
+    return (cxstring){ NULL, 0 };
 }
 
-sstr_t serverconfig_arg_name_value(UcxAllocator *a, scstr_t str, scstr_t *name) {
+cxmutstr serverconfig_arg_name_value(CxAllocator *a, cxstring str, cxstring *name) {
     int valstart = 0;
     for(int i=0;i<str.length;i++) {
         if(str.ptr[i] == '=') {
@@ -356,6 +388,6 @@
         }
     }
     
-    sstr_t ret;
+    cxmutstr ret;
     return ret;
 }
--- a/src/server/config/serverconfig.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/config/serverconfig.h	Sun Nov 06 15:53:32 2022 +0100
@@ -29,27 +29,31 @@
 #ifndef WS_CONFIG_SERVERCONFIG_H
 #define WS_CONFIG_SERVERCONFIG_H
 
-#include <ucx/list.h>
-#include <ucx/map.h>
-#include <ucx/mempool.h>
+#include <cx/linked_list.h>
+#include <cx/hash_map.h>
+#include <cx/mempool.h>
+#include <cx/string.h>
+
+#include "conf.h"
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-
+#define CFG_NODE_ADD(list_begin, list_end, elm) \
+    cx_linked_list_add((void**)list_begin, (void**)list_end, offsetof(ConfigNode, prev), offsetof(ConfigNode, next), elm)
+    
 typedef struct ServerConfig    ServerConfig;
 typedef struct ConfigNode      ConfigNode;
-typedef struct ConfigArg       ConfigArg;
 typedef struct CFGToken        CFGToken;
 
 typedef enum ConfigNodeType    ConfigNodeType;
 typedef enum CFGTokenType      CFGTokenType;
 
 struct ServerConfig {
-    UcxMempool *mp;
+    CxMempool  *mp;
     ConfigNode *root;
-    sstr_t tab;
+    cxmutstr   tab;
 };
 
 enum ConfigNodeType {
@@ -62,21 +66,25 @@
 };
 
 struct ConfigNode {
-    sstr_t text_begin;
-    sstr_t text_end; 
+    cxmutstr text_begin;
+    cxmutstr text_end; 
     ConfigNodeType type;
     
-    sstr_t name;
-    UcxList *args;
-    UcxList *children;
+    cxmutstr name;
+    ConfigParam *args;
+    ConfigNode *children_begin;
+    ConfigNode *children_end;
+    
+    ConfigNode *prev;
+    ConfigNode *next;
 };
 
-struct ConfigArg {
-    sstr_t name;
-    sstr_t value;
+typedef struct ConfigNodeStack ConfigNodeStack;
+struct ConfigNodeStack {
+    ConfigNode      *node;
+    ConfigNodeStack *next;
 };
 
-
 enum CFGTokenType {
     CFG_NO_TOKEN = 0,
     CFG_TOKEN_COMMENT,
@@ -86,23 +94,23 @@
 };
 
 struct CFGToken {
-    scstr_t content;
+    cxstring content;
     CFGTokenType type;
 };
 
 ServerConfig* serverconfig_load(const char *file);
 
-ServerConfig* serverconfig_parse(scstr_t content);
+ServerConfig* serverconfig_parse(cxstring content);
 
 void serverconfig_free(ServerConfig *cfg);
 
-ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, scstr_t name);
+ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, cxstring name);
 
-UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name);
+CxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, cxstring name);
 
-scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t name);
+cxstring serverconfig_directive_value(ConfigNode *obj, cxstring name);
 
-sstr_t serverconfig_arg_name_value(UcxAllocator *a, scstr_t str, scstr_t *name);
+cxmutstr serverconfig_arg_name_value(CxAllocator *a, cxstring str, cxstring *name);
 
 #ifdef __cplusplus
 }
--- a/src/server/daemon/acl.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/acl.c	Sun Nov 06 15:53:32 2022 +0100
@@ -318,7 +318,7 @@
         gid_t owninggroup);
 
 int fs_acl_check(SysACL *acl, User *user, const char *path, uint32_t access_mask) {
-    sstr_t p;
+    cxmutstr p;
     if(path[0] != '/') {
         size_t n = 128;
         char *cwd = malloc(n);
@@ -331,12 +331,12 @@
                 return 0;
             }
         }
-        sstr_t wd = sstr(cwd);
-        sstr_t pp = sstr((char*)path);
+        cxmutstr wd = cx_str(cwd);
+        cxmutstr pp = cx_str((char*)path);
 
-        p = sstrcat(3, wd, sstrn("/", 1), pp);
+        p = cx_strcat(3, wd, cx_strn("/", 1), pp);
     } else {
-        p = sstrdup(sstr((char*)path));
+        p = cx_strdup(cx_str((char*)path));
     }
     if(p.ptr[p.length-1] == '/') {
         p.ptr[p.length-1] = 0;
--- a/src/server/daemon/acldata.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/acldata.c	Sun Nov 06 15:53:32 2022 +0100
@@ -32,13 +32,13 @@
 
 #include "acldata.h"
 
-ACLData* acl_data_new(UcxAllocator *a) {
-    ACLData *dat = almalloc(a, sizeof(ACLData));
+ACLData* acl_data_new(CxAllocator *a) {
+    ACLData *dat = cxMalloc(a, sizeof(ACLData));
     if(!dat) {
         return NULL;
     }
     
-    dat->namedACLs = ucx_map_new_a(a, 16);
+    dat->namedACLs = cxHashMapCreate(a, 16);
     if(!dat->namedACLs) {
         return NULL;
     }
@@ -46,7 +46,7 @@
     return dat;
 }
 
-ACLList* acl_get(ACLData *acldata, char *name) {
-    ACLList *acl = ucx_map_cstr_get(acldata->namedACLs, name);
+ACLList* acl_get(ACLData *acldata, const char *name) {
+    ACLList *acl = cxMapGet(acldata->namedACLs, cx_hash_key_str(name));
     return acl;
 }
--- a/src/server/daemon/acldata.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/acldata.h	Sun Nov 06 15:53:32 2022 +0100
@@ -29,8 +29,8 @@
 #ifndef ACLCONF_H
 #define	ACLCONF_H
 
-#include <ucx/list.h>
-#include <ucx/map.h>
+#include <cx/list.h>
+#include <cx/map.h>
 
 #include "acl.h"
 #include "../config/acl.h"
@@ -40,12 +40,12 @@
 #endif
     
 typedef struct acl_data {
-    UcxMap  *namedACLs;
+    CxMap  *namedACLs;
 } ACLData;
 
 ACLData* acl_data_new();
 
-ACLList* acl_get(ACLData *acldata, char *name);
+ACLList* acl_get(ACLData *acldata, const char *name);
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/auth.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/auth.c	Sun Nov 06 15:53:32 2022 +0100
@@ -31,7 +31,7 @@
 #include <string.h>
 #include <pthread.h>
 
-#include <ucx/map.h>
+#include <cx/map.h>
 
 #include "../public/nsapi.h"
 #include "../util/atomic.h"
@@ -52,7 +52,7 @@
     cache.trail = NULL;
 }
 
-User* auth_cache_get(char *authdb, char *user) {
+User* auth_cache_get(char *authdb, const char *user) {
     //printf("auth_cache_get: %s\n", user);
     /*
      * create the key to access the map
@@ -62,12 +62,12 @@
     size_t userlen = strlen(user);
     
     size_t keylen = authdblen + userlen + 1;
-    char *key = malloc(keylen);
+    unsigned char *key = malloc(keylen);
     memcpy(key, authdb, authdblen);
     key[authdblen] = 0;
     memcpy(key + authdblen + 1, user, userlen);
     
-    UcxKey mapkey = ucx_key(key, keylen);
+    CxHashKey mapkey = cx_hash_key_bytes(key, keylen);
     
     // get cached user from map
     time_t now = time(NULL);
@@ -84,7 +84,7 @@
     if(elm) {
         // compare the key data to be sure it is the correct user
         int n = (mapkey.len > elm->key.len) ? elm->key.len : mapkey.len;
-        if (!memcmp(elm->key.data, mapkey.data, n)) {
+        if (!memcmp(elm->key.data.cbytes, mapkey.data.cbytes, n)) {
             // elm is now the correct UserCacheElm
             // TODO: use configuration for expire time
             if(now - elm->created > 120) {
@@ -119,8 +119,8 @@
 void auth_cache_add(
         char *authdb,
         User *user,
-        char *password,
-        char **groups,
+        const char *password,
+        const char **groups,
         size_t numgroups)
 {
     //printf("auth_cache_add: %s\n", user->name);
@@ -140,10 +140,10 @@
     
     cusr->authdb = strdup(authdb);
     cusr->password = strdup(password);
-    cusr->groups = numgroups ? calloc(numgroups, sizeof(sstr_t)) : NULL;
+    cusr->groups = numgroups ? calloc(numgroups, sizeof(cxmutstr)) : NULL;
     cusr->numgroups = numgroups;
     for(int i=0;i<numgroups;i++) {
-        cusr->groups[i] = sstrdup(sstr(groups[i]));
+        cusr->groups[i] = cx_strdup(cx_str(groups[i]));
     }
     cusr->ref = 1;
     
@@ -164,13 +164,13 @@
     size_t authdblen = strlen(authdb);
     size_t userlen = strlen(user->name);
     size_t keylen = authdblen + userlen + 1;
-    char *key = malloc(keylen);
+    unsigned char *key = malloc(keylen);
     memcpy(key, authdb, authdblen);
     key[authdblen] = 0;
     memcpy(key + authdblen + 1, user->name, userlen);
-    UcxKey mapkey = ucx_key(key, keylen);
+    CxHashKey mapkey = cx_hash_key_bytes(key, keylen);
     
-    elm->key.data = key;
+    elm->key.data.bytes = key;
     elm->key.len = mapkey.len;
     elm->key.hash = mapkey.hash;
     elm->slot = mapkey.hash%cache.size;
@@ -237,14 +237,14 @@
         cache.map[elm->slot] = elm->next_elm;
     }
     
-    free(elm->key.data);
+    free(elm->key.data.bytes);
     cached_user_unref(elm->user);
     free(elm);
     
     cache.count--;
 }
 
-int cached_user_verify_password(CachedUser *user, char *password) {
+int cached_user_verify_password(CachedUser *user, const char *password) {
     if(!strcmp(user->password, password)) {
         return 1;
     } else {
@@ -252,10 +252,10 @@
     }
 }
 
-int cached_user_check_group(CachedUser *user, char *group) {
-    sstr_t grp = sstr(group);
+int cached_user_check_group(CachedUser *user, const char *group) {
+    cxstring grp = cx_str(group);
     for(int i=0;i<user->numgroups;i++) {
-        if(!sstrcmp(user->groups[i], grp)) {
+        if(!cx_strcmp(cx_strcast(user->groups[i]), grp)) {
             return 1;
         }
     }
@@ -283,7 +283,7 @@
  * from public/auth.h
  */
 
-User* authdb_get_user(AuthDB *db, char *user) {
+User* authdb_get_user(AuthDB *db, const char *user) {
     if(db->use_cache) {
         User *u = auth_cache_get(db->name, user);
         if(u) {
@@ -293,7 +293,7 @@
     return db->get_user(db, user);
 }
 
-User* authdb_get_and_verify(AuthDB *db, char *user, char *password, int *pw) {
+User* authdb_get_and_verify(AuthDB *db, const char *user, const char *password, int *pw) {
     User *u = NULL;
     // try getting the user from the cache
     if(db->use_cache) {
--- a/src/server/daemon/auth.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/auth.h	Sun Nov 06 15:53:32 2022 +0100
@@ -33,7 +33,8 @@
 #include <inttypes.h>
 #include "../public/auth.h"
 
-#include <ucx/map.h>
+#include <cx/map.h>
+#include <cx/string.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -42,7 +43,7 @@
 typedef struct {
     User     user;
     char     *authdb;
-    sstr_t   *groups;
+    cxmutstr *groups;
     size_t   numgroups;
     char     *password;
     uint32_t ref;
@@ -52,7 +53,7 @@
 struct user_cache_elm {
     CachedUser       *user;
     UserCacheElm     *next_user; // next elm in the cached user list
-    struct UcxMapKey key;        // key to access this element
+    CxHashKey        key;        // key to access this element
     size_t           slot;       // slot in the map
     UserCacheElm     *next_elm;  // next element in this map slot
     time_t           created;
@@ -69,18 +70,18 @@
 
 void auth_cache_init();
 
-User* auth_cache_get(char *authdb, char *user);
+User* auth_cache_get(char *authdb, const char *user);
 void auth_cache_add(
         char *authdb,
         User *user,
-        char *password,
-        char **groups,
+        const char *password,
+        const char **groups,
         size_t numgroups);
 
 void auth_cache_remove_from_map(UserCacheElm *elm);
 
-int cached_user_verify_password(CachedUser *user, char *password);
-int cached_user_check_group(CachedUser *user, char *group);
+int cached_user_verify_password(CachedUser *user, const char *password);
+int cached_user_check_group(CachedUser *user, const char *group);
 void cached_user_unref(CachedUser *user);
 void cached_user_delete(CachedUser *user);
 
--- a/src/server/daemon/config.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/config.c	Sun Nov 06 15:53:32 2022 +0100
@@ -37,8 +37,11 @@
 #include <sys/stat.h>
 #include <sys/mman.h>
 
-#include <ucx/string.h>
-#include <ucx/utils.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/hash_map.h>
+#include <cx/linked_list.h>
+#include <cx/compare.h>
 
 #include "httplistener.h"
 #include "config.h"
@@ -54,13 +57,13 @@
 #include "../util/pblock.h"
 #include "../util/util.h"
 #include "../util/atomic.h"
-#include "ucx/buffer.h"
+#include "cx/buffer.h"
 
 pool_handle_t *init_pool;
 
 char* cfg_config_file_path(const char *file) {
-    sstr_t base = ST("config/");
-    sstr_t path = sstrcat(2, base, scstr(file));
+    cxstring base = CX_STR("config/");
+    cxmutstr path = cx_strcat(2, base, cx_str(file));
     return path.ptr;
 }
 
@@ -72,24 +75,23 @@
         log_ereport(LOG_FAILURE, "Cannot load init.conf");
         return 1;
     }
-    UcxAllocator *mp = cfg->parser.mp;
+    CxAllocator *mp = cfg->parser.mp;
 
     init_pool = pool_create(); // one pool for one Configuration
-    UcxList *dirs = cfg->directives;
+    ConfigDirectiveList *dirs = cfg->directives;
     while(dirs != NULL) {
-        ConfigDirective *dir = dirs->data;
+        ConfigDirective *dir = dirs->directive;
 
         /* create NSAPI directive */
         directive *d = malloc(sizeof(directive));
         d->param = pblock_create_pool(init_pool, 8);
-        UcxList *param = cfg_param_list(dir->value, mp);
+        ConfigParam *param = cfg_param_list(dir->value, mp);
         while(param != NULL) {
-            ConfigParam *p = param->data;
             pblock_nvlinsert(
-                    p->name.ptr,
-                    p->name.length,
-                    p->value.ptr,
-                    p->value.length,
+                    param->name.ptr,
+                    param->name.length,
+                    param->value.ptr,
+                    param->value.length,
                     d->param);
             
             param = param->next;
@@ -148,15 +150,15 @@
     serverconfig->ref = 1;
     serverconfig->pool = pool;
     
-    UcxAllocator allocator = util_pool_allocator(serverconfig->pool);
-    serverconfig->a = pool_malloc(pool, sizeof(UcxAllocator));
-    *serverconfig->a = allocator;
+    CxAllocator *allocator = pool_allocator(serverconfig->pool);
+    serverconfig->a = allocator;
     
-    serverconfig->listeners = NULL;
-    serverconfig->host_vs = ucx_map_new_a(serverconfig->a, 16);
-    serverconfig->authdbs = ucx_map_new_a(serverconfig->a, 16);
-    serverconfig->resources = ucx_map_new_a(serverconfig->a, 16);
-    serverconfig->dav = ucx_map_new_a(serverconfig->a, 16);
+    serverconfig->listeners = cxPointerLinkedListCreate(serverconfig->a, cx_cmp_ptr);
+    serverconfig->logfiles = cxPointerLinkedListCreate(serverconfig->a, cx_cmp_ptr);
+    serverconfig->host_vs = cxHashMapCreate(serverconfig->a, 16);
+    serverconfig->authdbs = cxHashMapCreate(serverconfig->a, 16);
+    serverconfig->resources = cxHashMapCreate(serverconfig->a, 16);
+    serverconfig->dav = cxHashMapCreate(serverconfig->a, 16);
     
     // STAGE 1 load_server_conf:
     // At stage 1 we load the file and get the Runtime infos for changing
@@ -168,25 +170,26 @@
     // Listener (dependencies: Threadpool, EventHandler)
     
     // load Runtime config
-    UcxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Runtime"));
-    UCX_FOREACH(elm, list) {
-        ConfigNode *runtimeobj = elm->data;
+    CxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Runtime"));
+    CxIterator iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, runtimeobj, iter) {
         if(cfg_handle_runtime(serverconfig, runtimeobj)) {
             // error
             return NULL;
         }
     }
-    ucx_list_free(list);
+    cxListDestroy(list);
     
     // load threadpool config
     log_ereport(LOG_DEBUG, "apply config: Threadpool");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Threadpool"));
-    UCX_FOREACH(elm, list) {
-        if(cfg_handle_threadpool(serverconfig, elm->data)) {
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Threadpool"));
+    iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, elm, iter) {
+        if(cfg_handle_threadpool(serverconfig, elm)) {
             return NULL;
         }
     }
-    ucx_list_free(list);
+    cxListDestroy(list);
     // check thread pool config
     if(check_thread_pool_cfg() != 0) {
         /* critical error */
@@ -195,9 +198,10 @@
     
     // load eventhandler config
     log_ereport(LOG_DEBUG, "apply config: EventHandler");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("EventHandler"));
-    UCX_FOREACH(elm, list) {
-        if(cfg_handle_eventhandler(serverconfig, elm->data)) {
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("EventHandler"));
+    iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, elm, iter) {
+        if(cfg_handle_eventhandler(serverconfig, elm)) {
             // error            
             return NULL;
         }
@@ -207,18 +211,18 @@
         /* critical error */
         return NULL;
     }
-    ucx_list_free(list);
+    cxListDestroy(list);
     
     // load Listener config
     log_ereport(LOG_DEBUG, "apply config: Listener");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Listener"));
-    UCX_FOREACH(elm, list) {
-        ConfigNode *scfgobj = elm->data;
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Listener"));
+    iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, scfgobj, iter) {
         if(cfg_handle_listener(serverconfig, scfgobj)) {
             return NULL;
         }
     }
-    ucx_list_free(list);
+    cxListDestroy(list);
     
     // we return here, to let the webserver use the runtime info to
     // change the uid if needed
@@ -242,95 +246,92 @@
      */
     
     // init logfile first
-    UcxList *list;
+    CxList *list;
     
     log_ereport(LOG_DEBUG, "apply config: LogFile");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("LogFile"));
-    if(list) {
-        ConfigNode *logobj = list->data;
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("LogFile"));
+    CxIterator iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, logobj, iter) {
         if(!logobj) {
             // error
-            return NULL; // TODO: fix memory leak
+            cxListDestroy(list);
+            return NULL;
         }
         
         int ret = cfg_handle_logfile(serverconfig, logobj);
         if(ret != 0) {
             // cannot initialize log file
-            return NULL; // TODO: fix memory leak
+            cxListDestroy(list);
+            return NULL;
         }
-    } else {
-        // horrible error
-        return NULL;
     }
-    ucx_list_free(list);
+    cxListDestroy(list);
     
     log_ereport(LOG_DEBUG, "apply config: AccessLog");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("AccessLog"));
-    UCX_FOREACH(elm, list) {
-        ConfigNode *scfgobj = elm->data;
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("AccessLog"));
+    iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, scfgobj, iter) {
         if(cfg_handle_accesslog(serverconfig, scfgobj)) {
             return NULL;
         }
     }
-    ucx_list_free(list);
+    cxListDestroy(list);
     
     log_ereport(LOG_DEBUG, "apply config: AuthDB");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("AuthDB"));
-    UCX_FOREACH(elm, list) {
-        ConfigNode *scfgobj = elm->data;
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("AuthDB"));
+    iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, scfgobj, iter) {
         if(cfg_handle_authdb(serverconfig, scfgobj)) {
             return NULL;
         }
     }
-    ucx_list_free(list);
+    cxListDestroy(list);
     
     log_ereport(LOG_DEBUG, "apply config: VirtualServer");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("VirtualServer"));
-    UCX_FOREACH(elm, list) {
-        ConfigNode *scfgobj = elm->data;
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("VirtualServer"));
+    iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, scfgobj, iter) {
         if(cfg_handle_vs(serverconfig, scfgobj)) {
             return NULL;
         }
     }
-    ucx_list_free(list);
+    cxListDestroy(list);
     
     log_ereport(LOG_DEBUG, "apply config: ResourcePool");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("ResourcePool"));
-    UCX_FOREACH(elm, list) {
-        ConfigNode *scfgobj = elm->data;
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("ResourcePool"));
+    iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, scfgobj, iter) {
         if(cfg_handle_resourcepool(serverconfig, scfgobj)) {
             return NULL;
         }
     }
+    cxListDestroy(list);
     
     log_ereport(LOG_DEBUG, "apply config: Dav");
-    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Dav"));
-    UCX_FOREACH(elm, list) {
-        ConfigNode *scfgobj = elm->data;
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Dav"));
+    iter = cxListIterator(list, 0);
+    cx_foreach(ConfigNode *, scfgobj, iter) {
         if(cfg_handle_dav(serverconfig, scfgobj)) {
             return NULL;
         }
     }
+    cxListDestroy(list);
 
     // set VirtualServer for all listeners
-    UcxList *ls = serverconfig->listeners;
-    while(ls) {
-        HttpListener *listener = ls->data;
-
-        sstr_t vsname = sstr(listener->default_vs.vs_name);
+    CxList *ls = serverconfig->listeners;
+    iter = cxListIterator(ls, 0);
+    cx_foreach(HttpListener *, listener, iter) {
+        cxstring vsname = cx_str(listener->default_vs.vs_name);
 
         // search for VirtualServer
         //int b = 0;
-        UcxMapIterator iter = ucx_map_iterator(serverconfig->host_vs);
-        VirtualServer *vs;
-        UCX_MAP_FOREACH(key, vs, iter) {
-            if(!sstrcmp(vsname, vs->name)) {
+        CxIterator map_iter = cxMapIteratorValues(serverconfig->host_vs);
+        cx_foreach(VirtualServer *, vs, map_iter) {
+            if(!cx_strcmp(vsname, (cxstring){vs->name.ptr, vs->name.length})) {
                 listener->default_vs.vs = vs;
                 break;
             }
         }
-
-        ls = ls->next;
     }
     
     serverconfig_free(serverconf);
@@ -355,13 +356,13 @@
 }
 
 int cfg_handle_runtime(ServerConfiguration *cfg, ConfigNode *obj) { 
-    scstr_t user = serverconfig_directive_value(obj, SC("User"));
+    cxstring user = serverconfig_directive_value(obj, cx_str("User"));
     if(user.ptr) {
-        cfg->user = sstrdup_a(cfg->a, user);
+        cfg->user = cx_strdup_a(cfg->a, user);
     }
-    scstr_t tmp = serverconfig_directive_value(obj, SC("Temp"));
+    cxstring tmp = serverconfig_directive_value(obj, cx_str("Temp"));
     if(tmp.ptr) {
-        cfg->tmp = sstrdup_a(cfg->a, tmp);
+        cfg->tmp = cx_strdup_a(cfg->a, tmp);
     } else {
         // TODO: do this check after all config loading is done
         log_ereport(LOG_MISCONFIG, "no temporary directory specified");
@@ -369,9 +370,9 @@
     }
     
     // mime file
-    scstr_t mf = serverconfig_directive_value(obj, SC("MimeFile"));  
-    scstr_t base = SC("config/"); 
-    sstr_t file = sstrcat(2, base, mf);
+    cxstring mf = serverconfig_directive_value(obj, cx_str("MimeFile"));  
+    cxstring base = cx_str("config/"); 
+    cxmutstr file = cx_strcat(2, base, mf);
     
     if(mime_conf_load(cfg, file)) {
         return -1;
@@ -382,8 +383,8 @@
 }
 
 int cfg_handle_logfile(ServerConfiguration *cfg, ConfigNode *obj) {
-    scstr_t file = serverconfig_directive_value(obj, SC("File"));
-    scstr_t lvl = serverconfig_directive_value(obj, SC("Level"));
+    cxstring file = serverconfig_directive_value(obj, cx_str("File"));
+    cxstring lvl = serverconfig_directive_value(obj, cx_str("Level"));
     
     int err = 0;
     if(file.ptr == NULL) {
@@ -418,11 +419,11 @@
     poolcfg.queue_size = 64;
     poolcfg.stack_size = 262144;
     
-    scstr_t name  = serverconfig_directive_value(obj, SC("Name"));
-    scstr_t min   = serverconfig_directive_value(obj, SC("MinThreads"));
-    scstr_t max   = serverconfig_directive_value(obj, SC("MaxThreads"));
-    scstr_t stack = serverconfig_directive_value(obj, SC("StackSize"));
-    scstr_t queue = serverconfig_directive_value(obj, SC("QueueSize"));
+    cxstring name  = serverconfig_directive_value(obj, cx_str("Name"));
+    cxstring min   = serverconfig_directive_value(obj, cx_str("MinThreads"));
+    cxstring max   = serverconfig_directive_value(obj, cx_str("MaxThreads"));
+    cxstring stack = serverconfig_directive_value(obj, cx_str("StackSize"));
+    cxstring queue = serverconfig_directive_value(obj, cx_str("QueueSize"));
     // TODO: Type
     
     if(name.length == 0) {
@@ -477,9 +478,9 @@
 int cfg_handle_eventhandler(ServerConfiguration *c, ConfigNode *obj) {
     EventHandlerConfig evcfg;
     
-    scstr_t name      = serverconfig_directive_value(obj, SC("Name"));
-    scstr_t threads   = serverconfig_directive_value(obj, SC("Threads"));
-    scstr_t isdefault = serverconfig_directive_value(obj, SC("Default"));
+    cxstring name      = serverconfig_directive_value(obj, cx_str("Name"));
+    cxstring threads   = serverconfig_directive_value(obj, cx_str("Threads"));
+    cxstring isdefault = serverconfig_directive_value(obj, cx_str("Default"));
     
     evcfg.name = name;
     
@@ -501,8 +502,8 @@
 }
 
 int cfg_handle_resourcepool(ServerConfiguration *cfg, ConfigNode *obj) {
-    scstr_t name = serverconfig_directive_value(obj, SC("Name"));
-    scstr_t type = serverconfig_directive_value(obj, SC("Type"));
+    cxstring name = serverconfig_directive_value(obj, cx_str("Name"));
+    cxstring type = serverconfig_directive_value(obj, cx_str("Type"));
     
     int ret = 0;
     if(resourcepool_new(cfg, type, name, obj)) {
@@ -515,11 +516,11 @@
 int cfg_handle_accesslog(ServerConfiguration *cfg, ConfigNode *obj) {
     // TODO: use a name to identify the log file
     
-    scstr_t file = serverconfig_directive_value(obj, SC("File"));
+    cxstring file = serverconfig_directive_value(obj, cx_str("File"));
     if(file.ptr == NULL) {
         return 0;
     }
-    sstr_t format;
+    cxmutstr format;
     format.ptr = NULL;
     format.length = 0;
     
@@ -530,10 +531,10 @@
         return 0;
     }
     AccessLog *log = pool_malloc(cfg->pool, sizeof(AccessLog));
-    log->file = sstrdup_a(cfg->a, file);
+    log->file = cx_strdup_a(cfg->a, file);
     log->format = format;
     log->log = log_file;
-    cfg->logfiles = ucx_list_append_a(cfg->a, cfg->logfiles, log);
+    cxListAdd(cfg->logfiles, log);
     
     if(!cfg->default_log) {
         cfg->default_log = log;
@@ -543,30 +544,30 @@
 }
 
 int cfg_handle_authdb(ServerConfiguration *cfg, ConfigNode *obj) {
-    scstr_t name = serverconfig_directive_value(obj, SC("Name"));
-    scstr_t type = serverconfig_directive_value(obj, SC("Type"));
+    cxstring name = serverconfig_directive_value(obj, cx_str("Name"));
+    cxstring type = serverconfig_directive_value(obj, cx_str("Type"));
     
     AuthDB *authdb = NULL;
     
-    if(!sstrcmp(type, sstr("ldap"))) {
+    if(!cx_strcmp(type, cx_str("ldap"))) {
         LDAPConfig conf;
         
-        scstr_t host = serverconfig_directive_value(obj, SC("Host"));
-        scstr_t port = serverconfig_directive_value( obj, SC("Port"));
-        scstr_t basedn = serverconfig_directive_value(obj, SC("BaseDN"));
-        scstr_t binddn = serverconfig_directive_value(obj, SC("BindDN"));
-        scstr_t basepw = serverconfig_directive_value(obj, SC("BindPW"));
+        cxstring host = serverconfig_directive_value(obj, cx_str("Host"));
+        cxstring port = serverconfig_directive_value( obj, cx_str("Port"));
+        cxstring basedn = serverconfig_directive_value(obj, cx_str("BaseDN"));
+        cxstring binddn = serverconfig_directive_value(obj, cx_str("BindDN"));
+        cxstring basepw = serverconfig_directive_value(obj, cx_str("BindPW"));
         
-        conf.hostname = sstrdup_a(cfg->a, host).ptr;
+        conf.hostname = cx_strdup_a(cfg->a, host).ptr;
         conf.port = atoi(port.ptr);
-        conf.basedn = sstrdup_a(cfg->a, basedn).ptr;
-        conf.binddn = sstrdup_a(cfg->a, binddn).ptr;
-        conf.bindpw = sstrdup_a(cfg->a, basepw).ptr;
+        conf.basedn = cx_strdup_a(cfg->a, basedn).ptr;
+        conf.binddn = cx_strdup_a(cfg->a, binddn).ptr;
+        conf.bindpw = cx_strdup_a(cfg->a, basepw).ptr;
         
         authdb = create_ldap_authdb(cfg, name.ptr, &conf);      
-    } else if(!sstrcmp(type, sstr("keyfile"))) {
+    } else if(!cx_strcmp(type, cx_str("keyfile"))) {
         // we only need the file parameter
-        scstr_t file = serverconfig_directive_value(obj, SC("File"));
+        cxstring file = serverconfig_directive_value(obj, cx_str("File"));
         if(file.length == 0) {
             log_ereport(
                     LOG_MISCONFIG,
@@ -579,7 +580,7 @@
     }
 
     if(authdb) {
-        if(ucx_map_sstr_put(cfg->authdbs, name, authdb)) {
+        if(cxMapPut(cfg->authdbs, cx_hash_key_bytes((const unsigned char*)name.ptr, name.length), authdb)) {
             return -1;
         }
     }
@@ -594,13 +595,13 @@
     lc.port = 8080;
     lc.nacceptors = 1;
     
-    scstr_t name = serverconfig_directive_value(obj, SC("Name"));
-    scstr_t port = serverconfig_directive_value(obj, SC("Port"));
-    scstr_t vs   = serverconfig_directive_value(obj, SC("DefaultVS"));
-    scstr_t thrp = serverconfig_directive_value(obj, SC("Threadpool"));
-    scstr_t blck = serverconfig_directive_value(obj, SC("BlockingIO"));
+    cxstring name = serverconfig_directive_value(obj, cx_str("Name"));
+    cxstring port = serverconfig_directive_value(obj, cx_str("Port"));
+    cxstring vs   = serverconfig_directive_value(obj, cx_str("DefaultVS"));
+    cxstring thrp = serverconfig_directive_value(obj, cx_str("Threadpool"));
+    cxstring blck = serverconfig_directive_value(obj, cx_str("BlockingIO"));
     
-    // TODO: use sstrdup_pool?
+    // TODO: use cx_strdup_pool?
     int64_t port_value;
     if(!util_strtoint(port.ptr, &port_value)) {
         log_ereport(LOG_MISCONFIG, "Listener: Invalid argument for parameter 'Port': '%s'", port.ptr);
@@ -611,19 +612,19 @@
         return 1;
     }
     
-    lc.name = sstrdup(name);
+    lc.name = cx_strdup(name);
     lc.port = port_value;
-    lc.vs = sstrdup(vs);
-    lc.threadpool = sstrdup(thrp);
+    lc.vs = cx_strdup(vs);
+    lc.threadpool = cx_strdup(thrp);
     
     lc.blockingio = util_getboolean_s(blck, WS_FALSE);
     
-    scstr_t ssl = serverconfig_directive_value(obj, SC("SSL"));
+    cxstring ssl = serverconfig_directive_value(obj, cx_str("SSL"));
     if(util_getboolean_s(ssl, WS_FALSE)) {
-        scstr_t cert        = serverconfig_directive_value(obj, SC("Cert"));
-        scstr_t privkey     = serverconfig_directive_value(obj, SC("Key"));
-        scstr_t chain       = serverconfig_directive_value(obj, SC("CertChain"));
-        scstr_t disableprot = serverconfig_directive_value(obj, SC("SSLDisableProtocol"));
+        cxstring cert        = serverconfig_directive_value(obj, cx_str("Cert"));
+        cxstring privkey     = serverconfig_directive_value(obj, cx_str("Key"));
+        cxstring chain       = serverconfig_directive_value(obj, cx_str("CertChain"));
+        cxstring disableprot = serverconfig_directive_value(obj, cx_str("SSLDisableProtocol"));
         
         WSBool config_ok = WS_TRUE;
         // TODO: log error
@@ -660,8 +661,8 @@
         return 1;
     }
     
-    listener->default_vs.vs_name = sstrdup_a(cfg->a, lc.vs).ptr;
-    cfg->listeners = ucx_list_append_a(cfg->a, cfg->listeners, listener); 
+    listener->default_vs.vs_name = cx_strdup_a(cfg->a, (cxstring){lc.vs.ptr, lc.vs.length}).ptr;
+    cxListAdd(cfg->listeners, listener);
     
     return 0;
 }
@@ -669,18 +670,17 @@
 int cfg_handle_vs(ServerConfiguration *cfg, ConfigNode *obj) {
     VirtualServer *vs = vs_new();
 
-    vs->name = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("Name")));
-    vs->host = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("Host")));
-    vs->document_root = sstrdup_a(cfg->a, serverconfig_directive_value(obj, SC("DocRoot")));
+    vs->name = cx_strdup_a(cfg->a, serverconfig_directive_value(obj, cx_str("Name")));
+    vs->host = cx_strdup_a(cfg->a, serverconfig_directive_value(obj, cx_str("Host")));
+    vs->document_root = cx_strdup_a(cfg->a, serverconfig_directive_value(obj, cx_str("DocRoot")));
     
-    scstr_t objfile = serverconfig_directive_value(obj, SC("ObjectFile"));
-    scstr_t aclfile = serverconfig_directive_value(obj, SC("ACLFile"));
+    cxstring objfile = serverconfig_directive_value(obj, cx_str("ObjectFile"));
+    cxstring aclfile = serverconfig_directive_value(obj, cx_str("ACLFile"));
     
     // load the object config file
-    sstr_t base = sstr("config/");
-    sstr_t file = sstrcat(2, base, objfile);
-    // sstrcat with allocator because we want to keep the string
-    file = sstrcat_a(cfg->a, 2, base, objfile);
+    cxstring base = cx_str("config/");
+    // cx_strcat with allocator because we want to keep the string
+    cxmutstr file = cx_strcat_a(cfg->a, 2, base, objfile);
 
     HTTPObjectConfig *httpobj = objconf_load(cfg, file);
     if(!httpobj) {
@@ -691,45 +691,44 @@
     
     
     // load acl config file
-    file = sstrcat(2, base, aclfile);
+    cxmutstr acl_filepath = cx_strcat(2, base, aclfile);
     
-    ACLData *acldata = acl_conf_load(cfg, file);
+    ACLData *acldata = acl_conf_load(cfg, acl_filepath.ptr);
+    free(acl_filepath.ptr);
     if(!acldata) {
         return -1;
     }
     vs->acls = acldata;
     
-    free(file.ptr);
-    
     
     // set the access log for the virtual server
     // TODO: don't always use the default
     vs->log = cfg->default_log;
 
-    ucx_map_sstr_put(cfg->host_vs, vs->host, vs);
+    cxMapPut(cfg->host_vs, cx_hash_key_bytes((unsigned const char*)vs->host.ptr, vs->host.length), vs);
     
     return 0;
 }
 
 int cfg_handle_dav(ServerConfiguration *cfg, ConfigNode *obj) {
-    UcxList *backends = NULL; // list of ConfigArg*
-    UcxAllocator a = util_pool_allocator(cfg->pool);
+    CxAllocator *a = pool_allocator(cfg->pool);
+    CxList *backends = cxPointerLinkedListCreate(a, cx_cmp_ptr); // list of ConfigParam*
     int init_error;
     
     // parse args
     char *uri = NULL;
     char *ppath = NULL;
     char *name = NULL;
-    UCX_FOREACH(elm, obj->args) {
-        ConfigArg *arg = elm->data;
+    for(ConfigParam *arg=obj->args;arg;arg=arg->next) {
+        cxstring arg_name = (cxstring){ arg->name.ptr, arg->name.length };
         if(arg->name.ptr == NULL) {
             // default: uri
             uri = arg->value.ptr;
-        } else if(!sstrcasecmp(arg->name, SC("uri"))) {
+        } else if(!cx_strcasecmp(arg_name, cx_str("uri"))) {
             uri = arg->value.ptr;
-        } else if(!sstrcasecmp(arg->name, SC("ppath"))) {
+        } else if(!cx_strcasecmp(arg_name, cx_str("ppath"))) {
             ppath = arg->value.ptr;
-        } else if(!sstrcasecmp(arg->name, SC("name"))) {
+        } else if(!cx_strcasecmp(arg_name, cx_str("name"))) {
             name = arg->value.ptr;
         }
     }
@@ -738,21 +737,20 @@
     }
     
     // get a list of all DavBackends
-    UCX_FOREACH(elm, obj->children) {
-        ConfigNode *node = elm->data;
-        if(!sstrcasecmp(node->name, SC("DavBackend"))) {
+    for(ConfigNode *node=obj->children_begin;node;node=node->next) {
+        cxstring node_name = cx_strn(node->name.ptr, node->name.length);
+        if(!cx_strcasecmp(node_name, cx_str("DavBackend"))) {
             if(node->type == CONFIG_NODE_DIRECTIVE) {
-                if(ucx_list_size(node->args) == 1) {
-                    ConfigArg *arg = node->args->data;
-                    backends = ucx_list_append(backends, arg);
+                if(CFG_NUM_PARAMS(node->args) == 1) {
+                    cxListAdd(backends, node->args);
                 } else {
                     log_ereport(LOG_MISCONFIG, "DavBackend must have only one value");
-                    ucx_list_free(backends);
+                    cxListDestroy(backends);
                     return 1;
                 }
             } else {
                 log_ereport(LOG_MISCONFIG, "DavBackend must be a directive");
-                ucx_list_free(backends);
+                cxListDestroy(backends);
                 return 1;
             }
         }
@@ -762,14 +760,14 @@
     WebdavRepository *repository = pool_malloc(cfg->pool, sizeof(WebdavRepository));
     repository->vfs = NULL;
     repository->vfsInitData = NULL;
-    repository->davBackends = NULL;
+    repository->davBackends = cxPointerLinkedListCreate(a, cx_cmp_ptr); // value type: WebdavBackendInitData*
     
     // initialize backends
-    UCX_FOREACH(elm, backends) {
+    CxIterator i = cxListIterator(backends, 0);
+    cx_foreach(ConfigParam *, backendArg, i) {
         // the DavBackend value should contain the dav class name
-        ConfigArg *backendArg = elm->data;
         
-        WebdavType *dav = webdav_get_type((scstr_t){backendArg->value.ptr, backendArg->value.length});
+        WebdavType *dav = webdav_get_type((cxstring){backendArg->value.ptr, backendArg->value.length});
         if(!dav) {
             log_ereport(LOG_MISCONFIG, "Unknown webdav backend type '%s'", backendArg->value.ptr);
             ret = 1;
@@ -794,13 +792,14 @@
         davInit->davType = dav;
         davInit->davInitData = init_data;
         
-        repository->davBackends = ucx_list_append_a(&a, repository->davBackends, davInit);
+        cxListAdd(repository->davBackends, davInit);
     }
+    cxListDestroy(backends);
     
     // initialize vfs
-    scstr_t vfs_class = serverconfig_directive_value(obj, SC("VFS"));
+    cxstring vfs_class = serverconfig_directive_value(obj, cx_str("VFS"));
     if(vfs_class.length > 0) {
-        VfsType *vfs = vfs_get_type((scstr_t){vfs_class.ptr, vfs_class.length});
+        VfsType *vfs = vfs_get_type((cxstring){vfs_class.ptr, vfs_class.length});
         if(vfs) {
             repository->vfs = vfs;
             repository->vfsInitData = vfs_init_backend(cfg, cfg->pool, vfs, obj, &init_error);
@@ -813,9 +812,9 @@
         }
     }
     
-    scstr_t object = serverconfig_directive_value(obj, SC("Object"));
+    cxstring object = serverconfig_directive_value(obj, cx_str("Object"));
     if(object.length > 0) {
-        repository->object = sstrdup_a(&a, object);
+        repository->object = cx_strdup_a(a, object);
         if(repository->object.length != object.length) {
             // OOM
             log_ereport(LOG_FAILURE, "Cannot create webdav repository: OOM");
@@ -825,7 +824,7 @@
     
     if(!ret) {
         if(name) {
-            ucx_map_cstr_put(cfg->dav, name, repository);
+            cxMapPut(cfg->dav, cx_hash_key_str(name), repository);
         } else {
             log_ereport(LOG_FAILURE, "TODO: location based dav repositories not implemented");
             ret = 1;
@@ -835,23 +834,22 @@
     return ret;    
 }
 
-static int convert_objconf(ServerConfiguration *scfg, ObjectConfig *cfg, HTTPObjectConfig *conf, sstr_t file) {
+static int convert_objconf(ServerConfiguration *scfg, ObjectConfig *cfg, HTTPObjectConfig *conf, cxmutstr file) {
     pool_handle_t *pool = conf->pool;
     
-    UcxList *objlist = cfg->objects;
+    CxList *objlist = cfg->objects;
+    CxIterator iter = cxListIterator(objlist, 0);
     int i = 0;
-    while(objlist != NULL) {
-        ConfigObject *cob = objlist->data;
-
+    cx_foreach(ConfigObject*, cob, iter) {
         // get name and ppath
         char *name = NULL;
         char *ppath = NULL;
         if(cob->name.length > 0) {
-            name = sstrdup_pool(pool, cob->name).ptr;
+            name = cx_strdup_pool(pool, cob->name).ptr;
             if(!name) return -1;
         }
         if(cob->ppath.length > 0) {
-            ppath = sstrdup_pool(pool, cob->ppath).ptr;
+            ppath = cx_strdup_pool(pool, cob->ppath).ptr;
             if(!ppath) return -1;
         }
 
@@ -864,14 +862,14 @@
 
         // add directives
         for(int j=0;j<NUM_NSAPI_TYPES-1;j++) {
-            UcxList *dirs = cob->directives[j];
+            ConfigDirectiveList *dirs = cob->directives[j];
             while(dirs != NULL) {
-                ConfigDirective *cfgdir = dirs->data;
+                ConfigDirective *cfgdir = dirs->directive;
 
                 directive *d = pool_malloc(pool, sizeof(directive));
                 if(!d) return -1;
                 if(cfgdir->condition) {
-                    sstr_t expr = cfgdir->condition->param_str;
+                    cxmutstr expr = cfgdir->condition->param_str;
                     d->cond = condition_from_str(pool, expr.ptr, expr.length);
                 } else {
                     d->cond = NULL;
@@ -879,14 +877,13 @@
                 d->param = pblock_create_pool(pool, 8);
 
                 // add params
-                UcxList *param = cfg_param_list(cfgdir->value, scfg->a);
+                ConfigParam *param = cfg_param_list(cfgdir->value, scfg->a);
                 while(param != NULL) {
-                    ConfigParam *p = param->data;
                     pblock_nvlinsert(
-                            p->name.ptr,
-                            p->name.length,
-                            p->value.ptr,
-                            p->value.length,
+                            param->name.ptr,
+                            param->name.length,
+                            param->value.ptr,
+                            param->value.length,
                             d->param);
                     param = param->next;
                 }
@@ -912,13 +909,12 @@
 
         // next
         i++;
-        objlist = objlist->next;
     }
     
     return 0;
 }
 
-HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, sstr_t file) {
+HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, cxmutstr file) {
     log_ereport(LOG_VERBOSE, "load_obj_conf");
     
     int ret = 0;
@@ -940,7 +936,7 @@
     // convert ObjectConfig to HTTPObjectConfig
 
     // add objects
-    conf->nobj = ucx_list_size(cfg->objects);
+    conf->nobj = cfg->objects->size;
     conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*));
     if(conf->objects) {
         ret = convert_objconf(scfg, cfg, conf, file);
@@ -953,7 +949,7 @@
     return !ret ? conf : NULL;
 }
 
-int mime_conf_load(ServerConfiguration *cfg, sstr_t file) {
+int mime_conf_load(ServerConfiguration *cfg, cxmutstr file) {
     MimeConfig *mimecfg = load_mime_config(file.ptr);
     if(!mimecfg) {
         return -1;
@@ -962,20 +958,19 @@
     int ret = 0;
     
     // cleanup in case of errors is done by the allocator
-    MimeMap *mimemap = almalloc(cfg->a, sizeof(MimeMap));
-    UcxMap *map = ucx_map_new_a(cfg->a, (mimecfg->ntypes * 3) / 2);
+    MimeMap *mimemap = cxMalloc(cfg->a, sizeof(MimeMap));
+    CxMap *map = cxHashMapCreate(cfg->a, (mimecfg->ntypes * 3) / 2);
     
     if(mimemap && map) {
         mimemap->map = map;
         
         // add ext type pairs
-        UCX_FOREACH(md, mimecfg->directives) {
-            MimeDirective *d = md->data;
+        for(MimeDirective *d=mimecfg->directives_begin;d;d=d->next) {
             // add the type for each extension to the map
-            UCX_FOREACH(xl, d->exts) {
-                sstr_t ext = sstr(xl->data);
-                sstr_t value = sstrdup(d->type);
-                if(ucx_map_sstr_put(map, ext, value.ptr)) {
+            for(int i=0;i<d->nextensions;i++) {
+                cxstring ext = d->extensions[i];
+                cxmutstr value = cx_strdup(cx_strn(d->type.ptr, d->type.length));
+                if(cxMapPut(map, cx_hash_key_bytes((const unsigned char *)ext.ptr, ext.length), value.ptr)) {
                     log_ereport(LOG_CATASTROPHE, "OOM");
                     ret = -1;
                     break;
@@ -998,17 +993,21 @@
 
 
 
-ACLData* acl_conf_load(ServerConfiguration *cfg, sstr_t file) {
-    ACLFile *aclfile = load_acl_file(file.ptr);
+ACLData* acl_conf_load(ServerConfiguration *cfg, const char *file) {
+    ACLFile *aclfile = load_acl_file(file);
+    if(!aclfile) {
+        log_ereport(LOG_FAILURE, "Cannot load acl file %s", file);
+        return NULL;
+    }
     
     // TODO: malloc return checks
     
     ACLData *acldata = acl_data_new(cfg->a);
-    UCX_FOREACH(elm, aclfile->namedACLs) {
-        ACLConfig *ac = elm->data;
+    CxIterator iter = cxListIterator(aclfile->namedACLs, 0);
+    cx_foreach(ACLConfig *, ac, iter) {
         ACLList *acl = acl_config_convert(cfg, ac);
         log_ereport(LOG_VERBOSE, "add acl: %.*s", (int)ac->id.length, ac->id.ptr);
-        ucx_map_sstr_put(acldata->namedACLs, ac->id, acl);
+        cxMapPut(acldata->namedACLs, cx_hash_key(ac->id.ptr, ac->id.length), acl);
     }
     free_acl_file(aclfile);
     
@@ -1016,9 +1015,9 @@
 }
 
 ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) {
-    UcxAllocator *a = cfg->a;
+    CxAllocator *a = cfg->a;
     
-    WSAcl *acllist = almalloc(cfg->a, sizeof(WSAcl));
+    WSAcl *acllist = cxMalloc(cfg->a, sizeof(WSAcl));
     acllist->acl.check = (acl_check_f)wsacl_check;
     acllist->acl.authdb = NULL;
     acllist->acl.authprompt = NULL;
@@ -1026,26 +1025,24 @@
     acllist->ace = NULL;
     acllist->ece = NULL;
     
-    if(acl->type.ptr && !sstrcmp(acl->type, sstr("fs"))) {
+    if(acl->type.ptr && !cx_strcmp(cx_strn(acl->type.ptr, acl->type.length), cx_str("fs"))) {
         acllist->acl.isextern = 1;
     }
     
-    size_t s = ucx_list_size(acl->entries);
+    size_t s = CFG_ACE_LIST_SIZE(acl->entries);
     WSAce **tmp_aces = calloc(s, sizeof(WSAce*));
     WSAce **tmp_eces = calloc(s, sizeof(WSAce*));
     int ai = 0;
     int ei = 0;
     
     // convert entries
-    UCX_FOREACH(elm, acl->entries) {
-        ACEConfig *acecfg = elm->data;
-        
+    for(ACEConfig *acecfg=acl->entries;acecfg;acecfg=acecfg->next) {
         // copy data
-        WSAce *ace = almalloc(a, sizeof(WSAce));
+        WSAce *ace = cxMalloc(a, sizeof(WSAce));
         ace->access_mask = acecfg->access_mask;
         ace->flags = acecfg->flags;
         ace->type = acecfg->type;
-        ace->who = sstrdup_a(a, acecfg->who).ptr;
+        ace->who = cx_strdup_a(a, cx_strcast(acecfg->who)).ptr;
         
         // add the entry to the correct array
         if(ace->type >= ACL_TYPE_AUDIT) {
@@ -1059,10 +1056,10 @@
     
     // create new entrie arrays with perfect fitting size
     if(ai > 0) {
-        acllist->ace = alcalloc(a, ai, sizeof(WSAce*));
+        acllist->ace = cxCalloc(a, ai, sizeof(WSAce*));
     }
     if(ei > 0) {
-        acllist->ece = alcalloc(a, ei, sizeof(WSAce*));
+        acllist->ece = cxCalloc(a, ei, sizeof(WSAce*));
     }
     memcpy(acllist->ace, tmp_aces, ai*sizeof(WSAce*));
     memcpy(acllist->ece, tmp_eces, ei*sizeof(WSAce*));
@@ -1074,14 +1071,14 @@
     
     // get authentication information
     if(acl->authparam) {
-        sstr_t authdb_str = cfg_param_get(acl->authparam, sstr("authdb"));
-        sstr_t prompt_str = cfg_param_get(acl->authparam, sstr("prompt"));
+        cxmutstr authdb_str = cfg_param_get(acl->authparam, cx_str("authdb"));
+        cxmutstr prompt_str = cfg_param_get(acl->authparam, cx_str("prompt"));
         
         if(authdb_str.ptr) {
-            AuthDB *authdb = ucx_map_sstr_get(cfg->authdbs, authdb_str);
+            AuthDB *authdb = cxMapGet(cfg->authdbs, cx_hash_key(authdb_str.ptr, authdb_str.length));
             acllist->acl.authdb = authdb;
             if(authdb && prompt_str.ptr) {
-                acllist->acl.authprompt = sstrdup_a(a, prompt_str).ptr;
+                acllist->acl.authprompt = cx_strdup_a(a, cx_strcast(prompt_str)).ptr;
             }
         }
     }
@@ -1089,7 +1086,7 @@
     return &acllist->acl;
 }
 
-AuthDB* keyfile_load(ServerConfiguration *cfg, scstr_t file) {
+AuthDB* keyfile_load(ServerConfiguration *cfg, cxstring file) {
     Keyfile *keyfile = keyfile_new(cfg->a);
     if(!keyfile) {
         return NULL;
@@ -1102,8 +1099,7 @@
     
     AuthDB *ret = &keyfile->authdb;
     
-    UCX_FOREACH(elm, conf->users) {
-        KeyfileEntry *user = elm->data;
+    for(KeyfileEntry *user=conf->users_begin;user;user=user->next) {
         if(keyfile_add_user(
                 keyfile,
                 user->name,
@@ -1124,10 +1120,9 @@
 
 pblock* config_obj2pblock(pool_handle_t *pool, ConfigNode *obj) {
     pblock *pb = pblock_create_pool(pool, 8);
-    UCX_FOREACH(elm, obj->children) {
-        ConfigNode *d = elm->data;
-        if(d->type == CONFIG_NODE_DIRECTIVE && d->name.length > 0 && ucx_list_size(d->args) == 1) {
-            ConfigArg *arg = d->args->data;
+    for(ConfigNode *d=obj->children_begin;d;d=d->next) {
+        if(d->type == CONFIG_NODE_DIRECTIVE && d->name.length > 0 && CFG_NUM_PARAMS(d->args) == 1) {
+            ConfigParam *arg = d->args;
             pblock_nvlinsert(d->name.ptr, d->name.length, arg->value.ptr, arg->value.length, pb);
         }
     }
--- a/src/server/daemon/config.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/config.h	Sun Nov 06 15:53:32 2022 +0100
@@ -47,10 +47,10 @@
 
 #include "../webdav/webdav.h"
 
-#include <ucx/list.h>
-#include <ucx/map.h>
-#include <ucx/mempool.h>
-#include <ucx/string.h>
+#include <cx/linked_list.h>
+#include <cx/hash_map.h>
+#include <cx/mempool.h>
+#include <cx/string.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -67,27 +67,27 @@
 } CfgManager;
 
 struct ServerConfiguration {
-    pool_handle_t   *pool;
-    UcxAllocator    *a;
+    pool_handle_t  *pool;
+    CxAllocator    *a;
       
-    UcxMap          *host_vs;   // map of all vservers. key is the host name
-    UcxList         *listeners; // list of all listeners
-    UcxList         *logfiles;
-    AccessLog       *default_log;
-    UcxMap          *authdbs;
-    MimeMap         *mimetypes;
-    UcxMap          *resources;
-    UcxMap          *dav;
-    sstr_t          tmp;
-    sstr_t          user;
-    uint32_t        ref;        // reference counter
+    CxMap          *host_vs;   // map of all vservers. key is the host name
+    CxList         *listeners; // list of all listeners
+    CxList         *logfiles;
+    AccessLog      *default_log;
+    CxMap          *authdbs;
+    MimeMap        *mimetypes;
+    CxMap          *resources;
+    CxMap          *dav;
+    cxmutstr       tmp;
+    cxmutstr       user;
+    uint32_t       ref;        // reference counter
 };
 
 struct WebdavRepository {
-    VfsType *vfs;
-    void *vfsInitData;
-    UcxList *davBackends; // list of WebdavBackendInitData*
-    sstr_t object;
+    VfsType  *vfs;
+    void     *vfsInitData;
+    CxList   *davBackends; // list of WebdavBackendInitData*
+    cxmutstr object;
 };
 
 struct WebdavBackendInitData {
@@ -96,7 +96,7 @@
 };
 
 struct mime_map {
-    UcxMap   *map;
+    CxMap   *map;
 };
 
 char* cfg_config_file_path(const char *file);
@@ -130,12 +130,12 @@
 void cfg_ref(ServerConfiguration *cfg);
 void cfg_unref(ServerConfiguration *cfg);
 
-HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, sstr_t file);
-int mime_conf_load(ServerConfiguration *cfg, sstr_t file);
+HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, cxmutstr file);
+int mime_conf_load(ServerConfiguration *cfg, cxmutstr file);
 
-ACLData* acl_conf_load(ServerConfiguration *cfg, sstr_t file);
+ACLData* acl_conf_load(ServerConfiguration *cfg, const char *file);
 ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl);
-AuthDB* keyfile_load(ServerConfiguration *cfg, scstr_t file);
+AuthDB* keyfile_load(ServerConfiguration *cfg, cxstring file);
 
 pblock* config_obj2pblock(pool_handle_t *pool, ConfigNode *obj);
 
--- a/src/server/daemon/configmanager.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/configmanager.c	Sun Nov 06 15:53:32 2022 +0100
@@ -31,7 +31,7 @@
 
 #include "../public/nsapi.h"
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "httplistener.h"
 #include "log.h"
--- a/src/server/daemon/configmanager.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/configmanager.h	Sun Nov 06 15:53:32 2022 +0100
@@ -33,10 +33,10 @@
 
 #include "vserver.h"
 
-#include <ucx/list.h>
-#include <ucx/map.h>
-#include <ucx/mempool.h>
-#include <ucx/string.h>
+#include <cx/list.h>
+#include <cx/map.h>
+#include <cx/mempool.h>
+#include <cx/string.h>
 
 #include <sys/types.h>
 
--- a/src/server/daemon/error.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/error.c	Sun Nov 06 15:53:32 2022 +0100
@@ -28,24 +28,24 @@
 
 #include "error.h"
 
-#include <ucx/string.h>
+#include <cx/string.h>
 #include "../util/pblock.h"
 #include "../util/strbuf.h"
 
-// macro for creating an error string (ucx sstr_t)
+// macro for creating an error string (ucx cxmutstr)
 #define ERRMSG(s) { s, sizeof(s)-1 }
 
-static sstr_t error_400 = ERRMSG("<html><body>bad request</body></html>");
-static sstr_t error_403 = ERRMSG("<html><body>forbidden</body></html>");
-static sstr_t error_404 = ERRMSG("<html><body>not found</body></html>");
-static sstr_t error_500 = ERRMSG("<html><body>server error</body></html>");
-static sstr_t error_503 = ERRMSG("<html><body>service unavailable</body></html>");
+static cxmutstr error_400 = ERRMSG("<html><body>bad request</body></html>");
+static cxmutstr error_403 = ERRMSG("<html><body>forbidden</body></html>");
+static cxmutstr error_404 = ERRMSG("<html><body>not found</body></html>");
+static cxmutstr error_500 = ERRMSG("<html><body>server error</body></html>");
+static cxmutstr error_503 = ERRMSG("<html><body>service unavailable</body></html>");
 
-static sstr_t error_std = ERRMSG("<html><body>error</body></html>");
+static cxmutstr error_std = ERRMSG("<html><body>error</body></html>");
 
 int nsapi_error_request(Session *sn, Request *rq) {
     short status = rq->status_num;
-    sstr_t msg;
+    cxmutstr msg;
     if(status < 400) {
         msg.ptr = NULL;
         msg.length = 0;
@@ -82,7 +82,7 @@
 }
 
 void fatal_error(HTTPRequest *req, int status) {
-    sstr_t msg = error_500;
+    cxmutstr msg = error_500;
     char *statusmsg = "Internal Server Error";
     switch(status) {
         case 400: {
--- a/src/server/daemon/event.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/event.c	Sun Nov 06 15:53:32 2022 +0100
@@ -26,12 +26,12 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <ucx/map.h>
+#include <cx/hash_map.h>
 #include "../util/atomic.h"
 
 #include "event.h"
 
-UcxMap *event_handler_map = NULL;
+CxMap *event_handler_map = NULL;
 int numevhandlers = 0;
 
 EVHandler *default_event_handler = NULL;
@@ -40,11 +40,13 @@
 
 int create_event_handler(EventHandlerConfig *cfg) {
     if(event_handler_map == NULL) {
-        event_handler_map = ucx_map_new(16);
+        event_handler_map = cxHashMapCreate(cxDefaultAllocator, 16);
     }
     
+    CxHashKey key = cx_hash_key_bytes((const unsigned char*)cfg->name.ptr, cfg->name.length);
+    
     /* if the event handler already exists, we don't modify it */
-    if(ucx_map_sstr_get(event_handler_map, cfg->name)) {
+    if(cxMapGet(event_handler_map, key)) {
         /* TODO: log message */
         /* TODO: set reload status */
         return 1;
@@ -64,7 +66,7 @@
         default_event_handler = e;
     }
     
-    int ret = ucx_map_sstr_put(event_handler_map, cfg->name, e);
+    int ret = cxMapPut(event_handler_map, key, e);
     if(ret == 0) {
         last_handler_c = e;
         numevhandlers++;
@@ -88,7 +90,7 @@
     }
     
     EventHandlerConfig cfg;
-    cfg.name = SC("default");
+    cfg.name = cx_str("default");
     cfg.nthreads = 1;
     cfg.isdefault = 1;
     
@@ -100,8 +102,8 @@
     return default_event_handler;
 }
 
-EVHandler* get_event_handler(char *name) {
-    return ucx_map_cstr_get(event_handler_map, name);
+EVHandler* get_event_handler(const char *name) {
+    return cxMapGet(event_handler_map, cx_hash_key_str(name));
 }
 
 EventHandler* ev_instance(EVHandler *ev) {
--- a/src/server/daemon/event.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/event.h	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,7 @@
 #define	EVENT_H
 
 #include "../public/nsapi.h"
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -43,7 +43,7 @@
 } EVHandler;
 
 typedef struct event_handler_conf {
-    scstr_t  name;
+    cxstring  name;
     int      nthreads;
     int      isdefault;
 } EventHandlerConfig;
@@ -54,7 +54,7 @@
 
 EVHandler* get_default_event_handler();
 
-EVHandler* get_event_handler(char *name);
+EVHandler* get_event_handler(const char *name);
 
 EventHandler* ev_instance(EVHandler *ev);
 
--- a/src/server/daemon/func.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/func.c	Sun Nov 06 15:53:32 2022 +0100
@@ -28,17 +28,18 @@
 
 
 #include <stdlib.h>
-#include <ucx/map.h>
+#include <cx/map.h>
+#include <cx/hash_map.h>
 
 #include "../public/nsapi.h"
 #include "../util/pblock.h"
 
 #include "func.h"
 
-UcxMap *function_map;
+CxMap *function_map;
 
 void func_init() {
-    function_map = ucx_map_new(1337);
+    function_map = cxHashMapCreate(cxDefaultAllocator, 256);
 }
 
 void add_function(FuncStruct *func) {
@@ -52,7 +53,7 @@
     }
     f->name = name;
     log_ereport(LOG_VERBOSE, "add_function %s", f->name);
-    ucx_map_cstr_put(function_map, (char*)f->name, f);
+    cxMapPut(function_map, cx_hash_key_str(f->name), f);
 }
 
 void add_functions(FuncStruct *funcs) {
@@ -63,13 +64,16 @@
     }
 }
 
-FuncStruct* get_function(char *name) {
-    for(int i=0;name[i]!='\0';i++) {
-        if(name[i] == '_') {
-            name[i] = '-';
+FuncStruct* get_function(const char *name) {
+    char *name_dup = strdup(name);
+    for(int i=0;name_dup[i]!='\0';i++) {
+        if(name_dup[i] == '_') {
+            name_dup[i] = '-';
         }
     }
-    return ucx_map_cstr_get(function_map, name);
+    void *ret = cxMapGet(function_map, cx_hash_key_str(name_dup));
+    free(name_dup);
+    return ret;
 }
 
 FuncStruct* func_resolve(pblock *pb, Session *sn, Request *rq) {
--- a/src/server/daemon/func.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/func.h	Sun Nov 06 15:53:32 2022 +0100
@@ -45,7 +45,7 @@
 
 void add_functions(struct FuncStruct *funcs);
 
-FuncStruct* get_function(char *name);
+FuncStruct* get_function(const char *name);
 
 
 #ifdef	__cplusplus
--- a/src/server/daemon/httplistener.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/httplistener.c	Sun Nov 06 15:53:32 2022 +0100
@@ -54,7 +54,8 @@
 #include <sys/socket.h>
 #include <unistd.h>
 
-#include <ucx/map.h>
+
+#include <cx/hash_map.h>
 
 #include "../util/atomic.h"
 #include "httplistener.h"
@@ -64,15 +65,16 @@
 #include "configmanager.h"
 #include "log.h"
 
-UcxMap *listener_map = NULL;
+#define LISTENER_MAX_PROTOCOL_TOKENS 1024
+
+CxMap *listener_map = NULL;
 
 int start_all_listener() {
     ServerConfiguration *conf = cfgmgr_get_server_config();
-    UcxList *ls = conf->listeners;
-    while(ls) {
-        HttpListener *listener = ls->data;
+    CxList *ls = conf->listeners;
+    CxIterator iter = cxListIterator(ls, 0);
+    cx_foreach(HttpListener *, listener, iter) {
         http_listener_start(listener);
-        ls = ls->next;
     }
 
     return 0;
@@ -80,10 +82,10 @@
 
 HttpListener* http_listener_create(ListenerConfig *conf) {
     if(listener_map == NULL) {
-        listener_map = ucx_map_new(16);
+        listener_map = cxHashMapCreate(cxDefaultAllocator, 16);
     }
 
-    HttpListener *fl = ucx_map_sstr_get(listener_map, conf->name);
+    HttpListener *fl = cxMapGet(listener_map, cx_hash_key(conf->name.ptr, conf->name.length));
     if(fl == NULL) {
         return http_listener_new(conf);
     }
@@ -108,7 +110,7 @@
     
     // the listener threadpool might be changed
     if(conf->threadpool.ptr != NULL) {
-        newls->threadpool = get_threadpool(conf->threadpool);
+        newls->threadpool = get_threadpool(cx_strcast(conf->threadpool));
     }
     if(newls->threadpool == NULL) {
         newls->threadpool = get_default_threadpool();
@@ -130,7 +132,7 @@
     fl->next = newls;
     
     
-    ucx_map_sstr_put(listener_map, newls->name, newls);
+    cxMapPut(listener_map, cx_hash_key(newls->name.ptr, newls->name.length), newls);
     
     for (int i=0;i<newls->nacceptors;i++) {
         //acceptor_start(newls->acceptors[i]);
@@ -149,10 +151,10 @@
 HttpListener* http_listener_new(ListenerConfig *conf) {
     // TODO: remove
     if(listener_map == NULL) {
-        listener_map = ucx_map_new(16);
+        listener_map = cxHashMapCreate(cxDefaultAllocator, 16);
     }
 
-    HttpListener *fl = ucx_map_sstr_get(listener_map, conf->name);
+    HttpListener *fl = cxMapGet(listener_map, cx_hash_key(conf->name.ptr, conf->name.length));
     if(fl != NULL) {
         return fl;
     }
@@ -165,7 +167,7 @@
     listener->default_vs.vs_name = conf->vs.ptr;
     listener->threadpool = NULL;
     if(conf->threadpool.ptr != NULL) {
-        listener->threadpool = get_threadpool(conf->threadpool);
+        listener->threadpool = get_threadpool(cx_strcast(conf->threadpool));
     }
     if(listener->threadpool == NULL) {
         listener->threadpool = get_default_threadpool();
@@ -191,23 +193,23 @@
                 ctx,
                 SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv3);
         if(conf->disable_proto.ptr) {
-            ssize_t n = 0;
-            sstr_t *plist = sstrsplit(conf->disable_proto, S(","), &n);
+            cxstring *plist = NULL;
+            ssize_t n = cx_strsplit_a(cxDefaultAllocator, conf->disable_proto, cx_str(","), LISTENER_MAX_PROTOCOL_TOKENS, &plist);
             if(plist) {
                 for(int i=0;i<n;i++) {
-                    sstr_t proto = plist[i];
+                    cxstring proto = plist[i];
                     log_ereport(
                             LOG_VERBOSE,
                             "Listener %s: Disable protocol %s",
                             listener->name.ptr,
                             proto.ptr);
-                    if(!sstrcasecmp(sstrtrim(proto), S("SSLv2"))) {
+                    if(!cx_strcasecmp(cx_strtrim(proto), cx_str("SSLv2"))) {
                         SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
-                    } else if(!sstrcasecmp(sstrtrim(proto), S("SSLv3"))) {
+                    } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("SSLv3"))) {
                         SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
-                    } else if(!sstrcasecmp(sstrtrim(proto), S("TLSv1"))) {
+                    } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1"))) {
                         SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
-                    } else if(!sstrcasecmp(sstrtrim(proto), S("TLSv1.1"))) {
+                    } else if(!cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.1"))) {
 #ifdef SSL_OP_NO_TLSv1_1
                         SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
 #else
@@ -216,7 +218,7 @@
                                 "Listener: %s: TLSv1.1 already not supported",
                                 listener->name.ptr);
 #endif
-                    } else if(sstrcasecmp(sstrtrim(proto), S("TLSv1.2"))) {
+                    } else if(cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.2"))) {
 #ifdef SSL_OP_NO_TLSv1_2
                         SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
 #else
@@ -225,7 +227,7 @@
                                 "Listener: %s: TLSv1.2 already not supported",
                                 listener->name.ptr);
 #endif
-                    } else if(sstrcasecmp(sstrtrim(proto), S("TLSv1.3"))) {
+                    } else if(cx_strcasecmp(cx_strtrim(proto), cx_str("TLSv1.3"))) {
 #ifdef SSL_OP_NO_TLSv1_3
                         SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_3);
 #else
@@ -242,7 +244,6 @@
                                 listener->name.ptr,
                                 proto.ptr);
                     }
-                    free(proto.ptr);
                 }
                 free(plist);
             }
@@ -284,7 +285,7 @@
     }
     
     
-    ucx_map_sstr_put(listener_map, listener->name, listener);
+    cxMapPut(listener_map, cx_hash_key(listener->name.ptr, listener->name.length), listener);
 
     struct sockaddr_in servaddr;   /* server address */
     struct sockaddr_in6 servaddr6;
--- a/src/server/daemon/httplistener.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/httplistener.h	Sun Nov 06 15:53:32 2022 +0100
@@ -56,18 +56,18 @@
 };
 struct _listener_config {
     ServerConfiguration  *cfg;
-    sstr_t               name;
-    sstr_t               vs;
-    sstr_t               threadpool;
+    cxmutstr               name;
+    cxmutstr               vs;
+    cxmutstr               threadpool;
     char                 *address;
     int                  port;
     int                  nacceptors;
     WSBool               blockingio;
     WSBool               ssl;
-    scstr_t              certfile;
-    scstr_t              privkeyfile;
-    scstr_t              chainfile;
-    scstr_t              disable_proto;
+    cxstring              certfile;
+    cxstring              privkeyfile;
+    cxstring              chainfile;
+    cxstring              disable_proto;
 };
 
 struct _acceptor {
@@ -78,7 +78,7 @@
 
 struct _http_listener {
     ServerConfiguration  *cfg;
-    sstr_t               name;
+    cxmutstr               name;
     union vs             default_vs;
     int                  port;
     int                  server_socket;
--- a/src/server/daemon/httpparser.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/httpparser.c	Sun Nov 06 15:53:32 2022 +0100
@@ -173,7 +173,7 @@
 }
 
 int parse_request_line(HttpParser *parser) {
-    sstr_t line = parser->start_line;
+    cxmutstr line = parser->start_line;
     parser->request->request_line = line;
     
     /*
--- a/src/server/daemon/httpparser.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/httpparser.h	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,7 @@
 #define	HTTPPARSER_H
 
 
-#include <ucx/string.h>
+#include <cx/string.h>
 #include "httprequest.h"
 
 #ifdef	__cplusplus
@@ -49,15 +49,15 @@
     HTTPRequest *request;
 
     int state;
-    sstr_t start_line;
+    cxmutstr start_line;
 
     /* local parser variables */
     int wl;       /* only white space */
     int tk;       /* token: 0: header name 1: header value */
     int offset;   /* offset of parsed string */
     int strend;   /* end position */
-    sstr_t name;
-    sstr_t value;
+    cxmutstr name;
+    cxmutstr value;
 
 } HttpParser;
 
--- a/src/server/daemon/httprequest.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/httprequest.c	Sun Nov 06 15:53:32 2022 +0100
@@ -64,27 +64,27 @@
     free(req);
 }
 
-sstr_t http_request_get_abspath(HTTPRequest *req) {
-    sstr_t uri = req->uri;
+cxmutstr http_request_get_abspath(HTTPRequest *req) {
+    cxmutstr uri = req->uri;
     
     int i = 0;
     if(uri.ptr[0] == '/') {
         return uri;
-    } else if(sstrprefix(uri, S("http://"))) {
+    } else if(cx_strprefix(cx_strcast(uri), (cxstring)CX_STR("http://"))) {
         i = 7;
-    } else if(sstrprefix(uri, S("https://"))) {
+    } else if(cx_strprefix(cx_strcast(uri), (cxstring)CX_STR("https://"))) {
         i = 8;
-    } else if(!sstrcmp(uri, S("*"))) {
+    } else if(!cx_strcmp(cx_strcast(uri), (cxstring)CX_STR("*"))) {
         return uri;
     }
     
     for(;i<uri.length;i++) {
         if(uri.ptr[i] == '/') {
-            return sstrsubs(uri, i);
+            return cx_strsubs_m(uri, i);
         }
     }
     
-    return S("/");
+    return (cxmutstr){ "/", 1 };
 }
 
 NSAPISession* nsapisession_create(pool_handle_t *pool) {
@@ -96,7 +96,6 @@
     ZERO(sn, sizeof(NSAPISession));
     
     sn->sn.pool = pool;
-    sn->allocator = util_pool_allocator(pool);
     
     sn->sn.client = pblock_create_pool(sn->sn.pool, 8);
     if(!sn->sn.client) {
@@ -198,7 +197,7 @@
 
     // Pass request line as "clf-request"
     // remove \r\n 
-    sstr_t clfreq = request->request_line;
+    cxmutstr clfreq = request->request_line;
     while(clfreq.length > 0 && clfreq.ptr[clfreq.length - 1] < 33) {
         clfreq.length--;
     }
@@ -225,9 +224,9 @@
             request->httpv.length,
             rq->rq.reqpb);
     
-    if(!sstrcmp(request->httpv, S("HTTP/1.1"))) {
+    if(!cx_strcmp(cx_strcast(request->httpv), (cxstring)CX_STR("HTTP/1.1"))) {
         rq->rq.protv_num = PROTOCOL_VERSION_HTTP11;
-    } else if(!sstrcmp(request->httpv, S("HTTP/1.0"))) {
+    } else if(!cx_strcmp(cx_strcast(request->httpv), (cxstring)CX_STR("HTTP/1.0"))) {
         rq->rq.protv_num = PROTOCOL_VERSION_HTTP10;
     } else {
         // invalid protocol version - abort
@@ -244,7 +243,7 @@
      * get absolute path and query of the request uri
      */
     // TODO: check for '#' #72
-    sstr_t absPath = http_request_get_abspath(request);
+    cxmutstr absPath = http_request_get_abspath(request);
     if(!absPath.ptr) {
         // TODO: error msg
         return 1;
@@ -253,7 +252,7 @@
         return 1;
     }
     
-    sstr_t query;
+    cxmutstr query;
     query.length = 0;
     
     for(int i=0;i<request->uri.length;i++) {
@@ -277,7 +276,7 @@
     }
     
     // Get abs_path part of request URI, and canonicalize the path
-    sstr_t orig_path = absPath;
+    cxmutstr orig_path = absPath;
     absPath.ptr = util_canonicalize_uri(
             pool,
             absPath.ptr,
@@ -371,7 +370,7 @@
     rq->port = request->connection->listener->port;
     
     if(rq->host) {
-        VirtualServer *vs = ucx_map_cstr_get(sn->config->host_vs, rq->host);
+        VirtualServer *vs = cxMapGet(sn->config->host_vs, cx_hash_key_str(rq->host));
         if(vs) {
             rq->vs = vs;
         } else {
@@ -454,7 +453,7 @@
 
 
 
-void header_add(HeaderArray *hd, sstr_t name, sstr_t value) {
+void header_add(HeaderArray *hd, cxmutstr name, cxmutstr value) {
     while(hd->len >= hd->alloc) {
         if(hd->next == NULL) {
             HeaderArray *block = malloc(sizeof(HeaderArray));
@@ -648,9 +647,8 @@
 void request_free_resources(NSAPISession *sn, NSAPIRequest *rq) {
     if(!rq->resources) return;
     
-    UcxMapIterator i = ucx_map_iterator(rq->resources);
-    ResourceData *resource;
-    UCX_MAP_FOREACH(key, resource, i) {
+    CxIterator i = cxMapIteratorValues(rq->resources);
+    cx_foreach(ResourceData *, resource, i) {
         resourcepool_free(&sn->sn, &rq->rq, resource);
     }
 }
@@ -737,7 +735,7 @@
 
     // if no function has set the ppath var, translate it to docroot
     if(ret == REQ_NOACTION && ppath == NULL) {
-        sstr_t docroot = rq->vs->document_root;
+        cxmutstr docroot = rq->vs->document_root;
         if(docroot.length < 1) {
             log_ereport(
                     LOG_WARN,
@@ -753,12 +751,12 @@
             docroot.length--;
         }
         
-        sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb));
+        cxmutstr uri = cx_str(pblock_findkeyval(pb_key_uri, rq->rq.reqpb));
         
-        sstr_t translated;
+        cxmutstr translated;
         translated.length = docroot.length + uri.length;
         translated.ptr = alloca(translated.length + 1);
-        translated = sstrncat(translated, 2, docroot, uri);
+        translated = cx_strncat(translated, 2, docroot, uri);
 
         pblock_kvinsert(
             pb_key_ppath,
@@ -766,8 +764,8 @@
             translated.length,
             rq->rq.vars);
         */
-        sstr_t uri = sstr(pblock_findkeyval(pb_key_uri, rq->rq.reqpb));
-        request_set_path(docroot, uri, rq->rq.vars);
+        cxstring uri = cx_str(pblock_findkeyval(pb_key_uri, rq->rq.reqpb));
+        request_set_path(cx_strcast(docroot), uri, rq->rq.vars);
     }
     
     // TODO: remove ppath
@@ -868,13 +866,13 @@
      * 'internal/directory' so that 'index-common' can serve the content.
      * Otherwise we set the content type to text/plain
      */
-    sstr_t path = sstr(pblock_findkeyval(pb_key_ppath, rq->rq.vars));
-    sstr_t ct;
+    cxstring path = cx_str(pblock_findkeyval(pb_key_ppath, rq->rq.vars));
+    cxstring ct;
     if(path.ptr[path.length - 1] == '/') {
         // directory
-        ct = sstrn("internal/directory", 18);
+        ct = (cxstring)CX_STR("internal/directory");
     } else {
-        ct = sstrn("text/plain", 10);
+        ct = (cxstring)CX_STR("text/plain");
     }
     pblock_kvinsert(pb_key_content_type, ct.ptr, ct.length, rq->rq.srvhdrs);
 
@@ -911,7 +909,7 @@
                                 rq->rq.srvhdrs);
                     }
                     // compare types
-                    if(!contenttype_match(sstr(dtp), sstr(content_type))) {
+                    if(!contenttype_match(cx_str(dtp), cx_str(content_type))) {
                         continue;
                     }
                 }
@@ -1079,7 +1077,7 @@
     
     char *poolname = pblock_findkeyval(pb_key_pool, d->param);
     if(poolname) {
-        threadpool_t *pool = get_threadpool(sstr(poolname));
+        threadpool_t *pool = get_threadpool(cx_str(poolname));
         if(pool && pool != sn->currentpool) {
             // execute directive in different thread pool
             return nsapi_exec_tp(d, sn, rq, pool);
@@ -1212,35 +1210,35 @@
  * of types (also with wildcard support)
  * (type1|type2*)
  */
-int contenttype_match(sstr_t cmp, sstr_t ctype) {
+int contenttype_match(cxstring cmp, cxstring ctype) {
     if(cmp.ptr[0] != '(') {
         if(cmp.ptr[0] == '*') {
             cmp.ptr++;
             cmp.length--;
-            return sstrsuffix(ctype, cmp);
+            return cx_strsuffix(ctype, cmp);
         } else if(cmp.ptr[cmp.length-1] == '*') {
             cmp.length--;
-            return sstrprefix(ctype, cmp);
+            return cx_strprefix(ctype, cmp);
         } else {
-            return !sstrcmp(cmp, ctype);
+            return !cx_strcmp(cmp, ctype);
         }
     } else if(cmp.ptr[0] == 0) {
         log_ereport(LOG_WARN, "Skipped service saf with empty type parameter");
         return 0;
     }
     
-    cmp = sstrsubsl(cmp, 1, cmp.length - 2);
+    cmp = cx_strsubsl(cmp, 1, cmp.length - 2);
     
     int begin = 0;
     for(int i=0;i<cmp.length;i++) {
         if(cmp.ptr[i] == '|') {
-            if(contenttype_match(sstrsubsl(cmp, begin, i-begin), ctype)) {
+            if(contenttype_match(cx_strsubsl(cmp, begin, i-begin), ctype)) {
                 return 1;
             }
             begin = i + 1;
         }
     }
-    return contenttype_match(sstrsubs(cmp, begin), ctype);
+    return contenttype_match(cx_strsubs(cmp, begin), ctype);
 }
 
 /*
--- a/src/server/daemon/httprequest.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/httprequest.h	Sun Nov 06 15:53:32 2022 +0100
@@ -29,7 +29,7 @@
 #ifndef HTTPREQUEST_H
 #define	HTTPREQUEST_H
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "sessionhandler.h"
 #include "../public/nsapi.h"
@@ -47,18 +47,18 @@
 
 struct _http_request {
     Connection     *connection;
-    sstr_t         request_line;
-    sstr_t         method;
-    sstr_t         uri;
-    sstr_t         httpv;
+    cxmutstr         request_line;
+    cxmutstr         method;
+    cxmutstr         uri;
+    cxmutstr         httpv;
     HeaderArray    *headers;
     netbuf         *netbuf;
     time_t         req_start;
 };
 
 struct _header {
-    sstr_t name;
-    sstr_t value;
+    cxmutstr name;
+    cxmutstr value;
 };
 
 struct _header_array {
@@ -71,7 +71,7 @@
 void http_request_init(HTTPRequest *req);
 void http_request_cleanup(HTTPRequest *req);
 
-sstr_t http_request_get_abspath(HTTPRequest *req);
+cxmutstr http_request_get_abspath(HTTPRequest *req);
 
 
 NSAPISession* nsapisession_create(pool_handle_t *pool);
@@ -87,7 +87,7 @@
 
 
 
-void header_add(HeaderArray *hd, sstr_t name, sstr_t value);
+void header_add(HeaderArray *hd, cxmutstr name, cxmutstr value);
 void header_array_free(HeaderArray *hd);
 
 int nsapi_handle_request(NSAPISession *sn, NSAPIRequest *rq);
@@ -132,7 +132,7 @@
         char *path);
 
 int method_match(char *cmp, char *method);
-int contenttype_match(sstr_t cmp, sstr_t ctype);
+int contenttype_match(cxstring cmp, cxstring ctype);
 
 /* request.h functions */
 int request_initialize(
--- a/src/server/daemon/keyfile_auth.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/keyfile_auth.c	Sun Nov 06 15:53:32 2022 +0100
@@ -41,28 +41,30 @@
 #include "../util/atomic.h"
 #include "../util/util.h"
 
+#include <cx/hash_map.h>
+
 #include "keyfile_auth.h"
 
-Keyfile* keyfile_new(UcxAllocator *a) {
-    Keyfile *keyfile = alcalloc(a, 1, sizeof(Keyfile));
+Keyfile* keyfile_new(CxAllocator *a) {
+    Keyfile *keyfile = cxCalloc(a, 1, sizeof(Keyfile));
     if(!keyfile) {
         return NULL;
     }
     keyfile->authdb.get_user = keyfile_get_user;
     keyfile->authdb.use_cache = 0;
-    keyfile->users = ucx_map_new_a(a, 16);
+    keyfile->users = cxHashMapCreate(a, 16);
     return keyfile;
 }
 
 int keyfile_add_user(
         Keyfile *keyfile,
-        sstr_t name,
+        cxmutstr name,
         enum KeyfileHashType hash_type,
-        sstr_t hash,
-        sstr_t *groups,
+        cxmutstr hash,
+        cxmutstr *groups,
         size_t ngroups)
 {
-    UcxAllocator *a = keyfile->users->allocator;
+    CxAllocator *a = keyfile->users->allocator;
     
     if(hash.length < 12) {
         // hash too short
@@ -70,8 +72,8 @@
         return -1;
     }
     
-    KeyfileUser *user = almalloc(a, sizeof(KeyfileUser));
-    user->user.name = sstrdup_a(a, name).ptr;
+    KeyfileUser *user = cxMalloc(a, sizeof(KeyfileUser));
+    user->user.name = cx_strdup_a(a, cx_strcast(name)).ptr;
     user->user.uid = -1;
     user->user.gid = -1;
     user->user.verify_password = keyfile_user_verify_password;
@@ -79,7 +81,7 @@
     user->user.free = keyfile_user_free;
     
     user->hash_type = hash_type;
-    user->hash = almalloc(a, hash.length + 1);
+    user->hash = cxMalloc(a, hash.length + 1);
     
     if(!user->user.name || !user->hash) {
         return -1;
@@ -88,12 +90,12 @@
     user->hashlen = util_base64decode(hash.ptr, hash.length, user->hash);
     
     if(ngroups > 0) {
-        user->groups = alcalloc(a, ngroups, sizeof(sstr_t));
+        user->groups = cxCalloc(a, ngroups, sizeof(cxmutstr));
         if(!user->groups) {
             return -1;
         }
         for(int i=0;i<ngroups;i++) {
-            user->groups[i] = sstrdup_a(a, groups[i]);
+            user->groups[i] = cx_strdup_a(a, cx_strcast(groups[i]));
         }
         
     } else {
@@ -101,26 +103,26 @@
     }
     
     // add to keyfile
-    return ucx_map_sstr_put(keyfile->users, name, user);
+    return cxMapPut(keyfile->users, cx_hash_key(name.ptr, name.length), user);
 }
 
 // authdb functions
 
-User* keyfile_get_user(AuthDB *db, char *user) {
+User* keyfile_get_user(AuthDB *db, const char *user) {
     Keyfile *keyfile = (Keyfile*)db;
-    return ucx_map_cstr_get(keyfile->users, user);
+    return cxMapGet(keyfile->users, cx_hash_key_str(user));
 }
 
-int keyfile_user_verify_password(User *user, char *password) {
+int keyfile_user_verify_password(User *user, const char *password) {
     KeyfileUser *usr = (KeyfileUser*)user;
     return ssha_verify(usr, password);
 }
 
-int keyfile_user_check_group(User *user, char *group) {
+int keyfile_user_check_group(User *user, const char *group) {
     KeyfileUser *usr = (KeyfileUser*)user;
-    sstr_t grp = sstr(group);
+    cxstring grp = cx_str(group);
     for(int i=0;i<usr->numgroups;i++) {
-        if(!sstrcmp(usr->groups[i], grp)) {
+        if(!cx_strcmp(cx_strcast(usr->groups[i]), grp)) {
             return 1;
         }
     }
@@ -132,7 +134,7 @@
 }
 
 
-int ssha_verify(KeyfileUser *user, char *password) {
+int ssha_verify(KeyfileUser *user, const char *password) {
     /*
      * SSHA: SHA(pw + salt) + salt
      * user->hash is already base64 decoded
--- a/src/server/daemon/keyfile_auth.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/keyfile_auth.h	Sun Nov 06 15:53:32 2022 +0100
@@ -31,7 +31,7 @@
 
 #include "../public/auth.h"
 
-#include <ucx/map.h>
+#include <cx/map.h>
 #include <inttypes.h>
 
 #ifdef	__cplusplus
@@ -49,35 +49,35 @@
 
 struct keyfile {
     AuthDB       authdb;
-    UcxMap       *users;
+    CxMap       *users;
 };
 
 struct keyfile_user {
     User                 user;
-    sstr_t               *groups;
+    cxmutstr             *groups;
     size_t               numgroups;
     enum KeyfileHashType hash_type;
     char                 *hash;
     size_t               hashlen;
 };
 
-Keyfile* keyfile_new(UcxAllocator *a);
+Keyfile* keyfile_new(CxAllocator *a);
 
 int keyfile_add_user(
         Keyfile *keyfile,
-        sstr_t user,
+        cxmutstr user,
         enum KeyfileHashType hash_type,
-        sstr_t hash,
-        sstr_t *groups,
+        cxmutstr hash,
+        cxmutstr *groups,
         size_t ngroups);
 
-User* keyfile_get_user(AuthDB *db, char *user);
-int keyfile_user_verify_password(User *user, char *password);
-int keyfile_user_check_group(User *user, char *group);
+User* keyfile_get_user(AuthDB *db, const char *user);
+int keyfile_user_verify_password(User *user, const char *password);
+int keyfile_user_check_group(User *user, const char *group);
 void keyfile_user_free(User *user);
 
 // SSHA password compare
-int ssha_verify(KeyfileUser *user, char *password);
+int ssha_verify(KeyfileUser *user, const char *password);
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/ldap_auth.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/ldap_auth.c	Sun Nov 06 15:53:32 2022 +0100
@@ -35,7 +35,8 @@
 #include <string.h>
 #include <sys/time.h>
 
-#include <ucx/utils.h>
+#include <cx/utils.h>
+#include <cx/hash_map.h>
 
 #include "ldap_auth.h"
 
@@ -48,7 +49,7 @@
 }
 
 AuthDB* create_ldap_authdb(ServerConfiguration *cfg, const char *name, LDAPConfig *conf) {
-    LDAPAuthDB *authdb = almalloc(cfg->a, sizeof(LDAPAuthDB));
+    LDAPAuthDB *authdb = cxMalloc(cfg->a, sizeof(LDAPAuthDB));
     authdb->authdb.name = pool_strdup(cfg->pool, name);
     authdb->authdb.get_user = ldap_get_user;
     authdb->authdb.use_cache = 1;
@@ -64,7 +65,7 @@
     // initialize group cache
     authdb->groups.first = NULL;
     authdb->groups.last = NULL;
-    authdb->groups.map = ucx_map_new_a(cfg->a, 32);
+    authdb->groups.map = cxHashMapCreate(cfg->a, 32);
 
     return (AuthDB*) authdb;
 }
@@ -113,7 +114,7 @@
     return ld;
 }
 
-User* ldap_get_user(AuthDB *db, char *username) {
+User* ldap_get_user(AuthDB *db, const char *username) {
     LDAPAuthDB *authdb = (LDAPAuthDB*) db;
     LDAPConfig *config = &authdb->config;
 
@@ -161,7 +162,7 @@
             user->user.verify_password = ldap_user_verify_password;
             user->user.check_group = ldap_user_check_group;
             user->user.free = ldap_user_free;
-            user->user.name = username; // must not be freed
+            user->user.name = (char*)username; // must not be freed TODO: maybe copy
             
             // TODO: get uid/gid from ldap
             user->user.uid = -1;
@@ -180,7 +181,7 @@
     return NULL;
 }
 
-LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, char *group) {
+LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, const char *group) {
     printf("ldap_get_group: %s\n", group);
     
     LDAPConfig *config = &authdb->config;
@@ -246,10 +247,10 @@
                     wsgroup->members = calloc(count, sizeof(LDAPMember));
                     wsgroup->nmembers = count;
                     for(int i=0;i<count;i++) {
-                        sstr_t member = sstrn(
+                        cxstring member = cx_strn(
                                 values[i]->bv_val,
                                 values[i]->bv_len);
-                        wsgroup->members[i].name = sstrdup(member).ptr;
+                        wsgroup->members[i].name = cx_strdup(member).ptr;
                         // TODO: uid?
                         printf("added member: %.*s\n", (int)member.length, member.ptr);
                     }
@@ -271,12 +272,12 @@
     return wsgroup;
 }
 
-int ldap_user_verify_password(User *u, char *password) {
+int ldap_user_verify_password(User *u, const char *password) {
     LDAPUser *user = (LDAPUser*)u;
     
     //int r = ldap_simple_bind_s(user->ldap, user->userdn, password);
     struct berval cred;
-    cred.bv_val = password;
+    cred.bv_val = (char*)password;
     cred.bv_len = strlen(password);
     struct berval *server_cred;
     int r = ldap_sasl_bind_s(
@@ -296,7 +297,7 @@
     }
 }
 
-int ldap_user_check_group(User *u, char *group_str) {
+int ldap_user_check_group(User *u, const char *group_str) {
     LDAPUser *user = (LDAPUser*)u;
     
     int ret = 0;
--- a/src/server/daemon/ldap_auth.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/ldap_auth.h	Sun Nov 06 15:53:32 2022 +0100
@@ -32,7 +32,7 @@
 #include "../public/auth.h"
 #include <sys/types.h>
 #include <ldap.h>
-#include <ucx/map.h>
+#include <cx/map.h>
 
 #include "config.h"
 
@@ -61,7 +61,7 @@
 struct ldap_group_cache {
     LDAPGroup *first;
     LDAPGroup *last;
-    UcxMap    *map;
+    CxMap    *map;
 };
 
 struct ldap_auth_db {
@@ -96,12 +96,12 @@
 
 LDAP* get_ldap_session(LDAPAuthDB *authdb);
 
-User* ldap_get_user(AuthDB *sb, char *username);
+User* ldap_get_user(AuthDB *sb, const char *username);
 
-LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, char *group);
+LDAPGroup* ldap_get_group(LDAPAuthDB *authdb, const char *group);
 
-int ldap_user_verify_password(User *user, char *password);
-int ldap_user_check_group(User *user, char *group);
+int ldap_user_verify_password(User *user, const char *password);
+int ldap_user_check_group(User *user, const char *group);
 void ldap_user_free(User *user);
 
 #ifdef	__cplusplus
--- a/src/server/daemon/log.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/log.c	Sun Nov 06 15:53:32 2022 +0100
@@ -43,8 +43,9 @@
 #include "../util/io.h"
 #include "../util/atomic.h"
 
-#include <ucx/map.h>
-#include <ucx/list.h>
+#include <cx/hash_map.h>
+#include <cx/linked_list.h>
+#include <cx/compare.h>
 
 static int is_initialized = 0;
 
@@ -52,7 +53,7 @@
 static int log_level = 0;
 
 static uint32_t log_dup_count = 0;
-static UcxList *log_dup_list = NULL;
+static CxList *log_dup_list = NULL;
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
 
 WSBool main_is_daemon(void);
@@ -65,7 +66,7 @@
 /*
  * access log file map
  */
-static UcxMap *access_log_files; // map of LogFile*
+static CxMap *access_log_files; // map of LogFile*
 
 
 static char *log_date_month[] = {
@@ -110,6 +111,8 @@
         return 0;
     }
     
+    log_dup_list = cxPointerLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr);
+    
     /* open the log file */
     mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
     log_file_fd = open(cfg->file, O_WRONLY | O_CREAT | O_APPEND, mode);
@@ -165,7 +168,7 @@
         }
     }
     
-    sstr_t s;
+    cxstring s;
     s.ptr = str;
     s.length = len;
     
@@ -195,8 +198,8 @@
         msg[len] = '\n';
         
         pthread_mutex_lock(&mutex);
-        UCX_FOREACH(elm, log_dup_list) {
-            LogDup *dup = elm->data;
+        CxIterator i = cxListIterator(log_dup_list, 0);
+        cx_foreach(LogDup *, dup, i) {
             dup->write(dup->cookie, msg, len + 1);
         }        
         pthread_mutex_unlock(&mutex);
@@ -205,10 +208,10 @@
     }
 }
 
-sstr_t log_get_prefix(int level) {
+cxmutstr log_get_prefix(int level) {
     time_t t = time(NULL);
     
-    sstr_t d;
+    cxmutstr d;
     d.ptr = NULL;
     d.length = 0;
     
@@ -238,21 +241,22 @@
 
 void log_add_logdup(LogDup *dup) {
     pthread_mutex_lock(&mutex);  
-    log_dup_list = ucx_list_append(log_dup_list, dup);
+    cxListAdd(log_dup_list, dup);
     ws_atomic_inc32(&log_dup_count);   
     pthread_mutex_unlock(&mutex);
 }
 
 void log_remove_logdup(LogDup *ldup) {
     pthread_mutex_lock(&mutex);
-    UcxList *elm = log_dup_list;
-    while(elm) {
-        if(elm->data == ldup) {
-            log_dup_list = ucx_list_remove(log_dup_list, elm);
+    CxIterator i = cxListIterator(log_dup_list, 0);
+    WSBool finished = 0;
+    cx_foreach(LogDup *, dup, i) {
+        if(finished) break;
+        if(dup == ldup) {
+            i.remove = 1;
+            finished = 1;
             ws_atomic_dec32(&log_dup_count);
-            break;
         }
-        elm = elm->next;
     }
     pthread_mutex_unlock(&mutex);
 }
@@ -278,18 +282,18 @@
         return 0;
     }
     
-    sstr_t lmsg;
+    cxmutstr lmsg;
     lmsg.ptr = NULL;
     
     /* create log message prefix */
-    sstr_t lpre = log_get_prefix(degree); 
+    cxmutstr lpre = log_get_prefix(degree); 
     
     /* format message */
     int len = vasprintf(&lmsg.ptr, format, args);
     lmsg.length = len;
     
     /* create message string */
-    sstr_t message = sstrcat(2, lpre, lmsg);
+    cxmutstr message = cx_strcat(2, lpre, lmsg);
     
     /* write message to the log file */ 
     log_file_writeln(message.ptr, message.length);
@@ -336,18 +340,19 @@
  * This source file only manages access log files. IO is performed directly
  * by AddLog safs. 
  */
-LogFile* get_access_log_file(scstr_t file) {
+LogFile* get_access_log_file(cxstring file) {
     // TODO: this looks dubious
     
     if(!access_log_files) {
-        access_log_files = ucx_map_new(4);
+        access_log_files = cxHashMapCreate(cxDefaultAllocator, 4);
     }
     
     if(file.ptr == NULL || file.length == 0) {
         return NULL;
     }
     
-    LogFile *log = ucx_map_sstr_get(access_log_files, file);
+    CxHashKey key = cx_hash_key_bytes((unsigned const char*)file.ptr, file.length);
+    LogFile *log = cxMapGet(access_log_files, key);
     if(log != NULL) {
         ws_atomic_inc32(&log->ref);
         return log;
@@ -366,7 +371,7 @@
     log->ref = 1;
     
     // add access log to the map
-    ucx_map_sstr_put(access_log_files, file, log);
+    cxMapPut(access_log_files, key, log);
     
     return log;
 }
--- a/src/server/daemon/log.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/log.h	Sun Nov 06 15:53:32 2022 +0100
@@ -30,8 +30,8 @@
 #define	LOG_H
 
 #include "../public/nsapi.h"
-#include <ucx/string.h>
-#include <ucx/utils.h>
+#include <cx/string.h>
+#include <cx/utils.h>
 
 #include <inttypes.h>
 
@@ -52,8 +52,8 @@
 } LogFile;
 
 typedef struct {
-    sstr_t   file;
-    sstr_t   format; // unused
+    cxmutstr   file;
+    cxmutstr   format; // unused
     LogFile  *log;
 } AccessLog;
 
@@ -69,13 +69,13 @@
 
 void log_file_writeln(char *str, size_t len);
 
-sstr_t log_get_prefix(int level);
+cxmutstr log_get_prefix(int level);
 
 void log_add_logdup(LogDup *dup);
 void log_remove_logdup(LogDup *dup);
 
 // access logging
-LogFile* get_access_log_file(scstr_t file);
+LogFile* get_access_log_file(cxstring file);
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/main.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/main.c	Sun Nov 06 15:53:32 2022 +0100
@@ -39,7 +39,7 @@
 #include "../util/plist.h"
 #include "../util/date.h"
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "webserver.h"
 #include "log.h"
@@ -55,7 +55,7 @@
 void test() {
     time_t t = time(NULL);
     pool_handle_t *pool = pool_create();
-    sstr_t date = date_format_http(t, pool);
+    cxmutstr date = date_format_http(t, pool);
     printf("%s\n", date.ptr);
 }
 
@@ -173,7 +173,7 @@
     int status;
     status = webserver_init();
     if(status != 0) {
-        log_ereport(LOG_FAILURE, "Cannot initialize server.");
+        log_ereport(LOG_FAILURE, "cannot initialize server.");
         return EXIT_FAILURE;
     }
 
--- a/src/server/daemon/protocol.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/protocol.c	Sun Nov 06 15:53:32 2022 +0100
@@ -58,7 +58,7 @@
 
 /*
  * http_status_message from Open Webserver (frame/http.cpp)
- * TODO: replace, use sstr_t
+ * TODO: replace, use cxmutstr
  */
 NSAPI_PUBLIC const char * protocol_status_message (int code)
 {
@@ -379,7 +379,7 @@
 
 int http_send_continue(Session *sn) {
     NSAPISession *s = (NSAPISession*)sn;
-    sstr_t msg = S("HTTP/1.1 100 Continue\r\n\r\n");
+    cxstring msg = CX_STR("HTTP/1.1 100 Continue\r\n\r\n");
     int w = s->connection->write(s->connection, msg.ptr, msg.length);
     if(w != msg.length) {
         return 1;
--- a/src/server/daemon/protocol.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/protocol.h	Sun Nov 06 15:53:32 2022 +0100
@@ -57,7 +57,7 @@
         char **host,
         uint16_t *port);
 
-#define sbuf_write(out, buf, len) sbuf_append(out, sstrn(buf, len))
+#define sbuf_write(out, buf, len) sbuf_append(out, cx_strn(buf, len))
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/request.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/request.c	Sun Nov 06 15:53:32 2022 +0100
@@ -127,7 +127,7 @@
     return s;
 }
 
-int request_set_path(sstr_t root, sstr_t path, pblock *vars) {
+int request_set_path(cxstring root, cxstring path, pblock *vars) {
     // TODO: maybe replace this code with request_set_path from req.cpp
     
     // concat path
--- a/src/server/daemon/request.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/request.h	Sun Nov 06 15:53:32 2022 +0100
@@ -32,7 +32,7 @@
 #include "../public/nsapi.h"
 #include "../util/object.h"
 
-#include <ucx/map.h>
+#include <cx/map.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -48,7 +48,7 @@
     uint16_t       port;
     NSAPIContext   context;
     void           *jvm_context;
-    UcxMap         *resources;
+    CxMap          *resources;
 };
 
 /* macros for context access */
@@ -57,7 +57,7 @@
 
 #define REQ_HASHSIZE 10
 
-int request_set_path(sstr_t root, sstr_t path, pblock *vars);
+int request_set_path(cxstring root, cxstring path, pblock *vars);
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/resourcepool.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/resourcepool.c	Sun Nov 06 15:53:32 2022 +0100
@@ -36,15 +36,15 @@
 
 #define RESOURCE_POOL_MAX_ALLOC 268435455
 
-static UcxMap *resource_pool_types;
+static CxMap *resource_pool_types;
 
 int init_resource_pools(void) {
-    resource_pool_types = ucx_map_new(4);
+    resource_pool_types = cxHashMapCreate(cxDefaultAllocator, 4);
     return resource_pool_types ? 0 : 1;
 }
 
 int resourcepool_register_type(const char *type_name, ResourceType *type_info) {
-    if(ucx_map_cstr_put(resource_pool_types, type_name, type_info)) {
+    if(cxMapPut(resource_pool_types, cx_hash_key_str(type_name), type_info)) {
         log_ereport(LOG_CATASTROPHE, "resourcepool_register_type: OOM");
         return 1;
     }
@@ -53,10 +53,10 @@
 
 
 
-int resourcepool_new(ServerConfiguration *cfg, scstr_t type, scstr_t name, ConfigNode *node) {
-    ResourceType *restype = ucx_map_sstr_get(resource_pool_types, type);
+int resourcepool_new(ServerConfiguration *cfg, cxstring type, cxstring name, ConfigNode *node) {
+    ResourceType *restype = cxMapGet(resource_pool_types, cx_hash_key_bytes((unsigned const char*)type.ptr, type.length));
     if(!restype) {
-        log_ereport(LOG_MISCONFIG, "Unknown resource pool type: %s", type.ptr);
+        log_ereport(LOG_MISCONFIG, "unknown resource pool type: %s", type.ptr);
         return 1;
     }
     
@@ -101,7 +101,7 @@
     respool->resalloc = respool->max;
     respool->resources = pool_malloc(cfg->pool, respool->resalloc * sizeof(ResourceDataPrivate*));
     
-    if(!respool->resources || ucx_map_sstr_put(cfg->resources, name, respool)) {
+    if(!respool->resources || cxMapPut(cfg->resources, cx_hash_key_bytes((unsigned const char*)name.ptr, name.length), respool)) {
         log_ereport(LOG_FAILURE, "Cannot add resource pool: OOM");
         // the only cleanup we have to do
         restype->destroy(respool_data);
@@ -121,13 +121,13 @@
     
     // was this resource already used by this request?
     if(request && request->resources) {
-        resource = ucx_map_cstr_get(request->resources, name);
+        resource = cxMapGet(request->resources, cx_hash_key_str(name));
         if(resource) {
             return &resource->data;
         }
     }
     
-    ResourcePool *respool = ucx_map_cstr_get(cfg->resources, name);
+    ResourcePool *respool = cxMapGet(cfg->resources, cx_hash_key_str(name));
     if(!respool) return NULL;
 
     
@@ -170,11 +170,11 @@
     if(resource) {
         if(request && session) {
             if(!request->resources) {
-                request->resources = ucx_map_new_a(&session->allocator, 8);
+                request->resources = cxHashMapCreate(pool_allocator(session->sn.pool), 8);
             }
 
             if(request->resources) {
-                if(ucx_map_cstr_put(request->resources, name, resource)) {
+                if(cxMapPut(request->resources, cx_hash_key_str(name), resource)) {
                     err = 1;
                 }
             } else {
--- a/src/server/daemon/resourcepool.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/resourcepool.h	Sun Nov 06 15:53:32 2022 +0100
@@ -111,7 +111,7 @@
 
 int init_resource_pools(void);
 
-int resourcepool_new(ServerConfiguration *cfg, scstr_t type, scstr_t name, ConfigNode *node);
+int resourcepool_new(ServerConfiguration *cfg, cxstring type, cxstring name, ConfigNode *node);
 
 void resourcepool_destroy_resource(ResourceDataPrivate *res);
 
--- a/src/server/daemon/session.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/session.c	Sun Nov 06 15:53:32 2022 +0100
@@ -41,7 +41,3 @@
     return sn->config;
 }
 
-NSAPI_PUBLIC void* session_get_allocator(Session *sn) {
-    return &((NSAPISession*)sn)->allocator;
-}
-
--- a/src/server/daemon/session.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/session.h	Sun Nov 06 15:53:32 2022 +0100
@@ -51,8 +51,6 @@
     int pos;
     int cursize;
     
-    UcxAllocator allocator;
-    
     ServerConfiguration *config;
 };
 
@@ -63,7 +61,6 @@
 // get the server configuration of this session
 NSAPI_PUBLIC void* session_get_config(Session *s);
 
-NSAPI_PUBLIC void* session_get_allocator(Session *sn);
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/sessionhandler.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/sessionhandler.c	Sun Nov 06 15:53:32 2022 +0100
@@ -117,7 +117,7 @@
         io = sslstream_new(pool, conn->ssl);
         *ssl = 1;
     } else {
-        io = sysstream_new(pool, conn->fd);
+        io = sycx_stream_new(pool, conn->fd);
         *ssl = 0;
     }
     return io;
--- a/src/server/daemon/srvctrl.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/srvctrl.c	Sun Nov 06 15:53:32 2022 +0100
@@ -37,22 +37,23 @@
 
 #include "../util/systhr.h"
 
-#include <ucx/utils.h>
-#include <ucx/buffer.h>
+#include <cx/utils.h>
+#include <cx/buffer.h>
+#include <cx/printf.h>
 
 #include "srvctrl.h"
 
 #define SRVCTRL_THREAD_STACKSIZE 8192
 
 static int srvctrl;
-static sstr_t srvctrl_path;
+static cxmutstr srvctrl_path;
 
 static WSBool srv_shutdown;
 
 int srvctrl_init(ServerConfiguration *cfg) {
     // create srvctrl unix domain socket
     // this socket is used for stop, reconfigure and other operations
-    srvctrl_path = ucx_sprintf("%s/private/srvctrl.sock", cfg->tmp.ptr);
+    srvctrl_path = cx_asprintf("%s/private/srvctrl.sock", cfg->tmp.ptr);
     struct sockaddr_un addr;
     if(srvctrl_path.length > sizeof(addr.sun_path)-1) {
         log_ereport(
@@ -148,7 +149,8 @@
     log_add_logdup(&log);
     
     char buf[64];
-    UcxBuffer *line = ucx_buffer_new(NULL, 32, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer line;
+    cxBufferInit(&line, NULL, 32, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
     
     ssize_t r;
     WSBool br = FALSE;
@@ -156,13 +158,13 @@
         for(int i=0;i<r;i++) {
             char c = buf[i];
             if(c == '\n') {
-                sstr_t ln = sstrn(line->space, line->pos);
+                cxmutstr ln = cx_mutstrn(line.space, line.pos);
                 if(srvctrl_handle_cmd(client, ln)) {
                     br = TRUE;
                     break;
                 }
             } else {
-                ucx_buffer_putc(line, c);
+                cxBufferPut(&line, c);
             }
         }
         if(br) {
@@ -171,25 +173,25 @@
     }
     log_remove_logdup(&log);
     
-    ucx_buffer_free(line);
+    cxBufferDestroy(&line);
     close(client->fd);
     free(client);
     return NULL;
 }
 
-int srvctrl_handle_cmd(SrvCtrlClient *client, sstr_t cmd) {
-    if(!sstrcmp(cmd, S("reconfig"))) {
+int srvctrl_handle_cmd(SrvCtrlClient *client, cxmutstr cmd) {
+    if(!cx_strcmp(cx_strcast(cmd), cx_str("reconfig"))) {
         log_ereport(LOG_INFORM, "reconfigure server");
         if(webserver_reconfig()) {
             log_ereport(LOG_FAILURE, "cannot reload config");
         } else {
             log_ereport(LOG_INFORM, "reconfig: success");
         }
-    } else if(!sstrcmp(cmd, S("shutdown"))) {
+    } else if(!cx_strcmp(cx_strcast(cmd), cx_str("shutdown"))) {
         webserver_shutdown();
-    } else if(!sstrcmp(cmd, S("stat"))) {
+    } else if(!cx_strcmp(cx_strcast(cmd), cx_str("stat"))) {
         // TODO: implement
-    } else if(!sstrcmp(cmd, S("log"))) {
+    } else if(!cx_strcmp(cx_strcast(cmd), cx_str("log"))) {
         return 0;
     } else {
         log_ereport(
--- a/src/server/daemon/srvctrl.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/srvctrl.h	Sun Nov 06 15:53:32 2022 +0100
@@ -31,7 +31,7 @@
 
 #include "../public/nsapi.h"
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "config.h"
 
@@ -55,7 +55,7 @@
 SrvCtrlClient* srvctrl_create_client(int fd);
 
 void* srvctrl_thread(SrvCtrlClient *client);
-int srvctrl_handle_cmd(SrvCtrlClient *client, sstr_t cmd);
+int srvctrl_handle_cmd(SrvCtrlClient *client, cxmutstr cmd);
 void srvctrl_log(SrvCtrlClient *client, char *msg, size_t len);
 
 
--- a/src/server/daemon/threadpools.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/threadpools.c	Sun Nov 06 15:53:32 2022 +0100
@@ -29,14 +29,14 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <ucx/map.h>
+#include <cx/hash_map.h>
 
 #include "threadpools.h"
 
 
-static UcxMap *thread_pool_map;
+static CxMap *thread_pool_map;
 static int num_thrpools;
-static UcxMap *io_pool_map;
+static CxMap *io_pool_map;
 static int num_iopools;
 
 static threadpool_t *default_thread_pool;
@@ -45,12 +45,13 @@
 static threadpool_t *default_io_pool;
 static threadpool_t *last_io_pool;
 
-int create_threadpool(scstr_t name, ThreadPoolConfig *cfg) { 
+int create_threadpool(cxstring name, ThreadPoolConfig *cfg) { 
     if(thread_pool_map == NULL) {
-        thread_pool_map = ucx_map_new(16);
+        thread_pool_map = cxHashMapCreate(cxDefaultAllocator, 16);
     }
     
-    threadpool_t *pool = ucx_map_sstr_get(thread_pool_map, name);
+    CxHashKey key = cx_hash_key_bytes((const unsigned char*)name.ptr, name.length);
+    threadpool_t *pool = cxMapGet(thread_pool_map, key);
     if(pool) {
         if(pool->min_threads > cfg->max_threads) {
             pool->min_threads = cfg->min_threads;
@@ -65,7 +66,7 @@
         
         int ret = 0;
         if(!threadpool_start(tp)) {
-            ret = ucx_map_sstr_put(thread_pool_map, name, tp);
+            ret = cxMapPut(thread_pool_map, key, tp);
         } else {
             ret = 1;
         }
@@ -82,11 +83,12 @@
     }
 }
 
-int create_io_pool(scstr_t name, int numthreads) {
+int create_io_pool(cxstring name, int numthreads) {
     if(io_pool_map == NULL) {
-        io_pool_map = ucx_map_new(4);
+        io_pool_map = cxHashMapCreate(cxDefaultAllocator, 4);
     }
-    threadpool_t *pool = ucx_map_sstr_get(io_pool_map, name);
+    CxHashKey key = cx_hash_key_bytes((const unsigned char*)name.ptr, name.length);
+    threadpool_t *pool = cxMapGet(io_pool_map, key);
     if(pool) {
         pool->min_threads = numthreads;
         pool->max_threads = numthreads;
@@ -94,7 +96,7 @@
     } else {
         threadpool_t *tp = threadpool_new(numthreads, numthreads);
         
-        int ret = ucx_map_sstr_put(io_pool_map, name, tp);
+        int ret = cxMapPut(io_pool_map, key, tp);
         
         if(ret == 0) {
             num_iopools++;
@@ -115,13 +117,13 @@
         cfg.max_threads = 8;
         cfg.queue_size = 64;
         cfg.stack_size = 262144;
-        if(create_threadpool(SC("default"), &cfg)) {
+        if(create_threadpool(cx_str("default"), &cfg)) {
             return 1;
         }
     }
     
     if(num_iopools == 0) {
-        if(create_io_pool(SC("default"), 8)) {
+        if(create_io_pool(cx_str("default"), 8)) {
             return 1;
         }
     }
@@ -133,14 +135,14 @@
     return default_thread_pool;
 }
 
-threadpool_t* get_threadpool(sstr_t name) {
-    return ucx_map_sstr_get(thread_pool_map, name);
+threadpool_t* get_threadpool(cxstring name) {
+    return cxMapGet(thread_pool_map, cx_hash_key_bytes((const unsigned char*)name.ptr, name.length));
 }
 
 threadpool_t* get_default_iopool() {
     return default_io_pool;
 }
 
-threadpool_t* get_iopool(sstr_t name) {
-    return ucx_map_sstr_get(io_pool_map, name);
+threadpool_t* get_iopool(cxstring name) {
+    return cxMapGet(io_pool_map, cx_hash_key_bytes((const unsigned char*)name.ptr, name.length));
 }
--- a/src/server/daemon/threadpools.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/threadpools.h	Sun Nov 06 15:53:32 2022 +0100
@@ -29,7 +29,7 @@
 #ifndef THREADPOOLS_H
 #define	THREADPOOLS_H
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "../public/nsapi.h"
 #include "../util/thrpool.h"
@@ -45,14 +45,14 @@
     int queue_size;
 } ThreadPoolConfig;
 
-int create_threadpool(scstr_t name, ThreadPoolConfig *cfg);
+int create_threadpool(cxstring name, ThreadPoolConfig *cfg);
 
 int check_thread_pool_cfg();
 
 threadpool_t* get_default_threadpool();
-threadpool_t* get_threadpool(sstr_t name);
+threadpool_t* get_threadpool(cxstring name);
 threadpool_t* get_default_iopool();
-threadpool_t* get_iopool(sstr_t name);
+threadpool_t* get_iopool(cxstring name);
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/vfs.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/vfs.c	Sun Nov 06 15:53:32 2022 +0100
@@ -33,7 +33,7 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <aio.h>
-#include <ucx/map.h>
+#include <cx/hash_map.h>
 
 #include "../util/pool.h"
 #include "netsite.h"
@@ -45,7 +45,7 @@
 #define VFS_MALLOC(pool, size) pool ? pool_malloc(pool, size) : malloc(size)
 #define VFS_FREE(pool, ptr) pool ? pool_free(pool, ptr) : free(ptr)
 
-static UcxMap *vfs_type_map;
+static CxMap *vfs_type_map;
 
 static VFS sys_vfs = {
     sys_vfs_open,
@@ -80,7 +80,7 @@
 };
 
 int vfs_init(void) {
-    vfs_type_map = ucx_map_new(16);
+    vfs_type_map = cxHashMapCreate(cxDefaultAllocator, 16);
     if(!vfs_type_map) {
         return -1;
     }
@@ -103,11 +103,11 @@
     vfsType->init = vfsInit;
     vfsType->create = vfsCreate;
     
-    return ucx_map_cstr_put(vfs_type_map, name, vfsType);
+    return cxMapPut(vfs_type_map, cx_hash_key_str(name), vfsType);
 }
 
-VfsType* vfs_get_type(scstr_t vfs_class) {
-    return ucx_map_sstr_get(vfs_type_map, vfs_class);
+VfsType* vfs_get_type(cxstring vfs_class) {
+    return cxMapGet(vfs_type_map, cx_hash_key_bytes((const unsigned char*)vfs_class.ptr, vfs_class.length));
 }
 
 void* vfs_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, VfsType *vfs_class, WSConfigNode *config, int *error) {
@@ -124,7 +124,7 @@
 }
 
 VFS* vfs_create(Session *sn, Request *rq, const char *vfs_class, pblock *pb, void *initData) {
-    VfsType *vfsType = ucx_map_cstr_get(vfs_type_map, vfs_class);
+    VfsType *vfsType = cxMapGet(vfs_type_map, cx_hash_key_str(vfs_class));
     if(!vfsType) {
         log_ereport(LOG_MISCONFIG, "vfs_create: unkown VFS type %s", vfs_class);
         return NULL;
--- a/src/server/daemon/vfs.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/vfs.h	Sun Nov 06 15:53:32 2022 +0100
@@ -32,7 +32,7 @@
 #include "../public/vfs.h"
 #include "acl.h"
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -55,7 +55,7 @@
 typedef enum VFSAioOp VFSAioOp;
     
 int vfs_init(void);
-VfsType* vfs_get_type(scstr_t vfs_class);
+VfsType* vfs_get_type(cxstring vfs_class);
 
 void* vfs_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, VfsType *vfs_class, WSConfigNode *config, int *error);
 
--- a/src/server/daemon/vserver.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/vserver.c	Sun Nov 06 15:53:32 2022 +0100
@@ -31,7 +31,7 @@
 VirtualServer* vs_new() {
     VirtualServer *vs = malloc(sizeof(VirtualServer));
     vs->objects = NULL;
-    vs->document_root = sstr("docs");
+    vs->document_root = cx_mutstr("docs");
     vs->acls = NULL;
     vs->log = NULL;
     vs->ref = 1;
--- a/src/server/daemon/vserver.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/vserver.h	Sun Nov 06 15:53:32 2022 +0100
@@ -29,7 +29,7 @@
 #ifndef VSERVER_H
 #define	VSERVER_H
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "../util/object.h"
 #include "../public/nsapi.h"
@@ -41,19 +41,32 @@
 extern "C" {
 #endif
 
+typedef struct VSLocation VSLocation;
+    
+struct VSLocation {
+    VSLocation *child_begin;
+    VSLocation *child_end;
+    
+    VSLocation *next;
+};
+    
 struct VirtualServer {
-    sstr_t            name;
-    sstr_t            host;
+    cxmutstr            name;
+    cxmutstr            host;
     // TODO: list of listeners, check listener of vs
     
-    sstr_t            objectfile;
+    cxmutstr            objectfile;
     HTTPObjectConfig  *objects;
 
-    sstr_t            document_root;
+    cxmutstr            document_root;
     
     ACLData           *acls;
     AccessLog         *log;
     
+    VSLocation *locations_begin;
+    VSLocation *locations_end;
+    
+    
     uint32_t          ref; // reference counter
 };
 
--- a/src/server/daemon/webserver.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/webserver.c	Sun Nov 06 15:53:32 2022 +0100
@@ -48,7 +48,9 @@
 #include "../util/pblock.h"
 #include "../util/util.h"
 
-#include <ucx/utils.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
+#include <cx/compare.h>
 
 #include "../safs/common.h"
 
@@ -200,7 +202,7 @@
     free(pid_file_path);
     
     // create unix domain socket for server control
-    sstr_t tmp_priv = ucx_sprintf("%s/private", cfg->tmp.ptr);
+    cxmutstr tmp_priv = cx_asprintf("%s/private", cfg->tmp.ptr);
     // TODO: remove existing private dir
     if(mkdir(tmp_priv.ptr, S_IRWXU)) {
         if(errno == EEXIST) {
--- a/src/server/daemon/websocket.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/daemon/websocket.c	Sun Nov 06 15:53:32 2022 +0100
@@ -35,7 +35,7 @@
 #include "../util/pblock.h"
 #include "../util/util.h"
 #include "../util/strbuf.h"
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #define WS_BUFFER_LEN 2048
 
@@ -51,25 +51,25 @@
         return REQ_NOACTION;
     }
     
-    if(sstrcasecmp(sstr(connection), S("upgrade"))) {
+    if(cx_strcasecmp(cx_str(connection), (cxstring)CX_STR("upgrade"))) {
         return REQ_NOACTION;
     }
-    if(sstrcasecmp(sstr(upgrade), S("websocket"))) {
+    if(cx_strcasecmp(cx_str(upgrade), (cxstring)CX_STR("websocket"))) {
         return REQ_NOACTION;
     }
     
-    sstr_t wsaccept = sstrcat(2, sstr(wskey), S("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
+    cxmutstr wsaccept = cx_strcat(2, cx_str(wskey), (cxstring)CX_STR("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
     unsigned char hash[20];
     SHA1((const unsigned char*)wsaccept.ptr, wsaccept.length, hash);
     char *websocket_accept = util_base64encode((char*)hash, 20);
     
     sbuf_t *response = sbuf_new(512);
-    sbuf_append(response, S("HTTP/1.1 101 Switching Protocols\r\n"));
-    sbuf_append(response, S("Upgrade: websocket\r\n"));
-    sbuf_append(response, S("Connection: Upgrade\r\n"));
-    sbuf_append(response, S("Sec-WebSocket-Accept: "));
+    sbuf_append(response, (cxstring)CX_STR("HTTP/1.1 101 Switching Protocols\r\n"));
+    sbuf_append(response, (cxstring)CX_STR("Upgrade: websocket\r\n"));
+    sbuf_append(response, (cxstring)CX_STR("Connection: Upgrade\r\n"));
+    sbuf_append(response, (cxstring)CX_STR("Sec-WebSocket-Accept: "));
     sbuf_puts(response, websocket_accept);
-    sbuf_append(response, S("\r\n\r\n"));
+    sbuf_append(response, (cxstring)CX_STR("\r\n\r\n"));
     
     net_write(sn->csd, response->ptr, response->length);
     sbuf_free(response);
--- a/src/server/plugins/java/jvm.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/plugins/java/jvm.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,7 @@
 #include <stdlib.h>
 #include <dlfcn.h> 
 
-#include <ucx/map.h>
+#include <cx/map.h>
 
 #include "jvm.h"
 #include "daemon/request.h"
--- a/src/server/plugins/postgresql/config.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/plugins/postgresql/config.c	Sun Nov 06 15:53:32 2022 +0100
@@ -87,18 +87,18 @@
 }
 
 PgRepository* pg_init_repo(ServerConfiguration *cfg, pool_handle_t *pool, WSConfigNode *config) {
-    UcxAllocator a = util_pool_allocator(pool);
+    CxAllocator *a = pool_allocator(pool);
     
-    ConfigNode *pg = serverconfig_get_node(config, CONFIG_NODE_OBJECT, SC("Postgresql"));
+    ConfigNode *pg = serverconfig_get_node(config, CONFIG_NODE_OBJECT, cx_str("Postgresql"));
     if(!pg) {
         log_ereport(LOG_MISCONFIG, "pg_init_repo: missing postgresql config object");
         return NULL;
     }
     
-    scstr_t cfg_respool = serverconfig_directive_value(pg, SC("ResourcePool"));
-    scstr_t cfg_rootid = serverconfig_directive_value(pg, SC("RootId"));
-    scstr_t cfg_rootnode = serverconfig_directive_value(pg, SC("RootNode"));
-    scstr_t cfg_dav = serverconfig_directive_value(pg, SC("PGDavConfig"));
+    cxstring cfg_respool = serverconfig_directive_value(pg, cx_str("ResourcePool"));
+    cxstring cfg_rootid = serverconfig_directive_value(pg, cx_str("RootId"));
+    cxstring cfg_rootnode = serverconfig_directive_value(pg, cx_str("RootNode"));
+    cxstring cfg_dav = serverconfig_directive_value(pg, cx_str("PGDavConfig"));
     
     // minimum requirement is a resource pool
     if(cfg_respool.length == 0) {
@@ -140,7 +140,7 @@
     PgRepository *repo = pool_malloc(pool, sizeof(PgRepository));
     ZERO(repo, sizeof(PgRepository));
     
-    repo->resourcepool = sstrdup_a(&a, cfg_respool);
+    repo->resourcepool = cx_strdup_a(a, cfg_respool);
     repo->root_resource_id = root_id;
     
     // check for extended pg dav config
@@ -188,7 +188,7 @@
         PgRepository *repo,
         const char *file_path)
 {
-    UcxAllocator a = util_pool_allocator(pool);
+    CxAllocator *a = pool_allocator(pool);
     
     // check if the file exists and can be accessed
     struct stat s;
@@ -203,7 +203,7 @@
     }
     
     // prepare PgRepository
-    repo->prop_ext = ucx_map_new_a(&a, 8);
+    repo->prop_ext = cxHashMapCreate(a, 8);
     if(!repo->prop_ext) {
         log_ereport(LOG_FAILURE, "pg: cannot load config file: OOM");
         return 1;
@@ -245,8 +245,8 @@
     int ret = 0;
     
     PgExtParser parserData;
-    parserData.table_lookup = ucx_map_new(8);
-    parserData.tables = NULL;
+    parserData.table_lookup = cxHashMapCreate(cxDefaultAllocator, 8);
+    parserData.tables = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_ptr, sizeof(PgExtTable));
     
     while(node && !ret) {
         // currently, the only possible config element is <extension>
@@ -260,13 +260,13 @@
     
     // convert parserData
     if(!ret) {
-        size_t ntables = ucx_list_size(parserData.tables);
+        size_t ntables = parserData.tables->size;
         repo->ntables = ntables;
         repo->tables = pool_calloc(pool, ntables, sizeof(PgExtTable));
         if(repo->tables) {
             int i = 0;
-            UCX_FOREACH(elm, parserData.tables) {
-                PgExtTable *tab = elm->data;
+            CxIterator iter = cxListIterator(parserData.tables, 0);
+            cx_foreach(PgExtTable *, tab, iter) {
                 repo->tables[i++] = *tab;
             }
         } else {
@@ -276,9 +276,8 @@
     }
     
     // cleanup parser
-    ucx_list_free_content(parserData.tables, free);
-    ucx_list_free(parserData.tables);
-    ucx_map_free(parserData.table_lookup);
+    cxListDestroy(parserData.tables);
+    cxMapDestroy(parserData.table_lookup);
     
     return ret;
 }
@@ -290,12 +289,12 @@
         const char *file_path,
         xmlNode *ext_node)
 {
-    UcxAllocator a = util_pool_allocator(pool);
+    CxAllocator *a = pool_allocator(pool);
     
     xmlNode *node = ext_node->children;
     
     const char *table = NULL;
-    UcxList *properties = NULL;
+    CxList *properties = cxPointerLinkedListCreate(a, cx_cmp_ptr);
     
     while(node) {
         if(node->type == XML_ELEMENT_NODE) {
@@ -325,7 +324,7 @@
                             log_ereport(LOG_MISCONFIG, "pg: config %s: no column specified for property %s", file_path, ps->name);
                             return 1;
                         }
-                        properties = ucx_list_append_a(&a, properties, ps);
+                        cxListAdd(properties, ps);
                     }
                     ps = ps->next;
                 }
@@ -345,7 +344,7 @@
     }  
     
     // check if the table was already specified
-    if(ucx_map_cstr_get(ext->table_lookup, table)) {
+    if(cxMapGet(ext->table_lookup, cx_hash_key_str(table))) {
         log_ereport(LOG_MISCONFIG, "pg: config %s: extension table %s not unique", file_path, table);
         return 1;
     }
@@ -355,19 +354,18 @@
     if(!tabname) return 1;
     
     // exttable is only used temporarily
-    PgExtTable *exttable = malloc(sizeof(PgExtTable));
-    if(!exttable) return 1;
-    exttable->table = tabname;
-    exttable->isused = 0; // not relevant in config
-    int tableindex = (int)ucx_list_size(ext->tables);
-    ext->tables = ucx_list_append(ext->tables, exttable);
+    PgExtTable exttable;
+    exttable.table = tabname;
+    exttable.isused = 0; // not relevant in config
+    int tableindex = (int)ext->tables->size;
+    cxListAdd(ext->tables, &exttable);
     
-    if(ucx_map_cstr_put(ext->table_lookup, table, table)) {
+    if(cxMapPut(ext->table_lookup, cx_hash_key_str(table), (void*)table)) {
         return 1;
     }
      
-    UCX_FOREACH(elm, properties) {
-        xmlNode *ps = elm->data;
+    CxIterator iter = cxListIterator(properties, 0);
+    cx_foreach(xmlNode *, ps, iter) {
         const char *value = pg_util_xml_get_text(ps);
         
         PgPropertyStoreExt *ext_col = pool_malloc(pool, sizeof(PgPropertyStoreExt));
@@ -382,9 +380,9 @@
             return 1;
         }
         
-        UcxKey key = webdav_property_key(ext_col->ns, ext_col->name);
-        int err = ucx_map_put(repo->prop_ext, key, ext_col);
-        free((void*)key.data);
+        CxHashKey key = webdav_property_key(ext_col->ns, ext_col->name);
+        int err = cxMapPut(repo->prop_ext, key, ext_col);
+        free(key.data.bytes);
         if(err) {
             return 1;
         }
--- a/src/server/plugins/postgresql/config.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/plugins/postgresql/config.h	Sun Nov 06 15:53:32 2022 +0100
@@ -40,7 +40,10 @@
 
 #include <libpq-fe.h>
 
-#include <ucx/string.h>
+#include <cx/string.h>
+#include <cx/linked_list.h>
+#include <cx/hash_map.h>
+#include <cx/compare.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -53,10 +56,10 @@
     
 typedef struct PgRepository {
     int64_t root_resource_id;
-    sstr_t resourcepool;
+    cxmutstr resourcepool;
     PgExtTable *tables;
     size_t ntables;
-    UcxMap *prop_ext;
+    CxMap *prop_ext;
 } PgRepository;
 
 typedef struct {
@@ -67,8 +70,8 @@
 } PgPropertyStoreExt;
 
 typedef struct {
-    UcxMap *table_lookup;
-    UcxList *tables;
+    CxMap *table_lookup;
+    CxList *tables;
 } PgExtParser;
 
 int pg_lookup_root(ResourceData *res, const char *rootnode, int64_t *rootid);
--- a/src/server/plugins/postgresql/pgtest.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/plugins/postgresql/pgtest.c	Sun Nov 06 15:53:32 2022 +0100
@@ -36,9 +36,9 @@
 #include "../../public/webdav.h"
 #include "../../webdav/webdav.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 "pgtest.h"
 #include "vfs.h"
@@ -48,6 +48,9 @@
 
 #define xstreq(a,b) xmlStrEqual(BAD_CAST a, BAD_CAST b)
 
+#define MAP_GET(map, key) cxMapGet(map, cx_hash_key_str(key))
+#define MAP_PUT(map, key, value) cxMapPut(map, cx_hash_key_str(key), value)
+
 static char *pg_connstr = "postgresql://localhost/test1";
 static int abort_pg_tests = 0;
 static PGconn *test_connection;
@@ -118,11 +121,11 @@
 
 static void parse_response_tag(TestMultistatus *ms, xmlNode *node) {
     // thanks to dav for some of this code
-    UcxAllocator *a = ms->mp->allocator;
+    CxAllocator *a = (CxAllocator*)ms->mp->allocator;
     node = node->children;
     
-    sstr_t href = {NULL, 0};
-    UcxMap *properties = ucx_map_new_a(ms->mp->allocator, 16);
+    cxmutstr href = {NULL, 0};
+    CxMap *properties = cxHashMapCreate(a, 16);
     
     while(node) {
         if(node->type == XML_ELEMENT_NODE) {
@@ -131,7 +134,7 @@
                 if(href_node->type != XML_TEXT_NODE) {
                     return;
                 }
-                href = sstrdup_a(ms->mp->allocator, scstr((const char*)href_node->content));
+                href = cx_strdup_a(a, cx_str((const char*)href_node->content));
             } else if(xstreq(node->name, "propstat")) {
                 xmlNode *n = node->children;
                 xmlNode *prop_node = NULL;
@@ -145,12 +148,12 @@
                             if(status_node->type != XML_TEXT_NODE) {
                                 return;
                             }
-                            sstr_t status_str = sstr((char*)status_node->content);
+                            cxmutstr status_str = cx_mutstr((char*)status_node->content);
                             if(status_str.length < 13) {
                                 return;
                             }
-                            status_str = sstrsubsl(status_str, 9, 3);
-                            sstr_t status_s = sstrdup(status_str);
+                            status_str = cx_strsubsl_m(status_str, 9, 3);
+                            cxmutstr status_s = cx_strdup(cx_strcast(status_str));
                             status_code = atoi(status_s.ptr);
                             free(status_s.ptr);
                         }
@@ -161,21 +164,21 @@
                 n = prop_node->children;
                 while(n) {
                     if(n->type == XML_ELEMENT_NODE) {
-                        TestProperty *property = ucx_mempool_calloc(ms->mp, 1, sizeof(TestProperty));
+                        TestProperty *property = cxCalloc(ms->mp->allocator, 1, sizeof(TestProperty));
                         if(n->ns) {
-                            property->prefix = n->ns->prefix ? sstrdup_a(a, scstr((const char*)n->ns->prefix)).ptr : NULL;
-                            property->namespace = n->ns->href ? sstrdup_a(a, scstr((const char*)n->ns->href)).ptr : NULL;
+                            property->prefix = n->ns->prefix ? cx_strdup_a(a, cx_str((const char*)n->ns->prefix)).ptr : NULL;
+                            property->namespace = n->ns->href ? cx_strdup_a(a, cx_str((const char*)n->ns->href)).ptr : NULL;
                         }
-                        property->name = sstrdup_a(a, scstr((const char*)n->name)).ptr;
+                        property->name = cx_strdup_a(a, cx_str((const char*)n->name)).ptr;
                         property->node = n;
                         property->status = status_code;
                         xmlNode *value = n->children;
                         if(value && value->type == XML_TEXT_NODE) {
-                            property->value = sstrdup_a(a, scstr((const char*)value->content)).ptr;
+                            property->value = cx_strdup_a(a, cx_str((const char*)value->content)).ptr;
                         }
                         if(property->namespace && property->name) {
-                            sstr_t pname = sstrcat(2, sstr(property->namespace), sstr(property->name));
-                            ucx_map_sstr_put(properties, pname, property);
+                            cxmutstr pname = cx_strcat(2, cx_str(property->namespace), cx_str(property->name));
+                            cxMapPut(properties, cx_hash_key(pname.ptr, pname.length), property);
                             free(pname.ptr);
                         }
                     }
@@ -186,11 +189,11 @@
         node = node->next;
     }
     
-    TestResponse *resp =almalloc(a, sizeof(TestResponse));
+    TestResponse *resp = cxMalloc(a, sizeof(TestResponse));
     resp->href = href.ptr;
     resp->properties = properties;
     
-    ucx_map_sstr_put(ms->responses, href, resp);
+    cxMapPut(ms->responses, cx_hash_key(href.ptr, href.length), resp);
 }
 
 TestMultistatus* test_parse_multistatus(const char *space, size_t size) {
@@ -199,11 +202,11 @@
         return NULL;
     }
     
-    UcxMempool *mp = ucx_mempool_new(64);
-    TestMultistatus *ms = ucx_mempool_malloc(mp, sizeof(TestMultistatus));
+    CxMempool *mp = cxBasicMempoolCreate(64);
+    TestMultistatus *ms = cxMalloc(mp->allocator, sizeof(TestMultistatus));
     ms->doc = doc;
     ms->mp = mp;
-    ms->responses = ucx_map_new_a(mp->allocator, 8);
+    ms->responses = cxHashMapCreate((CxAllocator*)mp->allocator, 8);
     
     // parse response
     xmlNode *xml_root = xmlDocGetRootElement(doc);
@@ -224,7 +227,7 @@
 void test_multistatus_destroy(TestMultistatus *ms) {
     if(!ms) return;
     xmlFreeDoc(ms->doc);
-    ucx_mempool_destroy(ms->mp);
+    cxMempoolDestroy(ms->mp);
 }
 
 
@@ -641,16 +644,16 @@
     TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size);
     UCX_TEST_ASSERT(ms, "propfind1: response is not valid xml");
     
-    TestResponse *r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
+    TestResponse *r1 = MAP_GET(ms->responses, "/propfind/");
     UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response");
     
-    UCX_TEST_ASSERT(ms->responses->count == 1, "propfind1: wrong response count");
+    UCX_TEST_ASSERT(ms->responses->size == 1, "propfind1: wrong response count");
     
-    TestProperty *p = ucx_map_cstr_get(r1->properties, "DAV:resourcetype");
+    TestProperty *p = MAP_GET(r1->properties, "DAV:resourcetype");
     UCX_TEST_ASSERT(p, "propfind1: missing property 'resourcetype'");
     UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'resourcetype'");
     
-    p = ucx_map_cstr_get(r1->properties, "DAV:getlastmodified");
+    p = MAP_GET(r1->properties, "DAV:getlastmodified");
     UCX_TEST_ASSERT(p, "propfind1: missing property 'getlastmodified'");
     UCX_TEST_ASSERT(p->status == 200, "propfind1: wrong status code for property 'getlastmodified'");
     
@@ -673,15 +676,15 @@
     ms = test_parse_multistatus(st->buf->space, st->buf->size);
     UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml");
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
+    r1 = MAP_GET(ms->responses, "/propfind/");
     UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response");
     
-    UCX_TEST_ASSERT(ms->responses->count == 5, "propfind2: wrong response count");
+    UCX_TEST_ASSERT(ms->responses->size == 5, "propfind2: wrong response count");
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/res2");
+    r1 = MAP_GET(ms->responses, "/propfind/res2");
     UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res2 response");
     
-    p = ucx_map_cstr_get(r1->properties, "http://example.com/test");
+    p = MAP_GET(r1->properties, "http://example.com/test");
     UCX_TEST_ASSERT(p, "propfind2: missing property 'test'");
     UCX_TEST_ASSERT(p->status == 200, "propfind2: wrong status code for property 'test'");
     UCX_TEST_ASSERT(!strcmp(p->value, "res2test"), "propfind2: wrong property value");
@@ -707,27 +710,27 @@
     ms = test_parse_multistatus(st->buf->space, st->buf->size);
     UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml");
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
+    r1 = MAP_GET(ms->responses, "/propfind/");
     UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response");
     
-    UCX_TEST_ASSERT(ms->responses->count == 6, "propfind3: wrong response count");
+    UCX_TEST_ASSERT(ms->responses->size == 6, "propfind3: wrong response count");
     
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/res1");
+    r1 = MAP_GET(ms->responses, "/propfind/res1");
     UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res1 response");
     
-    p = ucx_map_cstr_get(r1->properties, "http://example.com/test");
+    p = MAP_GET(r1->properties, "http://example.com/test");
     UCX_TEST_ASSERT(p, "propfind3: missing property 'test'");
     UCX_TEST_ASSERT(p->status == 200, "propfind3: wrong status code for property 'test'");
     UCX_TEST_ASSERT(!strcmp(p->value, "testvalue"), "propfind3: wrong property value");
     
-    p = ucx_map_cstr_get(r1->properties, "http://example.com/prop2");
+    p = MAP_GET(r1->properties, "http://example.com/prop2");
     UCX_TEST_ASSERT(p, "propfind3: missing property 'prop2'");
     UCX_TEST_ASSERT(p->status == 200, "propfind3: wrong status code for property 'prop2'");
     UCX_TEST_ASSERT(!strcmp(p->value, "value2"), "propfind3: wrong property value");
     
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/sub/res4");
+    r1 = MAP_GET(ms->responses, "/propfind/sub/res4");
     UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/sub/res4 response");
     
     testutil_destroy_session(sn);
@@ -770,11 +773,11 @@
     TestMultistatus *ms = test_parse_multistatus(st->buf->space, st->buf->size);
     UCX_TEST_ASSERT(ms, "propfind1: response is not valid xml");
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
+    r1 = MAP_GET(ms->responses, "/propfind/");
     UCX_TEST_ASSERT(r1, "propfind1: missing /propfind/ response");
-    UCX_TEST_ASSERT(ms->responses->count == 1, "propfind1: wrong response count");
+    UCX_TEST_ASSERT(ms->responses->size == 1, "propfind1: wrong response count");
     
-    p = ucx_map_cstr_get(r1->properties, "DAV:resourcetype");
+    p = MAP_GET(r1->properties, "DAV:resourcetype");
     UCX_TEST_ASSERT(r1, "propfind1: missing resourcetype property");
     
     testutil_destroy_session(sn);
@@ -795,23 +798,23 @@
     ms = test_parse_multistatus(st->buf->space, st->buf->size);
     UCX_TEST_ASSERT(ms, "propfind2: response is not valid xml");
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
+    r1 = MAP_GET(ms->responses, "/propfind/");
     UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/ response");
-    UCX_TEST_ASSERT(ms->responses->count == 5, "propfind2: wrong response count");
+    UCX_TEST_ASSERT(ms->responses->size == 5, "propfind2: wrong response count");
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/res1");
+    r1 = MAP_GET(ms->responses, "/propfind/res1");
     UCX_TEST_ASSERT(r1, "propfind2: missing /propfind/res1 response");
     
-    p = ucx_map_cstr_get(r1->properties, "DAV:resourcetype");
+    p = MAP_GET(r1->properties, "DAV:resourcetype");
     UCX_TEST_ASSERT(r1, "propfind2: missing resourcetype property");
-    p = ucx_map_cstr_get(r1->properties, "http://example.com/test");
+    p = MAP_GET(r1->properties, "http://example.com/test");
     UCX_TEST_ASSERT(r1, "propfind2: missing test property");
-    p = ucx_map_cstr_get(r1->properties, "http://example.com/prop2");
+    p = MAP_GET(r1->properties, "http://example.com/prop2");
     UCX_TEST_ASSERT(r1, "propfind2: missing prop2 property");
     
-    UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/res2"), "propfind2: missing /propfind/res2 response");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/res3"), "propfind2: missing /propfind/res3 response");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/sub/"), "propfind2: missing /propfind/sub response");
+    UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res2"), "propfind2: missing /propfind/res2 response");
+    UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res3"), "propfind2: missing /propfind/res3 response");
+    UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/"), "propfind2: missing /propfind/sub response");
     
     testutil_destroy_session(sn);
     test_multistatus_destroy(ms);
@@ -829,24 +832,24 @@
     ms = test_parse_multistatus(st->buf->space, st->buf->size);
     UCX_TEST_ASSERT(ms, "propfind3: response is not valid xml");
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/");
+    r1 = MAP_GET(ms->responses, "/propfind/");
     UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/ response");
-    UCX_TEST_ASSERT(ms->responses->count == 6, "propfind3: wrong response count");
+    UCX_TEST_ASSERT(ms->responses->size == 6, "propfind3: wrong response count");
     
-    r1 = ucx_map_cstr_get(ms->responses, "/propfind/res1");
+    r1 = MAP_GET(ms->responses, "/propfind/res1");
     UCX_TEST_ASSERT(r1, "propfind3: missing /propfind/res1 response");
     
-    p = ucx_map_cstr_get(r1->properties, "DAV:resourcetype");
+    p = MAP_GET(r1->properties, "DAV:resourcetype");
     UCX_TEST_ASSERT(r1, "propfind3: missing resourcetype property");
-    p = ucx_map_cstr_get(r1->properties, "http://example.com/test");
+    p = MAP_GET(r1->properties, "http://example.com/test");
     UCX_TEST_ASSERT(r1, "propfind3: missing test property");
-    p = ucx_map_cstr_get(r1->properties, "http://example.com/prop2");
+    p = MAP_GET(r1->properties, "http://example.com/prop2");
     UCX_TEST_ASSERT(r1, "propfind3: missing prop2 property");
     
-    UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/res2"), "propfind3: missing /propfind/res2 response");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/res3"), "propfind3: missing /propfind/res3 response");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/sub/"), "propfind3: missing /propfind/sub response");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(ms->responses, "/propfind/sub/res4"), "propfind3: missing /propfind/sub/res4 response");
+    UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res2"), "propfind3: missing /propfind/res2 response");
+    UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/res3"), "propfind3: missing /propfind/res3 response");
+    UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/"), "propfind3: missing /propfind/sub response");
+    UCX_TEST_ASSERT(MAP_GET(ms->responses, "/propfind/sub/res4"), "propfind3: missing /propfind/sub/res4 response");
     
     testutil_destroy_session(sn);
     test_multistatus_destroy(ms);
--- a/src/server/plugins/postgresql/pgtest.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/plugins/postgresql/pgtest.h	Sun Nov 06 15:53:32 2022 +0100
@@ -14,8 +14,9 @@
 #ifndef PGTEST_H
 #define PGTEST_H
 
-#include <ucx/test.h>
-#include <ucx/map.h>
+#include "../../test/test.h"
+#include <cx/map.h>
+#include <cx/basic_mempool.h>
 #include <libxml/tree.h>
 
 #ifdef __cplusplus
@@ -23,14 +24,14 @@
 #endif
 
 typedef struct TestMultistatus {
-    UcxMempool *mp;
+    CxMempool *mp;
     xmlDoc *doc;
-    UcxMap *responses;
+    CxMap *responses;
 } TestMultistatus;
     
 typedef struct TestResponse {
     char *href;
-    UcxMap *properties;
+    CxMap *properties;
 } TestResponse;
 
 typedef struct TestProperty {
--- a/src/server/plugins/postgresql/webdav.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/plugins/postgresql/webdav.c	Sun Nov 06 15:53:32 2022 +0100
@@ -35,8 +35,9 @@
 
 #include "../../daemon/http.h" // etag
 
-#include <ucx/buffer.h>
-#include <ucx/utils.h>
+#include <cx/buffer.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
 #include <libxml/tree.h>
 
 
@@ -238,14 +239,14 @@
  * adds str to the buffer
  * some characters will be escaped: \,{}
  */
-static void buf_addstr_escaped(UcxBuffer *buf, const char *str) {
+static void buf_addstr_escaped(CxBuffer *buf, const char *str) {
     size_t len = strlen(str);
     for(size_t i=0;i<len;i++) {
         char c = str[i];
         if(c == '{' || c == '}' || c == ',' || c == '\\') {
-            ucx_buffer_putc(buf, '\\');
+            cxBufferPut(buf, '\\');
         }
-        ucx_buffer_putc(buf, c);
+        cxBufferPut(buf, c);
     }
 }
 
@@ -257,23 +258,23 @@
  * 
  * returns 0 on success, 1 otherwise
  */
-int pg_create_property_param_arrays(WebdavPList *plist, UcxBuffer *xmlns, UcxBuffer *pname) {
-    ucx_buffer_putc(xmlns, '{');
-    ucx_buffer_putc(pname, '{');
+int pg_create_property_param_arrays(WebdavPList *plist, CxBuffer *xmlns, CxBuffer *pname) {
+    cxBufferPut(xmlns, '{');
+    cxBufferPut(pname, '{');
     while(plist) {
         WebdavProperty *property = plist->property;
         if(property && property->namespace && property->namespace->href && property->name) {
             buf_addstr_escaped(xmlns, (const char*)property->namespace->href);
             buf_addstr_escaped(pname, (const char*)property->name);
             if(plist->next) {
-                ucx_buffer_putc(xmlns, ',');
-                ucx_buffer_putc(pname, ',');
+                cxBufferPut(xmlns, ',');
+                cxBufferPut(pname, ',');
             }
         }
         plist = plist->next;
     }
-    int r1 = ucx_buffer_write("}\0", 2, 1, xmlns) == 0;
-    int r2 = ucx_buffer_write("}\0", 2, 1, pname) == 0;
+    int r1 = cxBufferWrite("}\0", 2, 1, xmlns) == 0;
+    int r2 = cxBufferWrite("}\0", 2, 1, pname) == 0;
     return r1+r2 != 0;
 }
 
@@ -295,7 +296,7 @@
         WSBool iscollection,
         PgPropfindExtCol *ext,
         size_t numext,
-        UcxBuffer *sql)
+        CxBuffer *sql)
 {
     PgWebdavBackend *pgdav = rq->dav->instance;
     PgRepository *repo = pgdav->repository;
@@ -318,47 +319,47 @@
     
     // CTE
     if(depth == -1) {
-        ucx_buffer_puts(sql, sql_propfind_cte_recursive);
+        cxBufferPutString(sql, sql_propfind_cte_recursive);
     }
     
     // select
-    ucx_buffer_puts(sql, sql_propfind_select);
+    cxBufferPutString(sql, sql_propfind_select);
     
     // ppath
     switch(depth) {
-        case 0: ucx_buffer_puts(sql, sql_propfind_ppath_depth0); break;
-        case 1: ucx_buffer_puts(sql, sql_propfind_ppath_depth1); break;
-        case -1: ucx_buffer_puts(sql, sql_propfind_ppath_depth_infinity); break;
+        case 0: cxBufferPutString(sql, sql_propfind_ppath_depth0); break;
+        case 1: cxBufferPutString(sql, sql_propfind_ppath_depth1); break;
+        case -1: cxBufferPutString(sql, sql_propfind_ppath_depth_infinity); break;
     }
     
     // cols
-    ucx_buffer_puts(sql, sql_propfind_cols);
+    cxBufferPutString(sql, sql_propfind_cols);
     
     // ext_cols
     if(ext) { 
         if(rq->allprop) {
             for(int i=0;i<repo->ntables;i++) {
-                ucx_bprintf(sql, ",x%d.*\n", i);
+                cx_bprintf(sql, ",x%d.*\n", i);
             }
         } else {
             for(int i=0;i<numext;i++) {
                 PgPropfindExtCol e = ext[i];
-                ucx_bprintf(sql, ",x%d.%s\n", e.ext->tableindex, e.ext->column);
+                cx_bprintf(sql, ",x%d.%s\n", e.ext->tableindex, e.ext->column);
             }
         }
     }
     
     // from
-    ucx_buffer_puts(sql, depth == -1 ? sql_propfind_from_cte : sql_propfind_from_table);
+    cxBufferPutString(sql, depth == -1 ? sql_propfind_from_cte : sql_propfind_from_table);
     
     // prop join
-    ucx_buffer_puts(sql, rq->allprop ? sql_propfind_propjoin_allprop : sql_propfind_propjoin_plist);
+    cxBufferPutString(sql, rq->allprop ? sql_propfind_propjoin_allprop : sql_propfind_propjoin_plist);
     
     // ext_join
     if(ext) {
         if(rq->allprop) {
             for(int i=0;i<repo->ntables;i++) {
-                ucx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[i].table, i, i);
+                cx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[i].table, i, i);
             }
         } else {
             int tab = -1;
@@ -366,7 +367,7 @@
                 PgPropfindExtCol e = ext[i];
                 if(e.ext->tableindex != tab) {
                     tab = e.ext->tableindex;
-                    ucx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[tab].table, tab, tab);
+                    cx_bprintf(sql, "left join %s x%d on r.resource_id = x%d.resource_id\n", repo->tables[tab].table, tab, tab);
                 }
             }
         }
@@ -375,20 +376,20 @@
     
     // where
     if(depth == 0) {
-        ucx_buffer_puts(sql, sql_propfind_where_depth0);
+        cxBufferPutString(sql, sql_propfind_where_depth0);
     } else if(depth == 1) {
-        ucx_buffer_puts(sql, sql_propfind_where_depth1);
+        cxBufferPutString(sql, sql_propfind_where_depth1);
     }
     
     // order
     if(depth == 1) {
-        ucx_buffer_puts(sql, sql_propfind_order_depth1);
+        cxBufferPutString(sql, sql_propfind_order_depth1);
     } else if(depth == -1) {
-        ucx_buffer_puts(sql, sql_propfind_order_depth_infinity);
+        cxBufferPutString(sql, sql_propfind_order_depth_infinity);
     }
     
     // end
-    ucx_buffer_puts(sql, ";\0");
+    cxBufferWrite(";\0", 1, 2, sql);
       
     return 0;
 }
@@ -400,6 +401,7 @@
         WebdavPList **outplist)
 {
     PgWebdavBackend *pgdav = rq->dav->instance;
+    CxAllocator *a = pool_allocator(rq->sn->pool);
     
     // first, check if the resource exists
     // if it doesn't exist, we can return immediately
@@ -432,7 +434,7 @@
     // like to use it
     char resource_id_str[32];
     snprintf(resource_id_str, 32, "%" PRId64, resource_id);
-    pblock_nvinsert("resource_id",resource_id_str, rq->rq->vars);
+    pblock_nvinsert("resource_id", resource_id_str, rq->rq->vars);
     
     // create a list of requsted extended properties
     PgPropfindExtCol *ext;
@@ -442,16 +444,15 @@
         ext = NULL;
         numext = 0;
     } else {     
-        numext = pgdav->repository->prop_ext->count;
+        numext = pgdav->repository->prop_ext->size;
         ext = pool_calloc(rq->sn->pool, numext, sizeof(PgPropfindExtCol));
         
         if(rq->allprop) {
             // the map pgdav->repository->prop_ext contains all property extensions
             // we can just convert the map to an array
-            UcxMapIterator i = ucx_map_iterator(pgdav->repository->prop_ext);
-            PgPropertyStoreExt *cfg_ext;
+            CxIterator i = cxMapIteratorValues(pgdav->repository->prop_ext);
             int j = 0;
-            UCX_MAP_FOREACH(key, cfg_ext, i) {
+            cx_foreach(PgPropertyStoreExt *, cfg_ext, i) {
                 PgPropfindExtCol extcol;
                 extcol.ext = cfg_ext;
                 extcol.field_num = -1; // get the field_num after the PQexec
@@ -464,9 +465,9 @@
             while(webdav_plist_iterator_next(&i, &cur)) {
                 WSNamespace *ns = cur->property->namespace;
                 if(ns) {
-                    UcxKey pkey = webdav_property_key((const char*)ns->href, cur->property->name);
-                    PgPropertyStoreExt *cfg_ext = ucx_map_get(pgdav->repository->prop_ext, pkey);
-                    free((void*)pkey.data);
+                    CxHashKey pkey = webdav_property_key((const char*)ns->href, cur->property->name);
+                    PgPropertyStoreExt *cfg_ext = cxMapGet(pgdav->repository->prop_ext, pkey);
+                    free(pkey.data.bytes);
                     if(cfg_ext) {
                         PgPropfindExtCol extcol;
                         extcol.ext = cfg_ext;
@@ -485,11 +486,15 @@
     
     // create sql query
     const char *query = NULL;
-    UcxBuffer *sql = ucx_buffer_new(NULL, 2048, UCX_BUFFER_AUTOEXTEND);
-    if(pg_create_propfind_query(rq, iscollection, ext, numext, sql)) {
+    CxBuffer sql;
+    if(cxBufferInit(&sql, NULL, 2048, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
         return 1;
     }
-    query = sql->space;
+    
+    if(pg_create_propfind_query(rq, iscollection, ext, numext, &sql)) {
+        return 1;
+    }
+    query = sql.space;
     
     // get all resources and properties
     size_t href_len = strlen(href);
@@ -501,32 +506,34 @@
     href_param[href_len] = '\0';
     
     // if allprop is false, create array pair for xmlns/property names
-    UcxBuffer *xmlns_buf = NULL;
-    UcxBuffer *pname_buf = NULL;
+    CxBuffer xmlns_buf;
+    CxBuffer pname_buf;
+    WSBool buf_initialized = FALSE;
     char *xmlns_param = NULL;
     char *pname_param = NULL;
     int nparam = 2;
     if(!rq->allprop) {
         size_t bufsize = rq->propcount < 200 ? 8 + rq->propcount * 32 : 4096;
-        xmlns_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND);
-        if(!xmlns_buf) {
+        if(cxBufferInit(&xmlns_buf, NULL, bufsize, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
             return 1;
         }
-        pname_buf = ucx_buffer_new(NULL, bufsize, UCX_BUFFER_AUTOEXTEND);
-        if(!pname_buf) {
-            ucx_buffer_free(xmlns_buf);
+        if(cxBufferInit(&pname_buf, NULL, bufsize, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
+            cxBufferDestroy(&xmlns_buf);
             return 1;
         }
-        if(pg_create_property_param_arrays(*outplist, xmlns_buf, pname_buf)) {
-            ucx_buffer_free(xmlns_buf);
-            ucx_buffer_free(pname_buf);
+        if(pg_create_property_param_arrays(*outplist, &xmlns_buf, &pname_buf)) {
+            cxBufferDestroy(&xmlns_buf);
+            cxBufferDestroy(&pname_buf);
             return 1;
         }
-        xmlns_param = xmlns_buf->space;
-        pname_param = pname_buf->space;
+        buf_initialized = TRUE;
+        xmlns_param = xmlns_buf.space;
+        pname_param = pname_buf.space;
         nparam = 4;
     }
     
+    
+    
     const char* params[4] = { resource_id_str, href_param, xmlns_param, pname_param };
     PGresult *result = PQexecParams(
             pgdav->connection,
@@ -539,9 +546,9 @@
             0);    // 0: result in text format
     int nrows = PQntuples(result);
     pool_free(rq->sn->pool, href_param);
-    if(xmlns_buf) {
-        ucx_buffer_free(xmlns_buf);
-        ucx_buffer_free(pname_buf);
+    if(buf_initialized) {
+        cxBufferDestroy(&xmlns_buf);
+        cxBufferDestroy(&pname_buf);
     }
     if(nrows < 1) {
         // we resolved the path, so the resource exists and nrows should
@@ -899,9 +906,9 @@
 
 
 static PgPropertyStoreExt* pg_proppatch_prop_get_ext(PgWebdavBackend *pgdav, WebdavProperty *property) {
-    UcxKey pkey = webdav_property_key((const char*)property->namespace->href, property->name);
-    PgPropertyStoreExt *ext = ucx_map_get(pgdav->repository->prop_ext, pkey);
-    free((void*)pkey.data);
+    CxHashKey pkey = webdav_property_key((const char*)property->namespace->href, property->name);
+    PgPropertyStoreExt *ext = cxMapGet(pgdav->repository->prop_ext, pkey);
+    free(pkey.data.bytes);
     return ext;
 }
 
@@ -923,14 +930,21 @@
     ext_prop->column = ext;
     ext_prop->property = property;
 
-    UcxAllocator a = util_pool_allocator(pool);
+    CxAllocator *a = pool_allocator(pool);
     proppatch->ext[ext->tableindex].isused = TRUE;
 
-    UcxList **list = proppatch_op == PG_PROPPATCH_EXT_REMOVE
-                        ? &proppatch->ext[ext->tableindex].remove
-                        : &proppatch->ext[ext->tableindex].set;
-    *list = ucx_list_append_a(&a, *list, ext_prop);
-
+    PgProppatchExtProp **list_begin;
+    PgProppatchExtProp **list_end;
+    if(proppatch_op == PG_PROPPATCH_EXT_SET) {
+        list_begin = &proppatch->ext[ext->tableindex].set_begin;
+        list_end = &proppatch->ext[ext->tableindex].set_end;
+    } else {
+        list_begin = &proppatch->ext[ext->tableindex].remove_begin;
+        list_end = &proppatch->ext[ext->tableindex].remove_end;
+    }
+    
+    cx_linked_list_add((void**)list_begin, (void**)list_end, -1, offsetof(PgProppatchExtProp, next), ext_prop);
+    
     proppatch->extensions_used = TRUE;
     
     return 0;
@@ -1053,33 +1067,36 @@
  * 
  * Query: insert into <table> (resource_id, col1, ...) values ($1, $2 ...);
  */
-static UcxBuffer* ext_row_create_insert_query(WebdavProppatchRequest *request, PgProppatchExt *ext, PgExtTable *table, char *** params, size_t *nparams) {
+static CxBuffer* ext_row_create_insert_query(WebdavProppatchRequest *request, PgProppatchExt *ext, PgExtTable *table, char *** params, size_t *nparams) {
     pool_handle_t *pool = request->sn->pool;
     
-    UcxBuffer *sql = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *sql = pool_malloc(pool, sizeof(CxBuffer)); 
     if(!sql) {
         return NULL;
     }
+    if(cxBufferInit(sql, NULL, 1024, pool_allocator(pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
+        pool_free(pool, sql);
+        return NULL;
+    }
     
-    size_t pg_nparams = ucx_list_size(ext->set) + 1;
+    size_t pg_nparams = cx_linked_list_size(ext->set_begin, offsetof(PgProppatchExtProp, next)) + 1;
     char** pg_params = pool_calloc(pool, pg_nparams, sizeof(char*));
     if(!pg_params) {
-        ucx_buffer_free(sql);
+        cxBufferDestroy(sql);
+        pool_free(pool, sql);
         return NULL;
     }
     
-    ucx_buffer_puts(sql, "insert into ");
-    ucx_buffer_puts(sql, table->table);
-    ucx_buffer_puts(sql, "(resource_id");
-    UCX_FOREACH(elm, ext->set) {
-        PgProppatchExtProp *prop = elm->data;
-        ucx_bprintf(sql, ",%s", prop->column->name);
+    cxBufferPutString(sql, "insert into ");
+    cxBufferPutString(sql, table->table);
+    cxBufferPutString(sql, "(resource_id");
+    for(PgProppatchExtProp *prop=ext->set_begin;prop;prop=prop->next) {
+        cx_bprintf(sql, ",%s", prop->column->name);
     }
     
-    ucx_buffer_puts(sql, ") values ($1\n");
+    cxBufferPutString(sql, ") values ($1\n");
     int i = 1;
-    UCX_FOREACH(elm, ext->set) {
-        PgProppatchExtProp *prop = elm->data;
+    for(PgProppatchExtProp *prop=ext->set_begin;prop;prop=prop->next) {
         WebdavProperty *property = prop->property;
         // convert the property value to WSXmlData
         // property->vtype == WS_VALUE_XML_NODE should always be true
@@ -1091,15 +1108,15 @@
             if(property_value->namespaces) {
                 // currently only text data is supported
                 pool_free(pool, params);
-                ucx_buffer_free(sql);
+                cxBufferDestroy(sql);
                 return NULL;
             }
         }
         
         pg_params[i] = value_str;
-        ucx_bprintf(sql, ",$%d", ++i);
+        cx_bprintf(sql, ",$%d", ++i);
     }
-    ucx_buffer_puts(sql, ");");
+    cxBufferPutString(sql, ");");
     
     
     //printf("\n\n%.*s\n\n", (int)sql->size, sql->space);
@@ -1122,28 +1139,32 @@
  *        ...
  *        where resource_id = $1 ;
  */
-static UcxBuffer* ext_row_create_update_query(WebdavProppatchRequest *request, PgProppatchExt *ext, PgExtTable *table, char *** params, size_t *nparams) {
+static CxBuffer* ext_row_create_update_query(WebdavProppatchRequest *request, PgProppatchExt *ext, PgExtTable *table, char *** params, size_t *nparams) {
     pool_handle_t *pool = request->sn->pool;
     
-    UcxBuffer *sql = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
+    CxBuffer *sql = pool_malloc(pool, sizeof(CxBuffer)); 
     if(!sql) {
         return NULL;
     }
+    if(cxBufferInit(sql, NULL, 1024, pool_allocator(pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
+        pool_free(pool, sql);
+        return NULL;
+    }
     
-    ucx_buffer_puts(sql, "update ");
-    ucx_buffer_puts(sql, table->table);
-    ucx_buffer_puts(sql, " set\n");
+    cxBufferPutString(sql, "update ");
+    cxBufferPutString(sql, table->table);
+    cxBufferPutString(sql, " set\n");
     
-    size_t pg_nparams = ucx_list_size(ext->set) + 1;
+    size_t pg_nparams = cx_linked_list_size(ext->set_begin, offsetof(PgProppatchExtProp, next)) + 1;
     char** pg_params = pool_calloc(pool, pg_nparams, sizeof(char*));
     if(!pg_params) {
-        ucx_buffer_free(sql);
+        cxBufferDestroy(sql);
+        pool_free(pool, sql);
         return NULL;
     }
     
     int i = 1;
-    UCX_FOREACH(elm, ext->set) {
-        PgProppatchExtProp *prop = elm->data;
+    for(PgProppatchExtProp *prop=ext->set_begin;prop;prop=prop->next)  {
         WebdavProperty *property = prop->property;
         // convert the property value to WSXmlData
         // property->vtype == WS_VALUE_XML_NODE should always be true
@@ -1155,23 +1176,22 @@
             if(property_value->namespaces) {
                 // currently only text data is supported
                 pool_free(pool, params);
-                ucx_buffer_free(sql);
+                cxBufferDestroy(sql);
                 return NULL;
             }
         }
         
         pg_params[i] = value_str;
-        ucx_bprintf(sql, " %s = $%d,\n", prop->column->name, ++i);
+        cx_bprintf(sql, " %s = $%d,\n", prop->column->name, ++i);
     }
     
-    UCX_FOREACH(elm, ext->remove) {
-        PgProppatchExtProp *prop = elm->data;
-        ucx_bprintf(sql, " %s = NULL,\n", prop->column->name);
+    for(PgProppatchExtProp *prop=ext->remove_begin;prop;prop=prop->next)  {
+        cx_bprintf(sql, " %s = NULL,\n", prop->column->name);
     }
     
     // check if any write worked
     if(sql->pos == 0) {
-        ucx_buffer_free(sql);
+        cxBufferDestroy(sql);
         pool_free(pool, pg_params);
         return NULL;
     }
@@ -1182,7 +1202,8 @@
         sql->space[sql->pos-2] = ' ';
     }
     
-    ucx_bprintf(sql, "where resource_id =  $1 ;\0");
+    cxBufferPutString(sql, "where resource_id =  $1 ;");
+    cxBufferPut(sql, '\0');
     
     //printf("\n\n%.*s\n\n", (int)sql->size, sql->space);
     //fflush(stdout);
@@ -1203,7 +1224,7 @@
     
     char **params;
     size_t nparam;
-    UcxBuffer *sql = ext_row_create_insert_query(request, ext, table, &params, &nparam);
+    CxBuffer *sql = ext_row_create_insert_query(request, ext, table, &params, &nparam);
     if(!sql) {
         return 1;
     }
@@ -1222,7 +1243,7 @@
             NULL,
             0);    // 0: result in text format
     
-    ucx_buffer_free(sql);
+    cxBufferDestroy(sql);
     
     int ret = 1;
     if(PQresultStatus(result) == PGRES_COMMAND_OK) {
@@ -1252,7 +1273,7 @@
     
     char **params;
     size_t nparam;
-    UcxBuffer *sql = ext_row_create_update_query(request, ext, table, &params, &nparam);
+    CxBuffer *sql = ext_row_create_update_query(request, ext, table, &params, &nparam);
     if(!sql) {
         return 1;
     }
@@ -1271,7 +1292,7 @@
             NULL,
             0);    // 0: result in text format
     
-    ucx_buffer_free(sql);
+    cxBufferDestroy(sql);
     
     int ret = 1;
     if(PQresultStatus(result) == PGRES_COMMAND_OK) {
--- a/src/server/plugins/postgresql/webdav.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/plugins/postgresql/webdav.h	Sun Nov 06 15:53:32 2022 +0100
@@ -35,7 +35,7 @@
 #include "config.h"
 
 #include <libpq-fe.h>
-#include <ucx/buffer.h>
+#include <cx/buffer.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -71,14 +71,18 @@
     int nrows;
 } PgPropfind;
 
-typedef struct {
+typedef struct PgProppatchExtProp PgProppatchExtProp;
+struct PgProppatchExtProp {
     PgPropertyStoreExt *column;
     WebdavProperty *property;
-} PgProppatchExtProp;
+    PgProppatchExtProp *next;
+};
 
 typedef struct {
-    UcxList *set; /* list of PgProppatchExtProp* */
-    UcxList *remove; /* list of PgProppatchExtProp* */
+    PgProppatchExtProp *set_begin;
+    PgProppatchExtProp *set_end;
+    PgProppatchExtProp *remove_begin;
+    PgProppatchExtProp *remove_end;
     WSBool isused;
 } PgProppatchExt;
 
@@ -95,7 +99,7 @@
 
 WebdavBackend* pg_webdav_prop_create(Session *sn, Request *rq, pblock *pb);
 
-int pg_create_property_param_arrays(WebdavPList *plist, UcxBuffer *xmlns, UcxBuffer *pname);
+int pg_create_property_param_arrays(WebdavPList *plist, CxBuffer *xmlns, CxBuffer *pname);
 
 /* ----------------- webdav backend functions ----------------- */
 int pg_dav_propfind_init(
--- a/src/server/public/auth.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/public/auth.h	Sun Nov 06 15:53:32 2022 +0100
@@ -49,7 +49,7 @@
  * param1: authentication database
  * param2: user
  */
-typedef User*(*authdb_get_user_f)(AuthDB*, char*);
+typedef User*(*authdb_get_user_f)(AuthDB*, const char*);
 
 struct auth_db {
     char                *name;
@@ -65,7 +65,7 @@
  * param1: user
  * param2: password
  */
-typedef int(*user_verify_passwd_f)(User*, char*);
+typedef int(*user_verify_passwd_f)(User*, const char*);
 
 /*
  * check if the user is a member of a given group
@@ -73,7 +73,7 @@
  * param1: user
  * param2: group
  */
-typedef int(*user_check_group_f)(User*, char*);
+typedef int(*user_check_group_f)(User*, const char*);
 
 /*
  * free the user object
@@ -93,8 +93,8 @@
 };
 
 
-User* authdb_get_user(AuthDB *db, char *user);
-User* authdb_get_and_verify(AuthDB *db, char *user, char *password, int *pw);
+User* authdb_get_user(AuthDB *db, const char *user);
+User* authdb_get_and_verify(AuthDB *db, const char *user, const char *password, int *pw);
 
 #ifdef	__cplusplus
 }
--- a/src/server/safs/addlog.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/addlog.c	Sun Nov 06 15:53:32 2022 +0100
@@ -72,11 +72,10 @@
     }
     
     // remove trailing line feed
-    sstr_t tmstr = sstr(time);
+    cxstring tmstr = cx_str(time);
     if(tmstr.ptr[tmstr.length-1] == '\n') {
         tmstr.length--;
     }
-    tmstr = sstrdup_pool(sn->pool, tmstr);
     
     if(combined) {
         char *referer = pblock_findval("referer", rq->headers);
@@ -93,9 +92,10 @@
         }
         fprintf(
                 log->log->file,
-                "%s - %s [%s] \"%s\" %d %s %.*s%s%.*s %.*s%s%.*s\n",
+                "%s - %s [%.*s] \"%s\" %d %s %.*s%s%.*s %.*s%s%.*s\n",
                 ip,
                 user,
+                (int)tmstr.length,
                 tmstr.ptr,
                 req,
                 rq->status_num,
@@ -114,9 +114,10 @@
     } else {
         fprintf(
                 log->log->file,
-                "%s - %s [%s] \"%s\" %d %s\n",
+                "%s - %s [%.*s] \"%s\" %d %s\n",
                 ip,
                 user,
+                (int)tmstr.length,
                 tmstr.ptr,
                 req,
                 rq->status_num,
--- a/src/server/safs/auth.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/auth.c	Sun Nov 06 15:53:32 2022 +0100
@@ -243,8 +243,7 @@
 
     // get auth db
     ServerConfiguration *config = session_get_config(sn);
-    sstr_t dbname = sstr(db);
-    AuthDB *authdb = ucx_map_sstr_get(config->authdbs, dbname);
+    AuthDB *authdb = cxMapGet(config->authdbs, cx_hash_key_str(db));
     
     User *auth_user = authdb->get_user(authdb, user);
     if(auth_user && !auth_user->verify_password(auth_user, pw)) {
--- a/src/server/safs/cgi.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/cgi.c	Sun Nov 06 15:53:32 2022 +0100
@@ -36,7 +36,7 @@
 #include <signal.h>
 #include <sys/wait.h>
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "../util/util.h"
 #include "../util/pblock.h"
@@ -216,13 +216,13 @@
         // child
         
         // get script directory and script name
-        sstr_t script = sstr(path);
-        sstr_t parent;    
+        cxstring script = cx_str(path);
+        cxmutstr parent;    
         int len = strlen(path);
         for(int i=len-1;i>=0;i--) {
             if(path[i] == '/') {
-                script = sstrn(path + i + 1, len - i);
-                parent = sstrdup(sstrn(path, i));
+                script = cx_strn(path + i + 1, len - i);
+                parent = cx_strdup(cx_strn(path, i));
                 if(chdir(parent.ptr)) {
                     perror("cgi_start: chdir");
                     free(parent.ptr);
@@ -281,15 +281,15 @@
     CGIResponseParser* parser = pool_malloc(sn->pool, sizeof(CGIResponseParser));
     parser->sn = sn;
     parser->rq = rq;
-    parser->tmp = ucx_buffer_new(NULL, 64, UCX_BUFFER_AUTOEXTEND);
     parser->status = 0;
     parser->msg = NULL;
+    cxBufferInit(&parser->tmp, NULL, 64, pool_allocator(sn->pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS);
     return parser;
 }
 
 void cgi_parser_free(CGIResponseParser *parser) {
-    if(parser->tmp) {
-        ucx_buffer_free(parser->tmp);
+    if(parser->tmp.space) {
+        cxBufferDestroy(&parser->tmp);
     }
     pool_free(parser->sn->pool, parser);
 }
@@ -302,9 +302,9 @@
  *        -1: error
  */
 static int parse_lines(CGIResponseParser *parser, char *buf, size_t len, int *pos) {
-    UcxAllocator a = util_pool_allocator(parser->sn->pool);
-    sstr_t name;
-    sstr_t value;
+    CxAllocator *a = pool_allocator(parser->sn->pool);
+    cxmutstr name;
+    cxmutstr value;
     WSBool space = TRUE;
     int i;
     
@@ -313,7 +313,7 @@
     for(i=0;i<len;i++) {
         char c = buf[i];
         if(value_begin == line_begin && c == ':') {
-            name = sstrn(buf + line_begin, i - line_begin);
+            name = cx_mutstrn(buf + line_begin, i - line_begin);
             value_begin = i + 1;
         } else if(c == '\n') {
             if(value_begin == line_begin) {
@@ -325,17 +325,18 @@
                     return -1;
                 }
             }
-            value = sstrn(buf + value_begin, i - value_begin);
+            value = cx_mutstrn(buf + value_begin, i - value_begin);
             
-            name = sstrlower_a(&a, sstrtrim(name));
-            value = sstrtrim(value);
+            cx_strlower(name);
+            name = cx_strdup_a(a, cx_strtrim((cxstring){name.ptr, name.length}));
+            value = cx_strtrim_m(value);
             
             if(name.length == 0 || value.length == 0) {
                 return -1;
             }
             
-            if(!sstrcmp(name, S("status"))) {
-                sstr_t codestr = value;
+            if(!cx_strcmp((cxstring){name.ptr, name.length}, (cxstring)CX_STR("status"))) {
+                cxmutstr codestr = value;
                 int j;
                 for(j=0;j<codestr.length;j++) {
                     if(!isdigit(codestr.ptr[j])) {
@@ -351,10 +352,10 @@
                 util_strtoint(codestr.ptr, &s);
                 parser->status = (int)s;
                 
-                sstr_t msg = sstrtrim(sstrsubs(value, j + 1));
+                cxmutstr msg = cx_strtrim_m(cx_strsubs_m(value, j + 1));
                 
                 if(msg.length > 0) {
-                    parser->msg = sstrdup_pool(parser->sn->pool, msg).ptr;
+                    parser->msg = cx_strdup_pool(parser->sn->pool, msg).ptr;
                 }
             } else {
                 pblock_nvlinsert(
@@ -388,7 +389,7 @@
 int cgi_parse_response(CGIResponseParser *parser, char *buf, size_t len, size_t *bpos) {
     *bpos = 0;
     int pos = 0;
-    if(parser->tmp->pos > 0) {
+    if(parser->tmp.pos > 0) {
         // the tmp buffer contains an unfinished line
         // fill up the buffer until the line is complete
         WSBool nb = FALSE;
@@ -398,12 +399,12 @@
                 break;
             }
         }
-        ucx_buffer_write(buf, 1, pos, parser->tmp);
+        cxBufferWrite(buf, 1, pos, &parser->tmp);
         
         if(nb) {
             // line complete
             int npos;
-            int r = parse_lines(parser, parser->tmp->space, parser->tmp->pos, &npos);
+            int r = parse_lines(parser, parser->tmp.space, parser->tmp.pos, &npos);
             switch(r) {
                 case -1: return -1;
                 case 0: return -1;
@@ -414,9 +415,9 @@
                 }
             }
             // reset tmp buffer
-            parser->tmp->pos = 0;
+            parser->tmp.pos = 0;
         } else {
-            if(parser->tmp->pos > CGI_RESPONSE_MAX_LINE_LENGTH) {
+            if(parser->tmp.pos > CGI_RESPONSE_MAX_LINE_LENGTH) {
                 return -1;
             }
         }
@@ -430,7 +431,7 @@
         case 1: {
             int newlen = len - npos;
             if(npos > 0) {
-                ucx_buffer_write(buf + npos, 1, newlen, parser->tmp);
+                cxBufferWrite(buf + npos, 1, newlen, &parser->tmp);
             }
             return 0;
         }
--- a/src/server/safs/cgi.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/cgi.h	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,7 @@
 #define CGI_H
 
 #include "../public/nsapi.h"
-#include <ucx/buffer.h>
+#include <cx/buffer.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -45,7 +45,7 @@
 typedef struct {
     Session   *sn;
     Request   *rq;
-    UcxBuffer *tmp;
+    CxBuffer  tmp;
     int       status;
     char      *msg;
 } CGIResponseParser;
--- a/src/server/safs/cgiutils.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/cgiutils.c	Sun Nov 06 15:53:32 2022 +0100
@@ -310,7 +310,7 @@
     env[x++] = util_env_str("SERVER_SOFTWARE", PRODUCT_HEADER_ID"/"PRODUCT_VERSION_ID);
     */ // TODO: enable
 
-    //NSString srvName, portStr;
+    //Ncx_string srvName, portStr;
     //char buf1[256], buf2[64];
     //srvName.useStatic(buf1, sizeof(buf1), 0); 
     //portStr.useStatic(buf2, sizeof(buf2), 0); 
--- a/src/server/safs/common.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/common.c	Sun Nov 06 15:53:32 2022 +0100
@@ -33,9 +33,10 @@
 
 #include "../util/pblock.h"
 #include "../util/util.h"
-#include <ucx/map.h>
+#include <cx/map.h>
+#include <cx/hash_map.h>
 
-static UcxMap *var_names;
+static CxMap *var_names;
 
 enum SAFVarNames {
     COMMONSAF_INSERT_CLIENT = 1,
@@ -74,35 +75,35 @@
 #define COMMONSAF_RET_ERROR -2
 
 void common_saf_init() {
-    var_names = ucx_map_new(32);
+    var_names = cxHashMapCreate(cxDefaultAllocator, 32);
     
-    ucx_map_cstr_put(var_names, "insert-client", (intptr_t)COMMONSAF_INSERT_CLIENT);
-    ucx_map_cstr_put(var_names, "insert-vars", (intptr_t)COMMONSAF_INSERT_VARS);
-    ucx_map_cstr_put(var_names, "insert-reqpb", (intptr_t)COMMONSAF_INSERT_REQPB);
-    ucx_map_cstr_put(var_names, "insert-headers", (intptr_t)COMMONSAF_INSERT_HEADERS);
-    ucx_map_cstr_put(var_names, "insert-srvhdrs", (intptr_t)COMMONSAF_INSERT_SRVHDRS);
+    cxMapPut(var_names, cx_hash_key_str("insert-client"), (void*)(intptr_t)COMMONSAF_INSERT_CLIENT);
+    cxMapPut(var_names, cx_hash_key_str("insert-vars"), (void*)(intptr_t)COMMONSAF_INSERT_VARS);
+    cxMapPut(var_names, cx_hash_key_str("insert-reqpb"), (void*)(intptr_t)COMMONSAF_INSERT_REQPB);
+    cxMapPut(var_names, cx_hash_key_str("insert-headers"), (void*)(intptr_t)COMMONSAF_INSERT_HEADERS);
+    cxMapPut(var_names, cx_hash_key_str("insert-srvhdrs"), (void*)(intptr_t)COMMONSAF_INSERT_SRVHDRS);
     
-    ucx_map_cstr_put(var_names, "set-client", (intptr_t)COMMONSAF_SET_CLIENT);
-    ucx_map_cstr_put(var_names, "set-vars", (intptr_t)COMMONSAF_SET_VARS);
-    ucx_map_cstr_put(var_names, "set-reqpb", (intptr_t)COMMONSAF_SET_REQPB);
-    ucx_map_cstr_put(var_names, "set-headers", (intptr_t)COMMONSAF_SET_HEADERS);
-    ucx_map_cstr_put(var_names, "set-srvhdrs", (intptr_t)COMMONSAF_SET_SRVHDRS);
+    cxMapPut(var_names, cx_hash_key_str("set-client"), (void*)(intptr_t)COMMONSAF_SET_CLIENT);
+    cxMapPut(var_names, cx_hash_key_str("set-vars"), (void*)(intptr_t)COMMONSAF_SET_VARS);
+    cxMapPut(var_names, cx_hash_key_str("set-reqpb"), (void*)(intptr_t)COMMONSAF_SET_REQPB);
+    cxMapPut(var_names, cx_hash_key_str("set-headers"), (void*)(intptr_t)COMMONSAF_SET_HEADERS);
+    cxMapPut(var_names, cx_hash_key_str("set-srvhdrs"), (void*)(intptr_t)COMMONSAF_SET_SRVHDRS);
     
-    ucx_map_cstr_put(var_names, "remove-client", (intptr_t)COMMONSAF_REMOVE_CLIENT);
-    ucx_map_cstr_put(var_names, "remove-vars", (intptr_t)COMMONSAF_REMOVE_VARS);
-    ucx_map_cstr_put(var_names, "remove-reqpb", (intptr_t)COMMONSAF_REMOVE_REQPB);
-    ucx_map_cstr_put(var_names, "remove-headers", (intptr_t)COMMONSAF_REMOVE_HEADERS);
-    ucx_map_cstr_put(var_names, "remove-srvhdrs", (intptr_t)COMMONSAF_REMOVE_SRVHDRS);
+    cxMapPut(var_names, cx_hash_key_str("remove-client"), (void*)(intptr_t)COMMONSAF_REMOVE_CLIENT);
+    cxMapPut(var_names, cx_hash_key_str("remove-vars"), (void*)(intptr_t)COMMONSAF_REMOVE_VARS);
+    cxMapPut(var_names, cx_hash_key_str("remove-reqpb"), (void*)(intptr_t)COMMONSAF_REMOVE_REQPB);
+    cxMapPut(var_names, cx_hash_key_str("remove-headers"), (void*)(intptr_t)COMMONSAF_REMOVE_HEADERS);
+    cxMapPut(var_names, cx_hash_key_str("remove-srvhdrs"), (void*)(intptr_t)COMMONSAF_REMOVE_SRVHDRS);
     
-    ucx_map_cstr_put(var_names, "abort", (intptr_t)COMMONSAF_ABORT);
-    ucx_map_cstr_put(var_names, "noaction", (intptr_t)COMMONSAF_NOACTION);
-    ucx_map_cstr_put(var_names, "error", (intptr_t)COMMONSAF_ERROR);
-    ucx_map_cstr_put(var_names, "escape", (intptr_t)COMMONSAF_ESCAPE);
-    ucx_map_cstr_put(var_names, "find-pathinfo-forward", (intptr_t)COMMONSAF_FIND_PATHINFO_FORWARD);
-    ucx_map_cstr_put(var_names, "http-downgrade", (intptr_t)COMMONSAF_HTTP_DOWNGRADE);
-    ucx_map_cstr_put(var_names, "http-upgrade", (intptr_t)COMMONSAF_HTTP_UPGRADE);
-    ucx_map_cstr_put(var_names, "keep-alive", (intptr_t)COMMONSAF_KEEP_ALIVE);
-    ucx_map_cstr_put(var_names, "name", (intptr_t)COMMONSAF_NAME);
+    cxMapPut(var_names, cx_hash_key_str("abort"), (void*)(intptr_t)COMMONSAF_ABORT);
+    cxMapPut(var_names, cx_hash_key_str("noaction"), (void*)(intptr_t)COMMONSAF_NOACTION);
+    cxMapPut(var_names, cx_hash_key_str("error"), (void*)(intptr_t)COMMONSAF_ERROR);
+    cxMapPut(var_names, cx_hash_key_str("escape"), (void*)(intptr_t)COMMONSAF_ESCAPE);
+    cxMapPut(var_names, cx_hash_key_str("find-pathinfo-forward"), (void*)(intptr_t)COMMONSAF_FIND_PATHINFO_FORWARD);
+    cxMapPut(var_names, cx_hash_key_str("http-downgrade"), (void*)(intptr_t)COMMONSAF_HTTP_DOWNGRADE);
+    cxMapPut(var_names, cx_hash_key_str("http-upgrade"), (void*)(intptr_t)COMMONSAF_HTTP_UPGRADE);
+    cxMapPut(var_names, cx_hash_key_str("keep-alive"), (void*)(intptr_t)COMMONSAF_KEEP_ALIVE);
+    cxMapPut(var_names, cx_hash_key_str("name"), (void*)(intptr_t)COMMONSAF_NAME);
 }
 
 int print_message(pblock *pb, Session *sn, Request *rq) {
@@ -115,8 +116,8 @@
 }
 
 static void var_set(char *value, pblock *pb, WSBool insert) {
-    sstr_t n;
-    sstr_t v;
+    cxstring n;
+    cxstring v;
     v.ptr = NULL;
     
     n.ptr = value;
@@ -125,7 +126,7 @@
     for(i=1;i<len;i++) {
         if(value[i] == '=') {
             n.length = i;
-            v = sstrsubs(sstrn(value, len), i + 1);
+            v = cx_strsubs(cx_strn(value, len), i + 1);
             break;
         }
     }
@@ -143,8 +144,8 @@
     pblock_nvlinsert(n.ptr, n.length, v.ptr, v.length, pb);
 }
 
-static int set_var(Session *sn, Request *rq, char *var, char *value) {
-    intptr_t v = (intptr_t)ucx_map_cstr_get(var_names, var);
+static int set_var(Session *sn, Request *rq, const char *var, char *value) {
+    intptr_t v = (intptr_t)cxMapGet(var_names, cx_hash_key_str(var));
     switch(v) {
         default: break;
         case COMMONSAF_INSERT_CLIENT: var_set(value, sn->client, TRUE); break;
@@ -184,7 +185,7 @@
                 return COMMONSAF_RET_ERROR;
             }
             
-            char *msg = isnum ? NULL : sstrtrim(sstr(value + i)).ptr;
+            const char *msg = isnum ? NULL : cx_strtrim(cx_str(value + i)).ptr;
             protocol_status(sn, rq, (int)status, msg);
             
             return COMMONSAF_REQ_ABORTED;
--- a/src/server/safs/init.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/init.c	Sun Nov 06 15:53:32 2022 +0100
@@ -27,7 +27,7 @@
  */
 
 #include <dlfcn.h> 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "../daemon/func.h"
 #include "../daemon/log.h"
@@ -94,7 +94,7 @@
             struct FuncStruct fc;
             ZERO(&fc, sizeof(struct FuncStruct));
             fc.func = (FuncPtr)sym;
-            fc.name = sstrdup(sstr(funcs)).ptr;
+            fc.name = cx_strdup(cx_str(funcs)).ptr;
             add_function(&fc);
 
             if(b) {
--- a/src/server/safs/nametrans.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/nametrans.c	Sun Nov 06 15:53:32 2022 +0100
@@ -48,22 +48,15 @@
         rq->vfs = vfs;
     }
     
-    WebdavBackend *backend_first = NULL;
-    WebdavBackend *backend_last = NULL;
-    UCX_FOREACH(elm, repo->davBackends) {
-        WebdavBackendInitData *davInit = elm->data;
+    void *backend_first = NULL;
+    void *backend_last = NULL;
+    CxIterator i = cxListIterator(repo->davBackends, 0);
+    cx_foreach(WebdavBackendInitData *, davInit, i) {
         WebdavBackend *backend = davInit->davType->create(sn, rq, pb, davInit->davInitData);
         if(!backend) {
             return REQ_ABORTED;
         }
-        
-        if(backend_last) {
-            backend_last->next = backend;
-            backend_last = backend;
-        } else {
-            backend_first = backend;
-            backend_last = backend;
-        }
+        cx_linked_list_add(&backend_first, &backend_last, -1, offsetof(WebdavBackend, next), backend);
     }
     rq->davCollection = backend_first;
     
@@ -75,7 +68,7 @@
     if(!dav) return 0;
     
     ServerConfiguration *config = session_get_config(sn);
-    WebdavRepository *repo = ucx_map_cstr_get(config->dav, dav);
+    WebdavRepository *repo = cxMapGet(config->dav, cx_hash_key_str(dav));
     
     if(!repo) {
         log_ereport(LOG_MISCONFIG, "nametrans: unknown dav repository '%s'", dav);
@@ -181,8 +174,8 @@
         return REQ_ABORTED;
     }
     
-    sstr_t root_str = sstr(root);
-    sstr_t uri_str = sstr(pblock_findkeyval(pb_key_uri, rq->reqpb));  
+    cxstring root_str = cx_str(root);
+    cxstring uri_str = cx_str(pblock_findkeyval(pb_key_uri, rq->reqpb));  
     
     request_set_path(root_str, uri_str, rq->vars);
     
@@ -255,7 +248,7 @@
         return REQ_ABORTED;
     }
     
-    request_set_path(sstr(dir), sstr(uri), rq->vars);
+    request_set_path(cx_str(dir), cx_str(uri), rq->vars);
     
     if(name) {
         // add object to rq->vars
@@ -306,13 +299,13 @@
     }
     
     char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
-    sstr_t u = sstr(uri);
-    sstr_t f = sstr(from);
-    if(sstrprefix(u, f)) {
-        sstr_t suf = sstrsubs(u, f.length);
-        sstr_t ppath = sstrcat(2, sstr(path), suf);
+    cxstring u = cx_str(uri);
+    cxstring f = cx_str(from);
+    if(cx_strprefix(u, f)) {
+        cxstring suf = cx_strsubs(u, f.length);
+        cxmutstr ppath = cx_strcat(2, cx_str(path), suf);
         
-        request_set_path(sstr(root), ppath, rq->vars);
+        request_set_path(cx_str(root), (cxstring){ppath.ptr, ppath.length}, rq->vars);
         free(ppath.ptr);
         
         if(name) {
--- a/src/server/safs/objecttype.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/objecttype.c	Sun Nov 06 15:53:32 2022 +0100
@@ -26,8 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <ucx/string.h>
-#include <ucx/map.h>
+#include <cx/string.h>
+#include <cx/map.h>
 
 #include "objecttype.h"
 #include "../util/pblock.h"
@@ -36,15 +36,15 @@
 #include "../daemon/session.h"
 
 int object_type_by_extension(pblock *pb, Session *sn, Request *rq) {
-    sstr_t path = sstr(pblock_findkeyval(pb_key_path, rq->vars));
+    cxstring path = cx_str(pblock_findkeyval(pb_key_path, rq->vars));
     //printf("\nobject_type_by_extension: {%s}[%d]\n\n", path);
 
-    sstr_t ct;
+    cxstring ct;
     if(path.ptr[path.length - 1] == '/') {
         // directory
-        ct = sstrn("internal/directory", 18);
+        ct = (cxstring)CX_STR("internal/directory");
     } else {
-        sstr_t ext;
+        cxstring ext;
         ext.length = 0;
         for(int i=path.length - 1;i>=0;i--) {
             if(path.ptr[i] == '.') {
@@ -66,7 +66,7 @@
         WS_ASSERT(config->mimetypes);
         WS_ASSERT(config->mimetypes->map);
         
-        char *type = ucx_map_sstr_get(config->mimetypes->map, ext);
+        char *type = cxMapGet(config->mimetypes->map, cx_hash_key_bytes((const unsigned char*)ext.ptr, ext.length));
         
         if(!type) {
             return REQ_NOACTION;
--- a/src/server/safs/pathcheck.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/pathcheck.c	Sun Nov 06 15:53:32 2022 +0100
@@ -26,7 +26,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "pathcheck.h"
 
@@ -41,6 +41,8 @@
 
 #include "../config/acl.h"
 
+#define PATHCHECK_MAX_TOKENS 2048
+
 int require_auth(pblock *pb, Session *sn, Request *rq) {
     char *user = pblock_findkeyval(pb_key_auth_user, rq->vars);
 
@@ -74,14 +76,13 @@
     }
     
     uint32_t access_mask = 0;
-    ssize_t n = 0;
-    sstr_t *rights = sstrsplit(sstr(mask_str), sstrn(",", 1), &n);
+    cxstring *rights = NULL;
+    ssize_t n = cx_strsplit_a(pool_allocator(sn->pool), cx_str(mask_str), (cxstring){",", 1}, PATHCHECK_MAX_TOKENS, &rights);
     for(int i=0;i<n;i++) {
-        sstr_t right = rights[i];
+        cxstring right = rights[i];
         access_mask = access_mask | accstr2int(right);
-        free(right.ptr);
     }
-    free(rights);
+    pool_free(sn->pool, rights);
     
     rq->aclreqaccess = access_mask;
     
@@ -132,8 +133,8 @@
         return REQ_ABORTED;
     }
     
-    ssize_t ni = 0;
-    sstr_t *names = sstrsplit(sstr(inames), S(","), &ni);
+    cxstring *names = NULL;
+    ssize_t ni = cx_strsplit_a(pool_allocator(sn->pool), cx_str(inames), (cxstring)CX_STR(","), PATHCHECK_MAX_TOKENS, &names);
     if(ni <= 0) {
         log_ereport(
                 LOG_MISCONFIG,
@@ -154,10 +155,10 @@
     
     char *path = pblock_findkeyval(pb_key_path, rq->vars);
     size_t pathlen = strlen(path);
-    sstr_t p = sstrn(path, pathlen);
+    cxstring p = cx_strn(path, pathlen);
     if(path[pathlen-1] == '/') {
         for(int i=0;i<ni;i++) {
-            sstr_t newpath = sstrcat(2, p, sstrtrim(names[i]));
+            cxmutstr newpath = cx_strcat(2, p, cx_strtrim(names[i]));
             struct stat s;
             if(!vfs_stat(vfs, newpath.ptr, &s)) {
                 pblock_kvinsert(
@@ -173,10 +174,7 @@
         }
     }
     
-    for(int i=0;i<ni;i++) {
-        free(names[i].ptr);
-    }
-    free(names);
+    pool_free(sn->pool, names);
     
     return ret;
 }
--- a/src/server/safs/service.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/safs/service.c	Sun Nov 06 15:53:32 2022 +0100
@@ -38,8 +38,9 @@
 #include "../daemon/vfs.h"
 
 #include "../util/strbuf.h"
-#include <ucx/string.h>
-#include <ucx/utils.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
 
 #include <errno.h>
 
@@ -112,14 +113,14 @@
 static HttpRange* parse_range(Session *sn, char *header, int *status) {
     *status = PROTOCOL_OK;
     
-    sstr_t range = sstrtrim(sstr(header));
-    if(!sstrprefix(range, S("bytes="))) {
+    cxstring range = cx_strtrim(cx_str(header));
+    if(!cx_strprefix(range, (cxstring)CX_STR("bytes="))) {
         // unknown range unit - ignore range header
         return NULL;
     }
     
     // get byte-range-set
-    range = sstrsubs(range, 6);
+    range = cx_strsubs(range, 6);
     if(range.length < 1) {
         return NULL;
     }
@@ -132,7 +133,7 @@
     for(int i=0;i<=range.length;i++) {
         char c = range.ptr[i];
         if(c == '-') {
-            sstr_t num = sstrsubsl(range, start, i-start);
+            cxstring num = cx_strsubsl(range, start, i-start);
             if(num.length == 0) {
                 // empty string before '-' is legal
                 hasbegin = 1;
@@ -152,7 +153,7 @@
                 return NULL;
             }
         } else if(c == ',' || c == '\0') {
-            sstr_t num = sstrsubsl(range, start, i-start);
+            cxstring num = cx_strsubsl(range, start, i-start);
             if(hasbegin) {
                 long long n;
                 if(num.length == 0) {
@@ -495,24 +496,26 @@
 }
 
 struct multi_range_elm {
-    sstr_t header;
+    cxmutstr header;
     off_t  offset;
     off_t  length;
 };
 
 static int send_multi_range(Session *sn, Request *rq, SYS_FILE fd, off_t filelen, HttpRange *range) {
+    CxAllocator *a = pool_allocator(sn->pool);
+    
     pb_param *content_type = pblock_remove("content-type", rq->srvhdrs);
     
     char sep[64];
     int seplen = util_mime_separator(sep);
     
-    sstr_t newct = ucx_sprintf("multipart/byteranges; boundary=%s", sep+4);
+    cxmutstr newct = cx_asprintf_a(a, "multipart/byteranges; boundary=%s", sep+4);
     pblock_kvinsert(
             pb_key_content_type,
             newct.ptr,
             newct.length,
             rq->srvhdrs);
-    free(newct.ptr);
+    cxFree(a, newct.ptr);
     
     // calculate content-length
     off_t response_len = 0;
@@ -524,12 +527,13 @@
         rangeelm = rangeelm->next;
     }
     
-    struct multi_range_elm *r = calloc(nrange, sizeof(struct multi_range_elm));
+    struct multi_range_elm *r = pool_calloc(sn->pool, nrange, sizeof(struct multi_range_elm));
     rangeelm = range;
     int i=0;
     while(rangeelm) {
         range2off(rangeelm, filelen, &(r[i].offset), &(r[i].length));
-        r[i].header = ucx_sprintf(
+        r[i].header = cx_asprintf_a(
+                a,
                 "%s\r\nContent-Type: %s\r\nContent-Range: bytes %lld-%lld/%lld\r\n\r\n",
                 sep,
                 content_type->value,
@@ -565,7 +569,7 @@
     }
     net_printf(sn->csd, "%s--\r\n", sep);
     
-    free(r);
+    pool_free(sn->pool, r);
     return 0;
 }
 
@@ -616,7 +620,7 @@
                     (long long)length,
                     rq->srvhdrs);
             
-            sstr_t content_range = ucx_sprintf(
+            cxmutstr content_range = cx_asprintf(
                     "%lld-%lld/%lld",
                     (long long)offset,
                     (long long)offset+length - 1,
@@ -696,7 +700,7 @@
     char *path = pblock_findkeyval(pb_key_path, rq->vars);
     char *uri = pblock_findkeyval(pb_key_uri, rq->reqpb);
 
-    sstr_t r_uri = sstr(uri);
+    cxstring r_uri = cx_str(uri);
 
     // open the file
     VFSContext *vfs = vfs_request_context(sn, rq);
@@ -717,7 +721,7 @@
     // list directory
     VFS_ENTRY f;
     while(vfs_readdir(dir, &f)) {
-        sstr_t filename = sstr(f.name);
+        cxstring filename = cx_str(f.name);
 
         sbuf_puts(out, "<a href=\"");
         sbuf_append(out, r_uri);
--- a/src/server/test/main.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/main.c	Sun Nov 06 15:53:32 2022 +0100
@@ -40,7 +40,7 @@
 #include "../util/date.h"
 #include "../daemon/vfs.h"
 
-#include <ucx/test.h>
+#include "test.h"
 
 #include "vfs.h"
 #include "writer.h"
--- a/src/server/test/objs.mk	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/objs.mk	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,8 @@
 
 TEST_OBJPRE = $(OBJ_DIR)$(TEST_SRC_DIR)
 
-TESTOBJ = main.o
+TESTOBJ  = test.o
+TESTOBJ += main.o
 TESTOBJ += testutils.o
 TESTOBJ += webdav.o
 TESTOBJ += vfs.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/test/test.c	Sun Nov 06 15:53:32 2022 +0100
@@ -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/src/server/test/test.h	Sun Nov 06 15:53:32 2022 +0100
@@ -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/src/server/test/testutils.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/testutils.c	Sun Nov 06 15:53:32 2022 +0100
@@ -29,8 +29,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <ucx/string.h>
-#include <ucx/utils.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
 
 #include "../util/pblock.h"
 
@@ -54,7 +55,7 @@
     ZERO(&httprequest, sizeof(HTTPRequest));
     request_initialize(pool, &httprequest, rq);
     
-    sstr_t clf = ucx_sprintf("%s %s HTTP/1.1", method, uri);
+    cxmutstr clf = cx_asprintf("%s %s HTTP/1.1", method, uri);
     pblock_kvinsert(
             pb_key_clf_request,
             clf.ptr,
@@ -100,7 +101,7 @@
 }
 
 void testutil_request_body(Session *sn, Request *rq, const char *body, size_t len) {
-    sstr_t cl = ucx_sprintf("%d", (int)len);
+    cxmutstr cl = cx_asprintf("%d", (int)len);
     pblock_nvreplace("content-length", cl.ptr, rq->headers);
     free(cl.ptr);
     
@@ -122,7 +123,7 @@
 
 static ssize_t test_io_write(IOStream *io, void *buf, size_t size) {
     TestIOStream *st = (TestIOStream*)io;
-    return ucx_buffer_write(buf, 1, size, st->buf);
+    return cxBufferWrite(buf, 1, size, st->buf);
 }
 
 static ssize_t test_io_writev(IOStream *io, struct iovec *iovec, int iovctn) {
@@ -153,9 +154,10 @@
     TestIOStream *stream = calloc(1, sizeof(TestIOStream));
     int flags = 0;
     if(autoextend) {
-        flags = UCX_BUFFER_AUTOEXTEND;
+        flags = CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS;
     }
-    stream->buf = ucx_buffer_new(NULL, size, flags);
+    stream->buf = malloc(sizeof(CxBuffer));
+    cxBufferInit(stream->buf, NULL, size, cxDefaultAllocator, flags);
     
     stream->io.st.write = test_io_write;
     stream->io.st.writev = test_io_writev;
@@ -166,6 +168,7 @@
 }
 
 void testutil_iostream_destroy(TestIOStream *stream) {
-    ucx_buffer_free(stream->buf);
+    cxBufferDestroy(stream->buf);
+    free(stream->buf);
     free(stream);
 }
--- a/src/server/test/testutils.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/testutils.h	Sun Nov 06 15:53:32 2022 +0100
@@ -29,11 +29,13 @@
 #ifndef TESTUTILS_H
 #define TESTUTILS_H
 
+#include "test.h"
+
 #include "../public/nsapi.h"
 #include "../daemon/httprequest.h"
 
 #include "../util/io.h"
-#include <ucx/buffer.h>
+#include <cx/buffer.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -41,7 +43,7 @@
 
 typedef struct TestIOStream {
     HttpStream io;
-    UcxBuffer *buf;
+    CxBuffer *buf;
 } TestIOStream;
     
 Session* testutil_session(void);
--- a/src/server/test/uri.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/uri.h	Sun Nov 06 15:53:32 2022 +0100
@@ -32,7 +32,7 @@
 
 #include "../public/nsapi.h"
 
-#include <ucx/test.h>
+#include "test.h"
 
 #ifdef __cplusplus
 extern "C" {
--- a/src/server/test/vfs.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/vfs.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,9 +30,9 @@
 #include <stdlib.h>
 #include <errno.h>
 
-#include <ucx/string.h>
-#include <ucx/list.h>
-#include <ucx/map.h>
+#include <cx/string.h>
+#include <cx/list.h>
+#include <cx/map.h>
 
 #include "../daemon/session.h"
 
@@ -41,33 +41,33 @@
 #include "vfs.h"
 
 typedef struct TestVFS {
-    UcxMap *files;
+    CxMap *files;
     int count_unlink;
     int count_rmdir;
 } TestVFS;
 
 typedef struct TestVFSFile {
     VFSFile file;
-    sstr_t path;
+    cxmutstr path;
     int isdir;
-    UcxBuffer *content;
+    CxBuffer content;
 } TestVFSFile;
 
 typedef struct TestVFSDir {
     VFSDir dir;
     TestVFSFile *file;
-    UcxMapIterator i;
-    sstr_t name;
+    CxIterator i;
+    cxmutstr name;
 } TestVFSDir;
 
 /* dir io */
 
-static char* test_resource_name(char *url) {
-    sstr_t urlstr = sstr(url);
+static const char* test_resource_name(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 {
@@ -79,29 +79,28 @@
     TestVFS *vfs = dir->ctx->vfs->instance;
     TestVFSDir *vfsdir = (TestVFSDir*)dir;
     
-    sstr_t prefix = sstrcat(2, vfsdir->file->path, S("/"));
+    cxmutstr prefix = cx_strcat(2, vfsdir->file->path, cx_str("/"));
     
+    // not the most efficient file system implementation ever, but it is only
+    // for testing
+    // check every entry in vfs->files and return, if the parent path
+    // matches the open directory
     TestVFSFile *file = NULL;
-    UCX_MAP_FOREACH(key, file, vfsdir->i) {
-        sstr_t file_path = sstrcat(
+    cx_foreach(TestVFSFile *, entry, vfsdir->i) {
+        if(file) break;
+        cxmutstr file_path = cx_strcat(
                 2,
                 prefix,
-                sstr(test_resource_name(file->path.ptr)));
-        void *m = ucx_map_get(vfs->files, ucx_key(file_path.ptr, file_path.length));
-        // don't ask why alfree and not free()
-        alfree(ucx_default_allocator(), file_path.ptr);
-        if(m) {
-            break;
-        } else {
-            file = NULL;
-        }
+                cx_str(test_resource_name(entry->path.ptr)));
+        file = cxMapGet(vfs->files, cx_hash_key(file_path.ptr, file_path.length));
+        free(file_path.ptr);
     }
     free(prefix.ptr);
     
     if(file) {
-        vfsdir->name = sstrdup_a(
-                session_get_allocator(dir->ctx->sn),
-                sstr(test_resource_name(file->path.ptr)));
+        vfsdir->name = cx_strdup_a(
+                pool_allocator(dir->ctx->sn->pool),
+                cx_str(test_resource_name(file->path.ptr)));
         ZERO(entry, sizeof(VFS_ENTRY));
         entry->name = vfsdir->name.ptr;
         
@@ -126,35 +125,35 @@
 
  ssize_t testvfs_read(SYS_FILE fd, void *buf, size_t nbyte) {
      TestVFSFile *file = (TestVFSFile*)fd;
-     return (ssize_t)ucx_buffer_read(buf, 1, nbyte, file->content);
+     return (ssize_t)cxBufferRead(buf, 1, nbyte, &file->content);
  }
  
  ssize_t testvfs_write(SYS_FILE fd, const void *buf, size_t nbyte) {
      TestVFSFile *file = (TestVFSFile*)fd;
-     return (ssize_t)ucx_buffer_write(buf, 1, nbyte, file->content);
+     return (ssize_t)cxBufferWrite(buf, 1, nbyte, &file->content);
  }
  
  ssize_t testvfs_pread(SYS_FILE fd, void *buf, size_t nbyte, off_t offset) {
      TestVFSFile *file = (TestVFSFile*)fd;
-     file->content->pos = (size_t)offset;
+     file->content.pos = (size_t)offset;
      return testvfs_read(fd, buf, nbyte);
  }
  
  ssize_t testvfs_pwrite(SYS_FILE fd, const void *buf, size_t nbyte, off_t offset) {
      TestVFSFile *file = (TestVFSFile*)fd;
-     file->content->pos = (size_t)offset;
+     file->content.pos = (size_t)offset;
      return testvfs_write(fd, buf, nbyte);
  }
  
  off_t testvfs_seek(SYS_FILE fd, off_t offset, int whence) {
      TestVFSFile *file = (TestVFSFile*)fd;
-     ucx_buffer_seek(file->content, offset, whence);
-     return (off_t)file->content->pos;
+     cxBufferSeek(&file->content, offset, whence);
+     return (off_t)file->content.pos;
  }
  
  void testvfs_close(SYS_FILE fd) {
      TestVFSFile *file = (TestVFSFile*)fd;
-     file->content->pos = 0;
+     file->content.pos = 0;
  }
 
 VFS_IO test_file_io = {
@@ -180,28 +179,23 @@
     TestVFS *vfs = ctx->vfs->instance;  
     TestVFSFile *file = NULL;
     
-    sstr_t s_path = sstr((char*)path);
-    if(sstrsuffix(s_path, S("/"))) {
+    cxstring s_path = cx_str((char*)path);
+    if(cx_strsuffix(s_path, cx_str("/"))) {
         s_path.length--;
     }
     
-    file = ucx_map_sstr_get(vfs->files, s_path);
+    file = cxMapGet(vfs->files, cx_hash_key_bytes((const unsigned char*)s_path.ptr, s_path.length));
     if(!file) {
         if((oflags & O_CREAT) == O_CREAT) {
             file = pool_malloc(ctx->sn->pool, sizeof(TestVFSFile));
             ZERO(file, sizeof(TestVFSFile));
             file->file.ctx = ctx;
-            file->path = sstrdup_a(session_get_allocator(ctx->sn), s_path);
+            file->path = cx_strdup_a(pool_allocator(ctx->sn->pool), s_path);
             file->file.io = &test_file_io;
             
-            file->content = pool_calloc(ctx->sn->pool, 1, sizeof(UcxBuffer));
-            file->content->capacity = 2048;
-            file->content->space = pool_malloc(ctx->sn->pool, file->content->capacity);
-            file->content->flags = 0;
-            file->content->pos = 0;
-            file->content->size = 0;
+            cxBufferInit(&file->content, pool_malloc(ctx->sn->pool, 2048), 2048, pool_allocator(ctx->sn->pool), 0);
             
-            ucx_map_sstr_put(vfs->files, s_path, file);
+            cxMapPut(vfs->files, cx_hash_key((void*)s_path.ptr, s_path.length), file);
         } else {
             ctx->vfs_errno = ENOENT;
         }
@@ -214,12 +208,12 @@
     TestVFS *vfs = ctx->vfs->instance;  
     TestVFSFile *file = NULL;
     
-    sstr_t s_path = sstr((char*)path);
-    if(sstrsuffix(s_path, S("/"))) {
+    cxstring s_path = cx_str((char*)path);
+    if(cx_strsuffix(s_path, cx_str("/"))) {
         s_path.length--;
     }
     
-    file = ucx_map_sstr_get(vfs->files, s_path);
+    file = cxMapGet(vfs->files, cx_hash_key((void*)s_path.ptr, s_path.length));
     if(!file) {
         ctx->vfs_errno = ENOENT;
         return 1;
@@ -241,12 +235,12 @@
     TestVFS *vfs = ctx->vfs->instance;  
     TestVFSFile *file = NULL;
     
-    sstr_t s_path = sstr((char*)path);
-    if(sstrsuffix(s_path, S("/"))) {
+    cxstring s_path = cx_str((char*)path);
+    if(cx_strsuffix(s_path, cx_str("/"))) {
         s_path.length--;
     }
     
-    file = ucx_map_sstr_get(vfs->files, s_path);
+    file = cxMapGet(vfs->files, cx_hash_key((void*)s_path.ptr, s_path.length));
     if(!file) {
         ctx->vfs_errno = ENOENT;
         return NULL;
@@ -259,7 +253,7 @@
     TestVFSDir *dir = pool_malloc(ctx->sn->pool, sizeof(TestVFSDir));
     ZERO(dir, sizeof(TestVFSDir));
     dir->file = file;
-    dir->i = ucx_map_iterator(vfs->files);
+    dir->i = cxMapIteratorValues(vfs->files);
     
     dir->dir.ctx = ctx;
     dir->dir.io = &test_dir_io;
@@ -277,7 +271,7 @@
     TestVFSDir *dir = pool_malloc(ctx->sn->pool, sizeof(TestVFSDir));
     ZERO(dir, sizeof(TestVFSDir));
     dir->file = file;
-    dir->i = ucx_map_iterator(vfs->files);
+    dir->i = cxMapIteratorValues(vfs->files);
     
     dir->dir.ctx = ctx;
     dir->dir.io = &test_dir_io;
@@ -299,7 +293,8 @@
 
 int testvfs_unlink(VFSContext *ctx, const char *path) {
     TestVFS *vfs = ctx->vfs->instance;
-    TestVFSFile *file = ucx_map_cstr_get(vfs->files, path);
+    CxHashKey path_key = cx_hash_key_str(path);
+    TestVFSFile *file = cxMapGet(vfs->files, path_key);
     if(!file) {
         return 1;
     }
@@ -308,14 +303,15 @@
         return 1;
     }
     
-    ucx_map_cstr_remove(vfs->files, path);
+    (void)cxMapRemove(vfs->files, path_key);
     vfs->count_unlink++;
     return 0;
 }
 
 int testvfs_rmdir(VFSContext *ctx, const char *path) {
     TestVFS *vfs = ctx->vfs->instance;
-    TestVFSFile *dir = ucx_map_cstr_get(vfs->files, path);
+    CxHashKey path_key = cx_hash_key_str(path);
+    TestVFSFile *dir = cxMapGet(vfs->files, path_key);
     if(!dir) {
         ctx->vfs_errno = ENOENT;
         return 1;
@@ -325,15 +321,14 @@
         return 1;
     }
     
-    UcxMapIterator i = ucx_map_iterator(vfs->files);
-    TestVFSFile *f;
-    UCX_MAP_FOREACH(key, f, i) {
-        if(f->path.length > dir->path.length && sstrprefix(f->path, dir->path)){
+    CxIterator i = cxMapIteratorValues(vfs->files);
+    cx_foreach(TestVFSFile *, f, i) {
+        if(f->path.length > dir->path.length && cx_strprefix(cx_strcast(f->path), cx_strcast(dir->path))){
             return 1; // dir not empty
         }
     }
     
-    ucx_map_cstr_remove(vfs->files, path);
+    (void)cxMapRemove(vfs->files, path_key);
     vfs->count_rmdir++;
     return 0;
 }
@@ -356,7 +351,7 @@
     TestVFS *vfs = pool_malloc(sn->pool, sizeof(TestVFS));
     vfs->count_unlink = 0;
     vfs->count_rmdir = 0;
-    vfs->files = ucx_map_new_a(session_get_allocator(sn), 64);
+    vfs->files = cxHashMapCreate(pool_allocator(sn->pool), 64);
     
     testVFSClass.instance = vfs;
     return &testVFSClass;
@@ -452,20 +447,20 @@
     VFSDir *dir = vfs_opendir(vfs, "/dir");
     UCX_TEST_ASSERT(dir, "dir not opened");
     
-    UcxMap *files = ucx_map_new(8);
+    CxMap *files = cxHashMapCreate(cxDefaultAllocator, 8);
     
     VFSEntry entry;
     while(vfs_readdir(dir, &entry)) {
-        ucx_map_cstr_put(files, entry.name, dir);
+        cxMapPut(files, cx_hash_key_str(entry.name), dir);
     }
     
-    UCX_TEST_ASSERT(files->count == 4, "wrong files count");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file1"), "file1 missing");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file2"), "file2 missing");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file3"), "file3 missing");
-    UCX_TEST_ASSERT(ucx_map_cstr_get(files, "file4"), "file4 missing");
+    UCX_TEST_ASSERT(files->size == 4, "wrong files count");
+    UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str("file1")), "file1 missing");
+    UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str("file2")), "file2 missing");
+    UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str("file3")), "file3 missing");
+    UCX_TEST_ASSERT(cxMapGet(files, cx_hash_key_str("file4")), "file4 missing");
     
-    ucx_map_free(files);
+    cxMapDestroy(files);
     
     UCX_TEST_END;
     
--- a/src/server/test/vfs.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/vfs.h	Sun Nov 06 15:53:32 2022 +0100
@@ -32,7 +32,7 @@
 #include "../public/nsapi.h"
 #include "../public/vfs.h"
 
-#include <ucx/test.h>
+#include "test.h"
 
 #ifdef __cplusplus
 extern "C" {
--- a/src/server/test/webdav.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/webdav.c	Sun Nov 06 15:53:32 2022 +0100
@@ -312,7 +312,7 @@
     // for adding resources to the response
     WebdavResponse *response = (WebdavResponse*)ms;
     
-    UcxList *requests = NULL;
+    WebdavPropfindRequestList *requests = NULL;
     
     // Initialize all Webdav Backends
     if(webdav_propfind_init(&backend1, propfind, "/", "/", &requests)) {
@@ -703,11 +703,12 @@
     rq = testutil_request(sn->pool, "PUT", "/");
     testutil_request_body(sn, rq, "Hello World!", 12);
     
-    UcxBuffer *b1 = rqbody2buffer(sn, rq);
-    UCX_TEST_ASSERT(b1->size == 12, "b1: wrong size");
-    UCX_TEST_ASSERT(!memcmp(b1->space,"Hello World!",12), "b1: wrong content");
+    CxBuffer b1;
+    rqbody2buffer(sn, rq, &b1);
+    UCX_TEST_ASSERT(b1.size == 12, "b1: wrong size");
+    UCX_TEST_ASSERT(!memcmp(b1.space,"Hello World!",12), "b1: wrong content");
     
-    ucx_buffer_free(b1);
+    cxBufferDestroy(&b1);
     testutil_destroy_session(sn);
     
     //
@@ -721,11 +722,12 @@
     rq = testutil_request(sn->pool, "PUT", "/");
     testutil_request_body(sn, rq, (char*)body1, len1);
     
-    UcxBuffer *b2 = rqbody2buffer(sn, rq);
-    UCX_TEST_ASSERT(b2->size == len1, "b2: wrong size");
-    UCX_TEST_ASSERT(!memcmp(b2->space, body1, len1), "b2: wrong content");
+    CxBuffer b2;
+    rqbody2buffer(sn, rq, &b2);
+    UCX_TEST_ASSERT(b2.size == len1, "b2: wrong size");
+    UCX_TEST_ASSERT(!memcmp(b2.space, body1, len1), "b2: wrong content");
     
-    ucx_buffer_free(b2);
+    cxBufferDestroy(&b2);
     testutil_destroy_session(sn);
     
     UCX_TEST_END;
@@ -997,15 +999,15 @@
     UCX_TEST_BEGIN;
     UCX_TEST_ASSERT(!test_init(&sn, &rq, &propfind, TEST_PROPFIND1), "init failed");
     
-    UcxList *requests = NULL;
+    WebdavPropfindRequestList *requests = NULL;
     int err = webdav_propfind_init(&backend1, propfind, "/", "/", &requests);
     
     UCX_TEST_ASSERT(!err, "webdav_propfind_init failed");
     UCX_TEST_ASSERT(requests, "request list is empty");
-    UCX_TEST_ASSERT(ucx_list_size(requests), "request list has wrong size");
+    UCX_TEST_ASSERT(cx_linked_list_size(requests, offsetof(WebdavPropfindRequestList, next)), "request list has wrong size");
     
-    WebdavPropfindRequest *p1 = requests->data;
-    WebdavPropfindRequest *p2 = requests->next->data;
+    WebdavPropfindRequest *p1 = requests->propfind;
+    WebdavPropfindRequest *p2 = requests->next->propfind;
     
     // backend1 removes the first property from the plist
     // backend2 should have one property less 
--- a/src/server/test/webdav.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/webdav.h	Sun Nov 06 15:53:32 2022 +0100
@@ -34,7 +34,7 @@
 
 #include "testutils.h"
 
-#include <ucx/test.h>
+#include "test.h"
 
 #ifdef __cplusplus
 extern "C" {
--- a/src/server/test/writer.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/writer.c	Sun Nov 06 15:53:32 2022 +0100
@@ -31,7 +31,7 @@
 
 #include "../util/writer.h"
 
-#include <ucx/buffer.h>
+#include <cx/buffer.h>
 
 #include "writer.h"
 #include "testutils.h"
@@ -39,7 +39,7 @@
 UCX_TEST(test_writer_putc) {
     Session *sn = testutil_session();
     TestIOStream *st = testutil_iostream(2048, TRUE);
-    UcxBuffer *buf = st->buf;
+    CxBuffer *buf = st->buf;
     
     UCX_TEST_BEGIN;
     
@@ -76,7 +76,7 @@
 UCX_TEST(test_writer_flush) {
     Session *sn = testutil_session();
     TestIOStream *st = testutil_iostream(2048, TRUE);
-    UcxBuffer *buf = st->buf;
+    CxBuffer *buf = st->buf;
     
     UCX_TEST_BEGIN;
     
@@ -106,7 +106,7 @@
 UCX_TEST(test_writer_put) {
     Session *sn = testutil_session();
     TestIOStream *st = testutil_iostream(2048, TRUE);
-    UcxBuffer *buf = st->buf;
+    CxBuffer *buf = st->buf;
     
     UCX_TEST_BEGIN;
     
@@ -135,7 +135,7 @@
     UCX_TEST_ASSERT(!memcmp(buf->space, "abcdefgh12345678", 16), "4: wrong UcxBuffer content");
     UCX_TEST_ASSERT(buf->pos == 16, "4: wrong UcxBuffer pos");
     
-    writer_puts(out, S("345678abcdefgh12345678end."));
+    writer_puts(out, cx_str("345678abcdefgh12345678end."));
     UCX_TEST_ASSERT(!memcmp(wbuf, "end.", 4), "5: wrong content");
     UCX_TEST_ASSERT(writer.pos == 4, "5: wrong pos");
     UCX_TEST_ASSERT(!memcmp(
--- a/src/server/test/writer.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/writer.h	Sun Nov 06 15:53:32 2022 +0100
@@ -29,7 +29,7 @@
 #ifndef TEST_WRITER_H
 #define TEST_WRITER_H
 
-#include <ucx/test.h>
+#include "test.h"
 
 #ifdef __cplusplus
 extern "C" {
--- a/src/server/test/xml.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/test/xml.h	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,7 @@
 #define TEST_XML_H
 
 #include "../public/nsapi.h"
-#include <ucx/test.h>
+#include "test.h"
 
 #ifdef __cplusplus
 extern "C" {
--- a/src/server/util/date.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/date.c	Sun Nov 06 15:53:32 2022 +0100
@@ -56,8 +56,8 @@
     "Dec"
 };
 
-sstr_t date_format_http(time_t t, pool_handle_t *pool) {
-    sstr_t d;
+cxmutstr date_format_http(time_t t, pool_handle_t *pool) {
+    cxmutstr d;
     d.ptr = NULL;
     d.length = 0;
     
@@ -83,8 +83,8 @@
     return d;
 }
 
-sstr_t date_format_iso8601(time_t t, pool_handle_t *pool) {
-    sstr_t d;
+cxmutstr date_format_iso8601(time_t t, pool_handle_t *pool) {
+    cxmutstr d;
     d.ptr = NULL;
     d.length = 0;
     
--- a/src/server/util/date.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/date.h	Sun Nov 06 15:53:32 2022 +0100
@@ -33,15 +33,15 @@
 
 #include <time.h>
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #ifdef	__cplusplus
 extern "C" {
 #endif
 
-sstr_t date_format_http(time_t t, pool_handle_t *pool);
+cxmutstr date_format_http(time_t t, pool_handle_t *pool);
 
-sstr_t date_format_iso8601(time_t t, pool_handle_t *pool);
+cxmutstr date_format_iso8601(time_t t, pool_handle_t *pool);
 
 
 #ifdef	__cplusplus
--- a/src/server/util/io.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/io.c	Sun Nov 06 15:53:32 2022 +0100
@@ -66,7 +66,8 @@
 #include "pool.h"
 #include "../daemon/netsite.h"
 #include "../daemon/event.h"
-#include "ucx/utils.h"
+#include "cx/utils.h"
+#include <cx/printf.h>
 
 IOStream native_io_funcs = {
     (io_write_f)net_sys_write,
@@ -106,31 +107,31 @@
 
 
 /*
- * SysStream implementation
+ * Sycx_stream implementation
  */
 
-IOStream* sysstream_new(pool_handle_t *pool, SYS_SOCKET fd) {
-    SysStream *st = pool_malloc(pool, sizeof(SysStream));
+IOStream* sycx_stream_new(pool_handle_t *pool, SYS_SOCKET fd) {
+    Sycx_stream *st = pool_malloc(pool, sizeof(Sycx_stream));
     st->st = native_io_funcs;
     st->fd = fd;
     return (IOStream*)st;
 }
 
 #ifdef XP_UNIX
-ssize_t net_sys_write(SysStream *st, void *buf, size_t nbytes) {
+ssize_t net_sys_write(Sycx_stream *st, void *buf, size_t nbytes) {
     return write(st->fd, buf, nbytes);
 }
 
-ssize_t net_sys_writev(SysStream *st, struct iovec *iovec, int iovcnt) {
+ssize_t net_sys_writev(Sycx_stream *st, struct iovec *iovec, int iovcnt) {
     return writev(st->fd, iovec, iovcnt);
 }
 
-ssize_t net_sys_read(SysStream *st, void *buf, size_t nbytes) {
+ssize_t net_sys_read(Sycx_stream *st, void *buf, size_t nbytes) {
     return read(st->fd, buf, nbytes);
 }
 
 #ifdef WS_SENDFILE
-ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd) {
+ssize_t net_sys_sendfile(Sycx_stream *st, sendfiledata *sfd) {
     ssize_t ret = 0;
     off_t fileoffset = sfd->offset;
     if(sfd->fd->fd != -1) {
@@ -177,11 +178,11 @@
 }
 #endif
 
-void net_sys_close(SysStream *st) {
+void net_sys_close(Sycx_stream *st) {
     system_close(st->fd);
 }
 
-void net_sys_setmode(SysStream *st, int mode) {
+void net_sys_setmode(Sycx_stream *st, int mode) {
     int flags;
     if (-1 == (flags = fcntl(st->fd, F_GETFL, 0))) {
         flags = 0;
@@ -199,7 +200,7 @@
     }
 }
 
-int net_sys_poll(SysStream *st, EventHandler *ev, int events, Event *cb) {
+int net_sys_poll(Sycx_stream *st, EventHandler *ev, int events, Event *cb) {
     switch(events) {
         default: return -1;
         case IO_POLL_NONE: return ev_remove_poll(ev, st->fd);
@@ -211,7 +212,7 @@
 
 #elif defined(XP_WIN32)
 
-ssize_t net_sys_write(SysStream *st, void *buf, size_t nbytes) {
+ssize_t net_sys_write(Sycx_stream *st, void *buf, size_t nbytes) {
     int ret = send(st->fd, buf, nbytes, 0);
     if(ret == SOCKET_ERROR) {
         return IO_ERROR;
@@ -219,11 +220,11 @@
     return ret;
 }
 
-ssize_t net_sys_writev(SysStream *st, struct iovec *iovec, int iovcnt) {
+ssize_t net_sys_writev(Sycx_stream *st, struct iovec *iovec, int iovcnt) {
     // TODO
 }
 
-ssize_t net_sys_read(SysStream *st, void *buf, size_t nbytes) {
+ssize_t net_sys_read(Sycx_stream *st, void *buf, size_t nbytes) {
     int ret = recv(st->fd, buf, nbytes, 0);
     if(ret == SOCKET_ERROR) {
         return IO_ERROR;
@@ -231,11 +232,11 @@
     return ret;
 }
 
-ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd) {
+ssize_t net_sys_sendfile(Sycx_stream *st, sendfiledata *sfd) {
     // TODO
 }
 
-void net_sys_close(SysStream *st) {
+void net_sys_close(Sycx_stream *st) {
     closesocket(st->fd);
 }
 
@@ -250,6 +251,7 @@
     HttpStream *st = pool_malloc(pool, sizeof(HttpStream));
     st->st = http_io_funcs;
     st->fd = fd;
+    st->written = 0;
     st->max_read = 0;
     st->read = 0;
     st->read_total = 0;
@@ -747,7 +749,7 @@
 ssize_t net_printf(SYS_NETFD fd, char *format, ...) {
     va_list arg;
     va_start(arg, format);
-    sstr_t buf = ucx_vasprintf(ucx_default_allocator(), format, arg);
+    cxmutstr buf = cx_vasprintf_a(cxDefaultAllocator, format, arg);
     ssize_t r = buf.length > 0 ? net_write(fd, buf.ptr, buf.length) : 0;
     free(buf.ptr);
     va_end(arg);
--- a/src/server/util/io.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/io.h	Sun Nov 06 15:53:32 2022 +0100
@@ -56,7 +56,7 @@
 #define IO_POLL_OUT         2
     
 typedef struct IOStream     IOStream;
-typedef struct SysStream    SysStream;
+typedef struct Sycx_stream    Sycx_stream;
 typedef struct HttpStream   HttpStream;
 
 typedef ssize_t(*io_write_f)(IOStream *, void *, size_t);
@@ -80,7 +80,7 @@
     int           io_errno;
 };
 
-struct SysStream {
+struct Sycx_stream {
     IOStream st;
 #ifdef XP_UNIX
     int      fd;
@@ -154,15 +154,15 @@
 
 
 /* system stream */
-IOStream* sysstream_new(pool_handle_t *pool, SYS_SOCKET fd);
+IOStream* sycx_stream_new(pool_handle_t *pool, SYS_SOCKET fd);
 
-ssize_t net_sys_write(SysStream *st, void *buf, size_t nbytes);
-ssize_t net_sys_writev(SysStream *st, struct iovec *iovec, int iovcnt);
-ssize_t net_sys_read(SysStream *st, void *buf, size_t nbytes);
-ssize_t net_sys_sendfile(SysStream *st, sendfiledata *sfd);
-void net_sys_close(SysStream *st);
-void net_sys_setmode(SysStream *st, int mode);
-int net_sys_poll(SysStream *st, EventHandler *ev, int events, Event *cb);
+ssize_t net_sys_write(Sycx_stream *st, void *buf, size_t nbytes);
+ssize_t net_sys_writev(Sycx_stream *st, struct iovec *iovec, int iovcnt);
+ssize_t net_sys_read(Sycx_stream *st, void *buf, size_t nbytes);
+ssize_t net_sys_sendfile(Sycx_stream *st, sendfiledata *sfd);
+void net_sys_close(Sycx_stream *st);
+void net_sys_setmode(Sycx_stream *st, int mode);
+int net_sys_poll(Sycx_stream *st, EventHandler *ev, int events, Event *cb);
 
 /* http stream */
 IOStream* httpstream_new(pool_handle_t *pool, IOStream *fd);
--- a/src/server/util/object.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/object.c	Sun Nov 06 15:53:32 2022 +0100
@@ -26,7 +26,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "../public/nsapi.h"
 
--- a/src/server/util/pool.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/pool.c	Sun Nov 06 15:53:32 2022 +0100
@@ -78,6 +78,14 @@
 /* Pool global statistics */
 static pool_global_stats_t pool_global_stats;
 
+/* ucx allocator pool class */
+static cx_allocator_class pool_allocator_class = {
+    (void *(*)(void *,size_t )) pool_malloc,
+    (void *(*)(void *,void *, size_t )) pool_realloc,
+    (void *(*)(void *,size_t ,size_t  )) pool_calloc,
+    (void (*)(void *, void *))pool_free
+};
+
 static int 
 pool_internal_init()
 {
@@ -134,6 +142,10 @@
     return REQ_PROCEED;
 }
 
+CxAllocator* pool_allocator(pool_handle_t *pool) {
+    return &((pool_t *)pool)->allocator;
+}
+
 static block_t *
 _create_block(pool_t *pool, int size)
 {
@@ -250,7 +262,10 @@
         //if (pool_global_stats.lock == NULL) { // TODO: remove
         //    pool_internal_init();
         //}
-
+        
+        newpool->allocator.cl = &pool_allocator_class;
+        newpool->allocator.data = newpool;
+        
         newpool->used_blocks = NULL;
         newpool->free_blocks = NULL;
         newpool->free_size = 0;
@@ -718,8 +733,8 @@
 #endif /* PER_POOL_STATISTICS */
 
 // new
-sstr_t sstrdup_pool(pool_handle_t *pool, sstr_t s) {
-    sstr_t newstring;
+cxmutstr cx_strdup_pool(pool_handle_t *pool, cxmutstr s) {
+    cxmutstr newstring;
     newstring.ptr = (char*)pool_malloc(pool, s.length + 1);
     if (newstring.ptr != NULL) {
         newstring.length = s.length;
--- a/src/server/util/pool.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/pool.h	Sun Nov 06 15:53:32 2022 +0100
@@ -53,7 +53,7 @@
  */
 
 // new
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #ifndef NETSITE_H
 //include "netsite.h"
@@ -83,7 +83,9 @@
 
 NSAPI_PUBLIC int INTpool_init(pblock *pb, Session *sn, Request *rq);
 
-sstr_t sstrdup_pool(pool_handle_t *pool, sstr_t s);
+cxmutstr cx_strdup_pool(pool_handle_t *pool, cxmutstr s);
+
+CxAllocator* pool_allocator(pool_handle_t *pool);
 
 #ifdef DEBUG_CACHES
 NSAPI_PUBLIC int INTpool_service_debug(pblock *pb, Session *sn, Request *rq);
--- a/src/server/util/pool_pvt.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/pool_pvt.h	Sun Nov 06 15:53:32 2022 +0100
@@ -37,6 +37,7 @@
 #ifndef BASE_POOL_H
 #include "pool.h"
 #include <inttypes.h>
+#include <cx/allocator.h>
 #endif /* BASE_POOL_H */
 
 /*
@@ -147,6 +148,7 @@
  */
 typedef struct pool_t pool_t;
 struct pool_t {
+    CxAllocator allocator;      /* ucx allocator interface */
     block_t  *curr_block;       /* current block being used */
     block_t  *used_blocks;      /* blocks that are all used up */
     block_t  *free_blocks;      /* blocks that are free */
--- a/src/server/util/strbuf.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/strbuf.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #include "strbuf.h"
 
@@ -47,14 +47,14 @@
 }
 
 void sbuf_puts(sbuf_t *buf, char *str) {
-    sbuf_append(buf, sstr(str));
+    sbuf_append(buf, cx_str(str));
 }
 
 void sbuf_put(sbuf_t *buf, char chr) {
-    sbuf_append(buf, sstrn(&chr, 1));
+    sbuf_append(buf, (cxstring){&chr, 1});
 }
 
-void sbuf_append(sbuf_t *buf, sstr_t str) {
+void sbuf_append(sbuf_t *buf, cxstring str) {
     if (buf->length + str.length >= buf->size) {
         buf->size *= 2;
         buf->ptr = realloc(buf->ptr, buf->size);
--- a/src/server/util/strbuf.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/strbuf.h	Sun Nov 06 15:53:32 2022 +0100
@@ -29,7 +29,7 @@
 #ifndef STRBUF_H
 #define	STRBUF_H
 
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -48,7 +48,7 @@
 
 void sbuf_put(sbuf_t *buf, char chr);
 
-void sbuf_append(sbuf_t *buf, sstr_t str);
+void sbuf_append(sbuf_t *buf, cxstring str);
 
 void sbuf_free(sbuf_t *buf);
 
--- a/src/server/util/util.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/util.c	Sun Nov 06 15:53:32 2022 +0100
@@ -53,9 +53,10 @@
 
 #include "../daemon/netsite.h"
 #include "../public/nsapi.h"
-#include <ucx/string.h>
-#include <ucx/mempool.h>
-#include <ucx/utils.h>
+#include <cx/string.h>
+#include <cx/mempool.h>
+#include <cx/utils.h>
+#include <cx/printf.h>
 
 #include "pblock.h"
 #include "util.h"
@@ -292,10 +293,10 @@
 NSAPI_PUBLIC int util_vasprintf(pool_handle_t *pool, char **s, const char *fmt, 
                                 va_list args)
 {
-    UcxAllocator a = util_pool_allocator(pool);
+    CxAllocator *a = pool_allocator(pool);
     va_list ap;
     va_copy(ap, args);
-    sstr_t str = ucx_vasprintf(&a, fmt, ap);
+    cxmutstr str = cx_vasprintf_a(a, fmt, ap);
     *s = str.ptr;
     return str.length;
 }
@@ -413,7 +414,7 @@
     return def;
 }
 
-int util_getboolean_s(scstr_t s, int def) {
+int util_getboolean_s(cxstring s, int def) {
     if(s.length == 0) {
         return def;
     }
@@ -439,11 +440,11 @@
 }
 
 NSAPI_PUBLIC const char* util_resource_name(const char *url) {
-    scstr_t urlstr = scstr(url);
+    cxstring urlstr = cx_str(url);
     if(urlstr.ptr[urlstr.length-1] == '/') {
         urlstr.length--;
     }
-    scstr_t resname = scstrrchr(urlstr, '/');
+    cxstring resname = cx_strrchr(urlstr, '/');
     if(resname.length > 1) {
         return resname.ptr+1;
     } else {
@@ -582,22 +583,22 @@
 
 
 NSAPI_PUBLIC
-sstr_t util_path_append(pool_handle_t *pool, char *path, char *ch) {
-    sstr_t parent = sstr(path);
-    sstr_t child = sstr(ch);
-    sstr_t newstr;
+cxmutstr util_path_append(pool_handle_t *pool, char *path, char *ch) {
+    cxmutstr parent = cx_mutstr(path);
+    cxmutstr child = cx_mutstr(ch);
+    cxmutstr newstr;
     
-    UcxAllocator a = util_pool_allocator(pool); 
+    CxAllocator *a = pool_allocator(pool); 
     if(parent.ptr[parent.length-1] == '/') {
-        newstr = sstrcat_a(&a, 2, parent, child);
+        newstr = cx_strcat_a(a, 2, parent, child);
     } else {
-        newstr = sstrcat_a(&a, 3, parent, S("/"), child);
+        newstr = cx_strcat_a(a, 3, parent, cx_str("/"), child);
     }
     
     return newstr;
 }
 
-sstr_t util_path_remove_last(sstr_t path) {
+cxmutstr util_path_remove_last(cxmutstr path) {
     int i;
     for(i=path.length-1;i>=0;i--) {
         char c = path.ptr[i];
@@ -614,7 +615,7 @@
     return path;
 }
 
-void util_add_ppath(sstr_t root, sstr_t path, pblock *vars) {
+void util_add_ppath(cxmutstr root, cxmutstr path, pblock *vars) {
     // concat path
     size_t length = root.length + path.length;
     char *translated_path = alloca(length);
@@ -632,17 +633,6 @@
 }
 
 
-UcxAllocator util_pool_allocator(pool_handle_t *pool) {
-    UcxAllocator a;
-    a.malloc = (ucx_allocator_malloc)pool_malloc;
-    a.calloc = (ucx_allocator_calloc)pool_calloc;
-    a.realloc = (ucx_allocator_realloc)pool_realloc;
-    a.free = (ucx_allocator_free)pool_free;
-    a.pool = pool;
-    return a;
-}
-
-
 // new - code from params.cpp
 NSAPI_PUBLIC pblock* util_parse_param(pool_handle_t *pool, char *query) {
     pblock *pb = pblock_create_pool(pool, 32);
@@ -701,21 +691,6 @@
     return pb;
 }
 
-// TODO: remove
-sstr_t sstrdup_mp(UcxMempool *pool, sstr_t s) {
-    sstr_t newstring;
-    newstring.ptr = (char*)ucx_mempool_malloc(pool, s.length + 1);
-    if (newstring.ptr != NULL) {
-        newstring.length = s.length;
-        newstring.ptr[newstring.length] = 0;
-
-        memcpy(newstring.ptr, s.ptr, s.length);
-    }
-
-    return newstring;
-}
-
-
 
 /* ---------------------------- util_mstr2num ----------------------------- */
 
@@ -1020,9 +995,9 @@
     return gmtime_r(clock, res);
 }
 
-int util_isdate(char *str) {
-    sstr_t datestr = sstr(str);  
-    sstr_t example = S("Sun, 06 Nov 1994 08:49:37 GMT");
+int util_isdate(const char *str) {
+    cxstring datestr = cx_str(str);  
+    cxstring example = cx_str("Sun, 06 Nov 1994 08:49:37 GMT");
     
     if(datestr.length != example.length) {
         return 0;
@@ -1049,7 +1024,7 @@
         }
     }
     
-    if(!sstrsuffix(datestr, S("GMT"))) {
+    if(!cx_strsuffix(datestr, cx_str("GMT"))) {
         return 0;
     }
     
--- a/src/server/util/util.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/util.h	Sun Nov 06 15:53:32 2022 +0100
@@ -35,7 +35,7 @@
 #define BASE_UTIL_H
 
 #include "../daemon/netsite.h"
-#include <ucx/string.h>
+#include <cx/string.h>
 #include "pool.h"
 
 #ifdef XP_UNIX
@@ -202,7 +202,7 @@
 NSAPI_PUBLIC PRBool INTutil_format_http_version(const char *v, int *protv_num, char *buffer, int size);
 
 NSAPI_PUBLIC int INTutil_getboolean(const char *v, int def);
-int util_getboolean_s(scstr_t s, int def);
+int util_getboolean_s(cxstring s, int def);
 
 // new
 NSAPI_PUBLIC int util_strtoint(const char *str, int64_t *value);
@@ -241,16 +241,14 @@
 
 /* path utils */
 NSAPI_PUBLIC
-sstr_t util_path_append(pool_handle_t *pool, char *path, char *child);
+cxmutstr util_path_append(pool_handle_t *pool, char *path, char *child);
 NSAPI_PUBLIC
-sstr_t util_path_remove_last(sstr_t path);
+cxmutstr util_path_remove_last(cxmutstr path);
 NSAPI_PUBLIC
-void util_add_ppath(sstr_t root, sstr_t path, pblock *vars);
+void util_add_ppath(cxmutstr root, cxmutstr path, pblock *vars);
 
-/* ucx utils */
-UcxAllocator util_pool_allocator(pool_handle_t *pool);
 
-int util_isdate(char *str);
+int util_isdate(const char *str);
 
 /* --- End common function prototypes --- */
 
--- a/src/server/util/writer.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/writer.c	Sun Nov 06 15:53:32 2022 +0100
@@ -104,7 +104,7 @@
     }
 }
 
-int writer_puts(Writer *w, sstr_t s) {
+int writer_puts(Writer *w, cxstring s) {
     return writer_put(w, s.ptr, s.length);
 }
 
--- a/src/server/util/writer.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/util/writer.h	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,7 @@
 #define WRITER_H
 
 #include "../public/nsapi.h"
-#include <ucx/string.h>
+#include <cx/string.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -47,7 +47,8 @@
     int error;
 } Writer;
 
-
+#define writer_put_lit(w, str) writer_put(w, str, sizeof(str)-1)
+#define writer_put_str(w, str) writer_put(w, str, strlen(str))
 
 void writer_init(Writer *w, SYS_NETFD fd, char *buf, size_t len);
 
@@ -57,7 +58,7 @@
 
 int writer_put(Writer *w, const char *s, size_t len);
 
-int writer_puts(Writer *w, sstr_t s);
+int writer_puts(Writer *w, cxstring s);
 
 int writer_putc(Writer *w, char c);
 
--- a/src/server/webdav/multistatus.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/multistatus.c	Sun Nov 06 15:53:32 2022 +0100
@@ -33,7 +33,8 @@
 #include "../daemon/protocol.h"
 #include "../util/platform.h"
 
-#include <ucx/string.h>
+#include <cx/string.h>
+#include <cx/hash_map.h>
 
 #include "multistatus.h"
 
@@ -51,48 +52,48 @@
     ms->response.addresource = multistatus_addresource;
     ms->sn = sn;
     ms->rq = rq;
-    ms->namespaces = ucx_map_new_a(session_get_allocator(ms->sn), 8);
+    ms->namespaces = cxHashMapCreate(pool_allocator(sn->pool), 8);
     ms->proppatch = FALSE;
     if(!ms->namespaces) {
         return NULL;
     }
-    if(ucx_map_cstr_put(ms->namespaces, "D", webdav_dav_namespace())) {
+    if(cxMapPut(ms->namespaces, cx_hash_key_str("D"), webdav_dav_namespace())) {
         return NULL;
     }
     return ms;
 }
 
 static int send_xml_root(Multistatus *ms, Writer *out) {
-    writer_puts(out, S("<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
-                       "<D:multistatus"));
+    writer_put_lit(out, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
+                        "<D:multistatus");
     
     // write the namespaces definitions
     // key is the namespace prefix
     // the map always contains the "DAV:" namespace with the prefix "D"
-    UcxMapIterator i = ucx_map_iterator(ms->namespaces);
-    WSNamespace *ns;
-    UCX_MAP_FOREACH(key, ns, i) {
-        writer_puts(out, S(" xmlns:"));
-        writer_put(out, key.data, key.len);
-        writer_puts(out, S("=\""));
-        writer_puts(out, sstr((char*)ns->href));
-        writer_puts(out, S("\""));
+    CxIterator i = cxMapIterator(ms->namespaces);
+    cx_foreach(CxMapEntry*, entry, i) {
+        WSNamespace *ns = entry->value;
+        writer_put_lit(out, " xmlns:");
+        writer_put    (out, entry->key->data.str, entry->key->len);
+        writer_put_lit(out, "=\"");
+        writer_put_str(out, (char*)ns->href);
+        writer_put_lit(out, "\"");
     }
     
-    writer_puts(out, S(">\n"));
+    writer_put_lit(out, ">\n");
     
     return out->error;
 }
 
 static void send_nsdef(WSNamespace *ns, Writer *out) {
-    writer_puts(out, S(" xmlns:"));
-    writer_puts(out, sstr((char*)ns->prefix));
-    writer_puts(out, S("=\""));
-    writer_puts(out, sstr((char*)ns->href));
-    writer_putc(out, '\"');
+    writer_put_lit(out, " xmlns:");
+    writer_put_str(out, (char*)ns->prefix);
+    writer_put_lit(out, "=\"");
+    writer_put_str(out, (char*)ns->href);
+    writer_putc   (out, '\"');
 }
 
-static void send_string_escaped(Writer *out, sstr_t str) {
+static void send_string_escaped(Writer *out, cxmutstr str) {
     char *begin = str.ptr;
     char *end = begin;
     char *escape = NULL;
@@ -150,10 +151,10 @@
         Writer *out)
 {
     // write: "<prefix:name"
-    writer_putc(out, '<');
-    writer_puts(out, sstr((char*)property->namespace->prefix));
-    writer_putc(out, ':');
-    writer_puts(out, sstr((char*)property->name));
+    writer_putc   (out, '<');
+    writer_put_str(out, (char*)property->namespace->prefix);
+    writer_putc   (out, ':');
+    writer_put_str(out, (char*)property->name);
     
     // send additional namespace definitions required for the value
     WebdavNSList *def = nsdef;
@@ -164,9 +165,9 @@
     
     // send xml lang attribute
     if(property->lang) {
-        writer_puts(out, S(" xml:lang=\""));
-        writer_puts(out, sstr((char*)property->lang));
-        writer_putc(out, '\"');
+        writer_put_lit(out, " xml:lang=\"");
+        writer_put_str(out, (char*)property->lang);
+        writer_putc   (out, '\"');
     }
     
     // end property tag and write content
@@ -202,55 +203,54 @@
         }
         
         // end tag
-        writer_puts(out, S("</"));
-        writer_puts(out, sstr((char*)property->namespace->prefix));
-        writer_putc(out, ':');
-        writer_puts(out, sstr((char*)property->name));
-        writer_putc(out, '>');
+        writer_put_lit(out, "</");
+        writer_put_str(out, (char*)property->namespace->prefix);
+        writer_putc   (out, ':');
+        writer_put_str(out, (char*)property->name);
+        writer_putc   (out, '>');
     } else {
-        writer_puts(out, S("/>"));
+        writer_put_lit(out, "/>");
     }
     
     return out->error;
 }
 
 static int send_response_tag(Multistatus *ms, MSResponse *rp, Writer *out) {
-    writer_puts(out, S(" <D:response>\n"
-                       "  <D:href>"));
-    //writer_puts(out, sstr(rp->resource.href));
-    send_string_escaped(out, sstr(rp->resource.href));
-    writer_puts(out, S("</D:href>\n"));
+    writer_put_lit(out, " <D:response>\n"
+                        "  <D:href>");
+    send_string_escaped(out, cx_mutstr(rp->resource.href));
+    writer_put_lit(out, "</D:href>\n");
     
     WSBool writeContent = ms->proppatch ? FALSE : TRUE;
     
     if(rp->plist_begin) {
-        writer_puts(out, S("  <D:propstat>\n"
-                           "   <D:prop>\n"));
+        writer_put_lit(out, "  <D:propstat>\n"
+                            "   <D:prop>\n");
         // send properties
         PropertyOkList *p = rp->plist_begin;
         while(p) {
-            writer_puts(out, S("    "));
+            writer_put_lit(out, "    ");
             if(send_property(ms, p->property, p->nsdef, writeContent, out)) {
                 return out->error;
             }
-            writer_puts(out, S("\n"));
+            writer_put_lit(out, "\n");
             p = p->next;
         }
         
-        writer_puts(out, S("   </D:prop>\n"
-                           "   <D:status>HTTP/1.1 200 OK</D:status>\n"
-                           "  </D:propstat>\n"));
+        writer_put_lit(out, "   </D:prop>\n"
+                            "   <D:status>HTTP/1.1 200 OK</D:status>\n"
+                            "  </D:propstat>\n");
     }
     
     // send error properties
     PropertyErrorList *error = rp->errors;
     while(error) {
-        writer_puts(out, S("  <D:propstat>\n"
-                           "   <D:prop>\n"));
+        writer_put_lit(out, "  <D:propstat>\n"
+                            "   <D:prop>\n");
         
         WebdavPList *errprop = error->begin;
         while(errprop) {
-            writer_puts(out, S("    "));
+            writer_put_lit(out, "    ");
             if(send_property(ms, errprop->property, NULL, FALSE, out)) {
                 return out->error;
             }
@@ -267,24 +267,24 @@
             statuscode[3] = ' ';
             sclen = 4;
         }
-        writer_puts(out, S("   </D:prop>\n"
-                           "   <D:status>HTTP/1.1 "));
+        writer_put_lit(out, "   </D:prop>\n"
+                            "   <D:status>HTTP/1.1 ");
         writer_put(out, statuscode, sclen);
         const char *status_msg = protocol_status_message(error->status);
         if(status_msg) {
             writer_put(out, status_msg, strlen(status_msg));
         } else {
-            writer_puts(out, S("Server Error"));
+            writer_put_lit(out, "Server Error");
         }
-        writer_puts(out, S("</D:status>\n"
-                           "  </D:propstat>\n"));
+        writer_put_lit(out, "</D:status>\n"
+                            "  </D:propstat>\n");
         
         
         error = error->next;
     }
     
     // end response tag
-    writer_puts(out, S(" </D:response>\n"));
+    writer_put_lit(out, " </D:response>\n");
     
     return out->error;
 }
@@ -322,7 +322,7 @@
     }
     
     // end multistatus
-    writer_puts(out, S("</D:multistatus>\n"));
+    writer_put_lit(out, "</D:multistatus>\n");
     
     //printf("\n\n");
     //fflush(stdout);
@@ -355,7 +355,7 @@
     res->resource.addproperty = msresponse_addproperty;
     res->resource.close = msresponse_close;
     
-    res->properties = ucx_map_new_a(session_get_allocator(ms->sn), 32);
+    res->properties = cxHashMapCreate(pool_allocator(ms->sn->pool), 32);
     if(!res->properties) {
         return NULL;
     }
@@ -414,7 +414,6 @@
         int statuscode)
 {
     pool_handle_t *pool = response->multistatus->sn->pool;
-    UcxAllocator *a = session_get_allocator(response->multistatus->sn);
        
     response->resource.err++;
       
@@ -459,6 +458,15 @@
     return 0;
 }
 
+static CxHashKey ms_property_key(
+        CxAllocator *a,
+        const xmlChar *href,
+        const char *property_name)
+{
+    cxmutstr key_data = cx_strcat_a(a, 3, cx_str((const char*)href), (cxstring){ "\0", 1 }, cx_str(property_name));
+    return cx_hash_key_bytes((unsigned char*)key_data.ptr, key_data.length);
+}
+
 int msresponse_addproperty(
         WebdavResource *res,
         WebdavProperty *property,
@@ -486,21 +494,16 @@
     }
     
     // check if the property was already added to the resource
-    UcxAllocator *a = session_get_allocator(sn);
-    sstr_t key = sstrcat_a(
-            a,
-            3,
-            sstr((char*)property->namespace->href),
-            S("\0"),
-            sstr((char*)property->name));
-    if(ucx_map_sstr_get(response->properties, key)) {
-        a->free(a->pool, key.ptr);
+    CxAllocator *a = pool_allocator(sn->pool);
+    CxHashKey key = ms_property_key(a, property->namespace->href, property->name);
+    if(cxMapGet(response->properties, key)) {
+        cxFree(a, key.data.bytes);
         return 0;
     }
-    if(ucx_map_sstr_put(response->properties, key, property)) {
+    if(cxMapPut(response->properties, key, property)) {
         return 1; // OOM
     }
-    a->free(a->pool, key.ptr);
+    cxFree(a, key.data.bytes);
     
     // list of namespace definitions for this property
     WebdavNSList *nsdef_begin = NULL;
@@ -509,14 +512,14 @@
     // add namespace of this property to the namespace map
     // the namespace map will be used for global namespace definitions
     if(property->namespace->prefix) {
-        WSNamespace *ns = ucx_map_cstr_get(
+        WSNamespace *ns = cxMapGet(
                 response->multistatus->namespaces,
-                (const char*)property->namespace->prefix);
+                cx_hash_key_str((const char*)property->namespace->prefix));
         if(!ns) {
             // prefix is not in use -> we can add the namespace to the ns map
-            int err = ucx_map_cstr_put(
+            int err = cxMapPut(
                     response->multistatus->namespaces,
-                    (const char*)property->namespace->prefix,
+                    cx_hash_key_str((const char*)property->namespace->prefix),
                     property->namespace);
             if(err) {
                 return 1; // OOM
@@ -622,16 +625,11 @@
     }
     
     // add missing properties with status code 404
-    UcxAllocator *a = session_get_allocator(ms->sn);
+    CxAllocator *a = pool_allocator(ms->sn->pool);
     WebdavPList *pl = ms->response.op->reqprops;
     while(pl) {
-        sstr_t key = sstrcat_a(
-            a,
-            3,
-            sstr((char*)pl->property->namespace->href),
-            S("\0"),
-            sstr((char*)pl->property->name));
-        if(!ucx_map_sstr_get(response->properties, key)) {
+        CxHashKey key = ms_property_key(a, pl->property->namespace->href, pl->property->name);
+        if(!cxMapGet(response->properties, key)) {
             // property was not added to this response
             if(ms->proppatch) {
                 if(msresponse_addproperty(res, pl->property, 424)) {
@@ -668,7 +666,7 @@
     }
     
     // we don't need the properties anymore
-    ucx_map_free(response->properties);
+    cxMapDestroy(response->properties);
     
     response->resource.isclosed = TRUE;
     return ret;
--- a/src/server/webdav/multistatus.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/multistatus.h	Sun Nov 06 15:53:32 2022 +0100
@@ -30,8 +30,8 @@
 
 #include "../public/webdav.h"
 
-#include <ucx/map.h>
-#include <ucx/buffer.h>
+#include <cx/map.h>
+#include <cx/buffer.h>
 #include <libxml/tree.h>
 #include "../util/writer.h"
 
@@ -61,7 +61,7 @@
      * key: (char*) namespace prefix
      * value: WSNamespace*
      */
-    UcxMap *namespaces;
+    CxMap *namespaces;
     
     /*
      * Is this a proppatch request?
@@ -84,7 +84,7 @@
      * key: <href> null-byte <name>
      * value: WebdavProperty*
      */
-    UcxMap *properties;
+    CxMap *properties;
     
     /*
      * All properties with status != 200
--- a/src/server/webdav/operation.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/operation.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,7 +30,7 @@
 #include <stdlib.h>
 #include <errno.h>
 
-#include <ucx/list.h>
+#include <cx/list.h>
 
 #include "../daemon/session.h"
 #include "../util/pblock.h"
@@ -60,7 +60,7 @@
         Request *rq,
         WebdavBackend *dav,
         WebdavPList *reqprops,
-        UcxList *requests,
+        WebdavPropfindRequestList *requests,
         WebdavResponse *response)
 {
     WebdavOperation *op = pool_malloc(sn->pool, sizeof(WebdavOperation));
@@ -94,7 +94,7 @@
     op->parent = parent;
     
     // get first propfind object
-    WebdavPropfindRequest *propfind = op->requests->data;
+    WebdavPropfindRequest *propfind = op->requests->propfind;
     
     // execute propfind_do of the first backend for the first resource
     int ret = REQ_PROCEED;
@@ -117,6 +117,7 @@
     char   *path;
     size_t hreflen;
     size_t pathlen;
+    struct PathSearchElm *next;
 } PathSearchElm;
 
 /*
@@ -187,7 +188,7 @@
         const char *href,
         const char *path)
 {
-    WebdavPropfindRequest *request = op->requests->data;
+    WebdavPropfindRequest *request = op->requests->propfind;
     return webdav_op_iterate_children(
             vfs, request->depth, href, path, propfind_child_cb, op);
 }
@@ -199,13 +200,13 @@
     // start with second backend and request, because
     // the first one was already called by webdav_op_propfind_begin
     WebdavBackend *dav = op->dav->next;
-    UcxList *request = op->requests->next;
+    WebdavPropfindRequestList *request = op->requests->next;
     
     // call propfind_do of all remaining backends
     int ret = REQ_PROCEED;
     while(dav && request) {
         if(dav->propfind_do(
-                request->data,
+                request->propfind,
                 op->response,
                 op->parent,
                 resource,
@@ -225,11 +226,11 @@
  */
 int webdav_op_propfind_finish(WebdavOperation *op) {
     WebdavBackend *dav = op->dav;
-    UcxList *requests = op->requests;
+    WebdavPropfindRequestList *requests = op->requests;
     
     int ret = REQ_PROCEED;
     while(dav && requests) {
-        if(dav->propfind_finish(requests->data)) {
+        if(dav->propfind_finish(requests->propfind)) {
             ret = REQ_ABORTED;
         }
         
@@ -274,7 +275,7 @@
         const char *path)
 {
     WebdavProppatchRequest *orig_request = op->proppatch;
-    UcxAllocator *a = session_get_allocator(op->sn);
+    CxAllocator *a = pool_allocator(op->sn->pool);
     
     // create WebdavResource object for the requested resource
     WebdavResource *resource = op->response->addresource(op->response, href);
@@ -499,7 +500,6 @@
         vfs_op_child_func func,
         void *userdata)
 {
-    UcxAllocator *a = session_get_allocator(vfs->sn);
     pool_handle_t *pool = vfs->sn->pool;
     
     PathSearchElm *start_elm = pool_malloc(pool, sizeof(PathSearchElm));
@@ -507,12 +507,10 @@
     start_elm->path = pool_strdup(pool, path ? path : "");
     start_elm->hreflen = href ? strlen(href) : 0;
     start_elm->pathlen = path ? strlen(path) : 0;
+    start_elm->next = NULL;
     
-    UcxList *stack = ucx_list_prepend_a(a, NULL, start_elm);
-    UcxList *stack_end = stack;
-    if(!stack) {
-        return 1;
-    }
+    PathSearchElm *stack = start_elm;
+    PathSearchElm *stack_end = start_elm;
     
     // reusable buffer for full child path and href
     char *newpath = pool_malloc(pool, 256);
@@ -523,7 +521,7 @@
     
     int err = 0;
     while(stack && !err) {
-        PathSearchElm *cur_elm = stack->data;
+        PathSearchElm *cur_elm = stack;
         
         // when newpath is initialized with the parent path
         // set path_buf_init to TRUE
@@ -596,38 +594,41 @@
                 
                 PathSearchElm *new_elm = pool_malloc(pool,
                                             sizeof(PathSearchElm));
+                if(!new_elm) {
+                    err = 1;
+                    break;
+                }
                 new_elm->href = hrefcp;
                 new_elm->path = pathcp;
                 new_elm->hreflen = childhreflen;
                 new_elm->pathlen = childpathlen;
+                new_elm->next = NULL;
                 
                 // add the new_elm to the stack
-                // stack_end is always not NULL here, because we remove
+                // stack_end is always not NULL here, because the loop is
+                // running as long as we have a stack and we remove
                 // the first stack element at the end of the loop
-                UcxList *newlistelm = ucx_list_append_a(a, stack_end, new_elm);
-                if(!newlistelm) {
-                    err = 1;
-                    break;
-                }
-                stack_end = newlistelm;
+                stack_end->next = new_elm;
+                stack_end = new_elm;
             }
         }
         
         vfs_closedir(dir);
         
+        stack = stack->next;
+        
         pool_free(pool, cur_elm->path);
         pool_free(pool, cur_elm->href);
         pool_free(pool, cur_elm);
-        
-        stack = ucx_list_remove_a(a, stack, stack);
     }
     
     // in case of an error, we have to free all remaining stack elements
-    UCX_FOREACH(elm, stack) {
-        char *data = elm->data;
-        if(data != path) {
-            pool_free(pool, data);
-        }
+    for(PathSearchElm *elm=stack;elm;) {
+        PathSearchElm *next_elm = elm->next;
+        pool_free(pool, elm->path);
+        pool_free(pool, elm->href);
+        pool_free(pool, elm);
+        elm = next_elm;
     }
     
     return err;
--- a/src/server/webdav/operation.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/operation.h	Sun Nov 06 15:53:32 2022 +0100
@@ -30,8 +30,8 @@
 #define OPERATION_H
 
 #include "../public/webdav.h"
-#include <ucx/list.h>
-#include <ucx/map.h>
+#include <cx/list.h>
+#include <cx/map.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -45,20 +45,20 @@
 typedef struct CopyResource       CopyResource;
     
 struct WebdavOperation {
-    WebdavBackend          *dav;
-    Request                *rq;
-    Session                *sn;
+    WebdavBackend             *dav;
+    Request                   *rq;
+    Session                   *sn;
     
-    WebdavProppatchRequest *proppatch;    /* proppatch request or NULL */
-    WebdavPList            *reqprops;     /* requested properties */
-    UcxList                *requests;     /* backend specific request objects */
+    WebdavProppatchRequest    *proppatch;    /* proppatch request or NULL */
+    WebdavPList               *reqprops;     /* requested properties */
+    WebdavPropfindRequestList *requests;     /* backend specific request objects */
     
-    WebdavResponse         *response;
+    WebdavResponse            *response;
     
-    response_close_func    response_close;
+    response_close_func       response_close;
     
-    VFS_DIR                parent;        /* current directory */
-    struct stat            *stat;         /* current stat object */
+    VFS_DIR                   parent;        /* current directory */
+    struct stat               *stat;         /* current stat object */
 };
 
 struct WebdavVFSOperation {
@@ -87,7 +87,7 @@
 
 struct CopyResource {
     WebdavResource resource;
-    UcxMap *properties;
+    CxMap *properties;
 };
 
 enum WebdavVFSOpType {
@@ -118,7 +118,7 @@
         Request *rq,
         WebdavBackend *dav,
         WebdavPList *reqprops,
-        UcxList *requests,
+        WebdavPropfindRequestList *requests,
         WebdavResponse *response);
 
 int webdav_op_propfind_begin(
--- a/src/server/webdav/requestparser.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/requestparser.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,13 +30,16 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <ucx/string.h>
-#include <ucx/utils.h>
-#include <ucx/map.h>
+#include <cx/string.h>
+#include <cx/utils.h>
+#include <cx/map.h>
+#include <cx/hash_map.h>
 
 #include "requestparser.h"
 #include "webdav.h"
 
+#include "../util/pool.h"
+
 #define xstreq(a, b) !strcmp((const char*)a, (const char*)b)
 
 void proplist_free(pool_handle_t *pool, WebdavPList *list) {
@@ -63,7 +66,7 @@
 static int parse_prop(
         Session *sn,
         xmlNode *node,
-        UcxMap *propmap,
+        CxMap *propmap,
         WebdavPList **plist_begin,
         WebdavPList **plist_end,
         size_t *propcount,
@@ -80,14 +83,14 @@
         const char* name = (const char*)pnode->name;
         
         // check for prop duplicates
-        UcxKey k = webdav_property_key((const char*)ns, (const char*)name);
-        if(!k.data) {
+        CxHashKey k = webdav_property_key((const char*)ns, (const char*)name);
+        if(!k.data.bytes) {
             *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM;
             return 1;
         }
-        void *c = ucx_map_get(propmap, k);
+        void *c = cxMapGet(propmap, k);
         if(!c) {
-            if(ucx_map_put(propmap, k, (void*)1)) {
+            if(cxMapPut(propmap, k, (void*)1)) {
                 *error = proppatch ? PROPPATCH_PARSER_OOM : PROPFIND_PARSER_OOM;
             }
             
@@ -111,7 +114,7 @@
             *error = PROPPATCH_PARSER_DUPLICATE;
         }
         
-        free((void*)k.data);
+        free((void*)k.data.str);
         if(*error) {
             return 1;
         }
@@ -156,7 +159,8 @@
         return NULL;
     }
     
-    UcxMap *propmap = ucx_map_new(32);
+    CxAllocator *a = pool_allocator(sn->pool);
+    CxMap *propmap = cxHashMapCreate(a, 32); // value: intptr_t
     if(!propmap) {
         *error = PROPFIND_PARSER_OOM;
         xmlFreeDoc(doc);
@@ -190,7 +194,7 @@
         node = node->next;
     }
     
-    ucx_map_free(propmap); // no allocated content must be freed
+    cxMapDestroy(propmap); // no allocated content must be freed
     
     if(ret) {
         // parse_prop failed
@@ -295,7 +299,10 @@
     // ret vars
     *error = 0;
     
-    UcxMap *propmap = ucx_map_new(32); // map for duplicate checking
+    CxAllocator *a = pool_allocator(sn->pool);
+    // map for duplicate checking
+    // value type: intptr_t
+    CxMap *propmap = cxHashMapCreate(a, 32);
     if(!propmap) {
         *error = PROPPATCH_PARSER_OOM;
         xmlFreeDoc(doc);
@@ -350,7 +357,7 @@
         node = node->next;
     }
     
-    ucx_map_free(propmap); // allocated content must not be freed
+    cxMapDestroy(propmap); // allocated content must not be freed
     
     if(set_count + remove_count == 0) {
         *error = PROPPATCH_PARSER_NO_PROPERTIES;
--- a/src/server/webdav/requestparser.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/requestparser.h	Sun Nov 06 15:53:32 2022 +0100
@@ -32,7 +32,7 @@
 #include "../public/webdav.h"
 
 #include <libxml/tree.h>
-#include <ucx/buffer.h>
+#include <cx/buffer.h>
 
 #ifdef __cplusplus
 extern "C" {
--- a/src/server/webdav/webdav.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/webdav.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,8 +30,11 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <ucx/buffer.h>
-#include <ucx/list.h>
+#include <cx/buffer.h>
+#include <cx/list.h>
+#include <cx/linked_list.h>
+#include <cx/hash_map.h>
+#include <cx/printf.h>
 
 #include "webdav.h"
 
@@ -53,14 +56,14 @@
  * key: http method name (string)
  * value: SAF fptr
  */
-static UcxMap *method_handler_map;
+static CxMap *method_handler_map;
 
 /*
  * webdav backend types
  * key: backend name (string)
  * value: WebdavBackend*
  */
-static UcxMap *webdav_type_map;
+static CxMap *webdav_type_map;
 
 static WebdavBackend default_backend;
 
@@ -89,11 +92,11 @@
     WebdavType *webdavType = malloc(sizeof(WebdavType));
     webdavType->init = webdavInit;
     webdavType->create = webdavCreate;
-    return ucx_map_cstr_put(webdav_type_map, name, webdavType);
+    return cxMapPut(webdav_type_map, cx_hash_key_str(name), webdavType);
 }
 
-WebdavType* webdav_get_type(scstr_t dav_class) {
-    return ucx_map_sstr_get(webdav_type_map, dav_class);
+WebdavType* webdav_get_type(cxstring dav_class) {
+    return cxMapGet(webdav_type_map, cx_hash_key_bytes((unsigned const char *)dav_class.ptr, dav_class.length));
 }
 
 void* webdav_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, WebdavType *dav_class, WSConfigNode *config, int *error) {
@@ -110,7 +113,7 @@
 }
 
 WebdavBackend* webdav_create(Session *sn, Request *rq, const char *dav_class, pblock *pb, void *initData) {
-    WebdavType *webdavType = ucx_map_cstr_get(webdav_type_map, dav_class);
+    WebdavType *webdavType = cxMapGet(webdav_type_map, cx_hash_key_str(dav_class));
     if(!webdavType) {
         log_ereport(LOG_MISCONFIG, "webdav_create: unkown dav type %s", dav_class);
         return NULL;
@@ -127,43 +130,43 @@
     }
     webdav_is_initialized = TRUE;
     
-    webdav_type_map = ucx_map_new(8);
+    webdav_type_map = cxHashMapCreate(cxDefaultAllocator, 8);
     if(!webdav_type_map) {
         return REQ_ABORTED;
     }
     
-    method_handler_map = ucx_map_new(64);
+    method_handler_map = cxHashMapCreate(cxDefaultAllocator, 64);
     if(!method_handler_map) {
         return REQ_ABORTED;
     }
     
     init_default_backend();
-    ucx_map_cstr_put(webdav_type_map, "default", &default_backend);
+    cxMapPut(webdav_type_map, cx_hash_key_str("default"), &default_backend);
     
-    ucx_map_cstr_put(method_handler_map, "OPTIONS", webdav_options);
-    ucx_map_cstr_put(method_handler_map, "PROPFIND", webdav_propfind);
-    ucx_map_cstr_put(method_handler_map, "PROPPATCH", webdav_proppatch);
-    ucx_map_cstr_put(method_handler_map, "MKCOL", webdav_mkcol);
-    ucx_map_cstr_put(method_handler_map, "POST", webdav_post);
-    ucx_map_cstr_put(method_handler_map, "DELETE", webdav_delete);
-    ucx_map_cstr_put(method_handler_map, "PUT", webdav_put);
-    ucx_map_cstr_put(method_handler_map, "COPY", webdav_copy);
-    ucx_map_cstr_put(method_handler_map, "MOVE", webdav_move);
-    ucx_map_cstr_put(method_handler_map, "LOCK", webdav_lock);
-    ucx_map_cstr_put(method_handler_map, "UNLOCK", webdav_unlock);
-    ucx_map_cstr_put(method_handler_map, "REPORT", webdav_report);
-    ucx_map_cstr_put(method_handler_map, "ACL", webdav_acl);
+    cxMapPut(method_handler_map, cx_hash_key_str("OPTIONS"), webdav_options);
+    cxMapPut(method_handler_map, cx_hash_key_str("PROPFIND"), webdav_propfind);
+    cxMapPut(method_handler_map, cx_hash_key_str("PROPPATCH"), webdav_proppatch);
+    cxMapPut(method_handler_map, cx_hash_key_str("MKCOL"), webdav_mkcol);
+    cxMapPut(method_handler_map, cx_hash_key_str("POST"), webdav_post);
+    cxMapPut(method_handler_map, cx_hash_key_str("DELETE"), webdav_delete);
+    cxMapPut(method_handler_map, cx_hash_key_str("PUT"), webdav_put);
+    cxMapPut(method_handler_map, cx_hash_key_str("COPY"), webdav_copy);
+    cxMapPut(method_handler_map, cx_hash_key_str("MOVE"), webdav_move);
+    cxMapPut(method_handler_map, cx_hash_key_str("LOCK"), webdav_lock);
+    cxMapPut(method_handler_map, cx_hash_key_str("UNLOCK"), webdav_unlock);
+    cxMapPut(method_handler_map, cx_hash_key_str("REPORT"), webdav_report);
+    cxMapPut(method_handler_map, cx_hash_key_str("ACL"), webdav_acl);
     
-    ucx_map_cstr_put(method_handler_map, "SEARCH", webdav_search);
+    cxMapPut(method_handler_map, cx_hash_key_str("SEARCH"), webdav_search);
     
-    ucx_map_cstr_put(method_handler_map, "VERSION-CONTROL", webdav_version_control);
-    ucx_map_cstr_put(method_handler_map, "CHECKOUT", webdav_checkout);
-    ucx_map_cstr_put(method_handler_map, "CHECKIN", webdav_checkin);
-    ucx_map_cstr_put(method_handler_map, "UNCHECKOUT", webdav_uncheckout);
-    ucx_map_cstr_put(method_handler_map, "MKWORKSPACE", webdav_mkworkspace);
-    ucx_map_cstr_put(method_handler_map, "UPDATE", webdav_update);
-    ucx_map_cstr_put(method_handler_map, "LABEL", webdav_label);
-    ucx_map_cstr_put(method_handler_map, "MERGE", webdav_merge);
+    cxMapPut(method_handler_map, cx_hash_key_str("VERSION-CONTROL"), webdav_version_control);
+    cxMapPut(method_handler_map, cx_hash_key_str("CHECKOUT"), webdav_checkout);
+    cxMapPut(method_handler_map, cx_hash_key_str("CHECKIN"), webdav_checkin);
+    cxMapPut(method_handler_map, cx_hash_key_str("UNCHECKOUT"), webdav_uncheckout);
+    cxMapPut(method_handler_map, cx_hash_key_str("MKWORKSPACE"), webdav_mkworkspace);
+    cxMapPut(method_handler_map, cx_hash_key_str("UPDATE"), webdav_update);
+    cxMapPut(method_handler_map, cx_hash_key_str("LABEL"), webdav_label);
+    cxMapPut(method_handler_map, cx_hash_key_str("MERGE"), webdav_merge);
     
     dav_namespace.href = (xmlChar*)"DAV:";
     dav_namespace.prefix = (xmlChar*)"D";
@@ -191,7 +194,7 @@
     }
     char *method = pblock_findkeyval(pb_key_method, rq->reqpb);
     
-    FuncPtr saf = (FuncPtr)ucx_map_cstr_get(method_handler_map, method);
+    FuncPtr saf = (FuncPtr)cxMapGet(method_handler_map, cx_hash_key_str(method));
     if(!saf) {
         return REQ_NOACTION;
     }
@@ -199,33 +202,30 @@
     return saf(pb, sn, rq);
 }
 
-UcxBuffer* rqbody2buffer(Session *sn, Request *rq) {
+int rqbody2buffer(Session *sn, Request *rq, CxBuffer *buf) {
     if(!sn->inbuf) {
         //request body required, set http response code
         protocol_status(sn, rq, 400, NULL);
-        return NULL;
+        return 1;
     }
     
-    UcxBuffer *buf = ucx_buffer_new(
-            NULL,
-            sn->inbuf->maxsize,
-            UCX_BUFFER_AUTOEXTEND);
-    if(!buf) {
+    CxAllocator *a = pool_allocator(sn->pool);
+    if(cxBufferInit(buf, NULL, sn->inbuf->maxsize, a, CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
         protocol_status(sn, rq, 500, NULL);
-        return NULL;
+        return 1;
     }
     
     char in[2048];
     int r;
     while((r = netbuf_getbytes(sn->inbuf, in, 2048)) > 0) {
-        if(ucx_buffer_write(in, 1, r, buf) != r) {
+        if(cxBufferWrite(in, 1, r, buf) != r) {
             protocol_status(sn, rq, 500, NULL);
-            ucx_buffer_free(buf);
-            return NULL;
+            cxBufferDestroy(buf);
+            return 1;
         }
     }
     
-    return buf;
+    return 0;
 }
 
 int webdav_options(pblock *pb, Session *sn, Request *rq) {
@@ -254,8 +254,8 @@
         }
     }
     
-    UcxBuffer *reqbody = rqbody2buffer(sn, rq);
-    if(!reqbody) {
+    CxBuffer reqbody;
+    if(rqbody2buffer(sn, rq, &reqbody)) {
         return REQ_ABORTED;
     }
     
@@ -263,10 +263,10 @@
     WebdavPropfindRequest *propfind = propfind_parse(
             sn,
             rq,
-            reqbody->space,
-            reqbody->size,
+            reqbody.space,
+            reqbody.size,
             &error);
-    ucx_buffer_free(reqbody);
+    cxBufferDestroy(&reqbody);
     if(!propfind) {
         log_ereport(LOG_FAILURE, "webdav-propfind: %s", propfind_error2str(error));
         return REQ_ABORTED;
@@ -318,13 +318,14 @@
         WebdavPropfindRequest *propfind,
         const char *path,
         const char *uri,
-        UcxList **out_req)
+        WebdavPropfindRequestList **out_req)
 {   
     pool_handle_t *pool = propfind->sn->pool;
-    UcxAllocator *a = session_get_allocator(propfind->sn);
+    CxAllocator *a = pool_allocator(pool);
     
     // list of individual WebdavPropfindRequest objects for each Backend
-    UcxList *requestObjects = NULL;
+    WebdavPropfindRequestList *requestObjectsBegin = NULL;
+    WebdavPropfindRequestList *requestObjectsEnd = NULL;
     
     // new properties after init, start with clone of original plist
     WebdavPList *newProp = webdav_plist_clone(pool, propfind->properties);
@@ -347,10 +348,18 @@
         pReq->dav = davList;
         
         // add new WebdavPropfindRequest object to list for later use
-        requestObjects = ucx_list_append_a(a, requestObjects, pReq);
-        if(!requestObjects) {
+        WebdavPropfindRequestList *reqListElm = pool_malloc(pool, sizeof(WebdavPropfindRequestList));
+        if(!reqListElm) {
             return REQ_ABORTED; // OOM
         }
+        reqListElm->propfind = pReq;
+        reqListElm->next = NULL;
+        cx_linked_list_add(
+                (void**)&requestObjectsBegin,
+                (void**)&requestObjectsEnd,
+                -1,
+                offsetof(WebdavPropfindRequestList, next),
+                reqListElm);
         
         // create plist copy as out-plist for init
         newProp = webdav_plist_clone(pool, newProp);
@@ -366,7 +375,7 @@
         davList = davList->next;
     }
     
-    *out_req = requestObjects;
+    *out_req = requestObjectsBegin;
     return REQ_PROCEED;
 }
 
@@ -385,7 +394,7 @@
     uint32_t settings = dav->settings;
     
     // list of individual WebdavPropfindRequest objects for each Backend
-    UcxList *requestObjects = NULL;
+    WebdavPropfindRequestList *requestObjects = NULL;
     
     // Initialize all Webdav Backends
     if(webdav_propfind_init(dav, propfind, path, uri, &requestObjects)) {
@@ -480,8 +489,8 @@
         }
     }
     
-    UcxBuffer *reqbody = rqbody2buffer(sn, rq);
-    if(!reqbody) {
+    CxBuffer reqbody;
+    if(rqbody2buffer(sn, rq, &reqbody)) {
         // most likely OOM
         return REQ_ABORTED;
     }
@@ -490,10 +499,10 @@
     WebdavProppatchRequest *proppatch = proppatch_parse(
             sn,
             rq,
-            reqbody->space,
-            reqbody->size,
+            reqbody.space,
+            reqbody.size,
             &error);
-    ucx_buffer_free(reqbody);
+    cxBufferDestroy(&reqbody);
     if(!proppatch) {
         log_ereport(LOG_FAILURE, "webdav-proppatch: %s", proppatch_error2str(error));
         return REQ_ABORTED;
@@ -587,14 +596,16 @@
 typedef struct DeleteFile {
     char *path;
     struct stat s;
+    struct DeleteFile *prev;
+    struct DeleteFile *next;
 } DeleteFile;
 
 typedef struct DeleteLists {
-    UcxAllocator *a;
-    UcxList *dirs_begin;
-    UcxList *dirs_end;
-    UcxList *files_begin;
-    UcxList *files_end;
+    CxAllocator *a;
+    DeleteFile *dirs_begin;
+    DeleteFile *dirs_end;
+    DeleteFile *files_begin;
+    DeleteFile *files_end;
 } DeleteOp;
 
 static int deletelist_add(
@@ -608,19 +619,20 @@
     DeleteOp *op = userdata;
     
     // create object for this file
-    DeleteFile *file = almalloc(op->a, sizeof(DeleteFile));
+    DeleteFile *file = cxMalloc(op->a, sizeof(DeleteFile));
     if(!file) {
         return 1;
     }
-    file->path = sstrdup_a(op->a, sstr((char*)path)).ptr;
+    file->path = cx_strdup_a(op->a, cx_str((char*)path)).ptr;
     if(!file->path) {
         return 1;
     }
     file->s = *s;
+    file->next = NULL;
     
     // determine which list to use
-    UcxList **begin;
-    UcxList **end;
+    DeleteFile **begin;
+    DeleteFile **end;
     if(S_ISDIR(s->st_mode)) {
         begin = &op->dirs_begin;
         end = &op->dirs_end;
@@ -630,19 +642,10 @@
     }
     
     // add file to list
-    UcxList *elm = ucx_list_append_a(op->a, NULL, file);
-    if(!elm) {
-        alfree(op->a, file->path); // at least do some cleanup, although it
-        alfree(op->a, file);       // isn't really necessary
-        return 1;
-    }
-    if(*begin == NULL) {
-        *begin = elm;
-        *end = elm;
-    } else {
-        ucx_list_concat(*end, elm);
-        *end = elm;
-    }
+    cx_linked_list_add(
+            (void**)begin, (void**)end,
+            offsetof(DeleteFile, prev), offsetof(DeleteFile, next),
+            file);
     
     return 0;
 }
@@ -651,7 +654,7 @@
 {
     DeleteOp del;
     ZERO(&del, sizeof(DeleteOp));
-    del.a = session_get_allocator(op->sn);
+    del.a = pool_allocator(op->sn->pool);
     
     // get a list of all files
     if(webdav_op_iterate_children(op->vfs, -1, NULL, op->path,
@@ -664,22 +667,18 @@
     DeleteFile root;
     root.path = op->path;
     root.s = *op->stat;
-    UcxList root_elm;
-    root_elm.data = &root;
-    root_elm.prev = NULL;
-    root_elm.next = del.dirs_begin;
+    root.prev = NULL;
+    root.next = del.dirs_begin;
     
     if(del.dirs_begin) {
-        del.dirs_begin->prev = &root_elm;
-        del.dirs_begin = &root_elm;
+        del.dirs_begin->prev = &root;
     } else {
-        del.dirs_begin = &root_elm;
-        del.dirs_end = &root_elm;
+        del.dirs_end = &root;
     }
+    del.dirs_begin = &root;
     
     // delete files first
-    UCX_FOREACH(elm, del.files_begin) {
-        DeleteFile *file = elm->data;
+    for(DeleteFile *file=del.files_begin;file;file=file->next) {
         WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s);
         if(webdav_vfs_op_do(&sub, WEBDAV_VFS_DELETE)) {
             return 1;
@@ -687,8 +686,7 @@
     }
     
     // delete directories, reverse order
-    for(UcxList *elm=del.dirs_end;elm;elm=elm->prev) {
-        DeleteFile *file = elm->data;
+    for(DeleteFile *file=del.dirs_end;file;file=file->prev) {
         WebdavVFSOperation sub = webdav_vfs_sub_op(op, file->path, &file->s);
         if(webdav_vfs_op_do(&sub, WEBDAV_VFS_DELETE)) {
             return 1;
@@ -913,18 +911,16 @@
 
 /* ------------------------------ Utils ------------------------------ */
 
-UcxKey webdav_property_key(const char *ns, const char *name) {
-    UcxKey key;
-    sstr_t data = ucx_sprintf("%s\n%s", name, ns);
-    key.data = data.ptr;
+CxHashKey webdav_property_key(const char *ns, const char *name) {
+    CxHashKey key;
+    cxmutstr data = cx_asprintf("%s\n%s", name, ns);
+    key.data.str = data.ptr;
     key.len = data.length;
-    key.hash = ucx_hash(data.ptr, data.length);
+    cx_hash_murmur(&key);
     return key;
 }
 
 
-
-
 /* ------------------------------ public API ------------------------------ */
 
 int webdav_getdepth(Request *rq) {
--- a/src/server/webdav/webdav.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/webdav.h	Sun Nov 06 15:53:32 2022 +0100
@@ -31,9 +31,10 @@
 
 #include "../public/webdav.h"
 
-#include <ucx/map.h>
-#include <ucx/list.h>
-#include <ucx/buffer.h>
+#include <cx/map.h>
+#include <cx/list.h>
+#include <cx/buffer.h>
+#include <cx/string.h>
 
 #ifdef	__cplusplus
 extern "C" {
@@ -47,8 +48,14 @@
 typedef struct DefaultWebdavData {
     WebdavVFSProperties vfsproperties;
 } DefaultWebdavData;
+
+typedef struct WebdavPropfindRequestList WebdavPropfindRequestList;
+struct WebdavPropfindRequestList {
+    WebdavPropfindRequest *propfind;
+    WebdavPropfindRequestList *next;
+};
     
-WebdavType* webdav_get_type(scstr_t dav_class);
+WebdavType* webdav_get_type(cxstring dav_class);
 
 void* webdav_init_backend(ServerConfiguration *cfg, pool_handle_t *pool, WebdavType *dav_class, WSConfigNode *config, int *error);  
 
@@ -62,7 +69,7 @@
  * this function sets an http response code in case of an error
  * or missing request body
  */
-UcxBuffer* rqbody2buffer(Session *sn, Request *rq);
+int rqbody2buffer(Session *sn, Request *rq, CxBuffer *buf);
 
 
 int webdav_options(pblock *pb, Session *sn, Request *rq);
@@ -74,7 +81,7 @@
         WebdavPropfindRequest *propfind,
         const char *path,
         const char *uri,
-        UcxList **out_req);
+        WebdavPropfindRequestList **out_req);
 
 int webdav_propfind_do(
         WebdavBackend *dav,
@@ -123,7 +130,7 @@
             VFSFile *file,
             WSBool commit);
 
-UcxKey webdav_property_key(const char *ns, const char *name);
+CxHashKey webdav_property_key(const char *ns, const char *name);
 
 #ifdef	__cplusplus
 }
--- a/src/server/webdav/xml.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/xml.c	Sun Nov 06 15:53:32 2022 +0100
@@ -30,9 +30,11 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include <ucx/string.h>
-#include <ucx/map.h>
-#include <ucx/buffer.h>
+#include <cx/string.h>
+#include <cx/map.h>
+#include <cx/hash_map.h>
+#include <cx/buffer.h>
+#include <cx/linked_list.h>
 
 #include "../util/util.h"
 #include "../util/pool.h"
@@ -47,12 +49,12 @@
  * generates a string key for an xml namespace
  * format: prefix '\0' href
  */
-static sstr_t xml_namespace_key(UcxAllocator *a, WSNamespace *ns) {
-    sstr_t key = sstrcat_a(a, 3,
-            ns->prefix ? sstr((char*)ns->prefix) : S("\0"),
-            S("\0"),
-            sstr((char*)ns->href));
-    return key;
+static CxHashKey xml_namespace_key(CxAllocator *a, WSNamespace *ns) {
+    cxmutstr key_data = cx_strcat_a(a, 3,
+            ns->prefix ? cx_str((char*)ns->prefix) : cx_strn("\0", 1),
+            cx_strn("\0", 1),
+            cx_str((char*)ns->href));
+    return cx_hash_key_bytes((unsigned char*)key_data.ptr, key_data.length);
 }
 
 
@@ -195,8 +197,8 @@
 /* ------------------- wsxml_get_required_namespaces ------------------- */
 
 typedef struct WSNsCollector {
-    UcxAllocator *a;
-    UcxMap *nsmap;
+    CxAllocator *a;
+    CxMap *nsmap;
     WebdavNSList *def;
     int error;
 } WSNsCollector;
@@ -207,12 +209,12 @@
     if(node->type == XML_ELEMENT_NODE && node->ns) {
         // we create a list of unique prefix-href namespaces by putting
         // all namespaces in a map
-        sstr_t nskey = xml_namespace_key(col->a, node->ns);
-        if(!nskey.ptr) {
+        CxHashKey nskey = xml_namespace_key(col->a, node->ns);
+        if(!nskey.data.bytes) {
             col->error = 1;
             return 1;
         }
-        if(ucx_map_sstr_put(col->nsmap, nskey, node->ns)) {
+        if(cxMapPut(col->nsmap, nskey, node->ns)) {
             col->error = 1;
             return 1;
         }
@@ -221,8 +223,7 @@
         // from col->nsmap later
         WSNamespace *def = node->nsDef;
         while(def) {
-            WebdavNSList *newdef = col->a->malloc(
-                    col->a->pool, sizeof(WebdavNSList));
+            WebdavNSList *newdef = cxMalloc(col->a, sizeof(WebdavNSList));
             if(!newdef) {
                 col->error = 1;
                 return 1;
@@ -255,15 +256,15 @@
 {
     if(error) *error = 0;
     
-    UcxAllocator a = util_pool_allocator(pool);
-    UcxMap *nsmap = ucx_map_new_a(&a, 16);
+    CxAllocator *a = pool_allocator(pool);
+    CxMap *nsmap = cxHashMapCreate(a, 16);
     if(!nsmap) {
         if(error) *error = 1;
         return NULL;
     }
     
     WSNsCollector col;
-    col.a = &a;
+    col.a = a;
     col.nsmap = nsmap;
     col.def = NULL;
     
@@ -279,19 +280,19 @@
         // what we get is a map that contains all missing namespace definitions
         WebdavNSList *def = col.def;
         while(def) {
-            sstr_t nskey = xml_namespace_key(&a, def->namespace);
-            if(!nskey.ptr) {
+            CxHashKey nskey = xml_namespace_key(a, def->namespace);
+            if(!nskey.data.bytes) {
                 if(error) *error = 1;
                 break;
             }
-            ucx_map_sstr_remove(nsmap, nskey);
+            (void)cxMapRemove(nsmap, nskey);
             def = def->next;
         }
         
         // convert nsmap to a list
-        UcxMapIterator i = ucx_map_iterator(nsmap);
+        CxIterator i = cxMapIteratorValues(nsmap);
         WSNamespace *ns;
-        UCX_MAP_FOREACH(key, ns, i) {
+        cx_foreach(WSNamespace *, ns, i) {
             WebdavNSList *newelm = pool_malloc(pool, sizeof(WebdavNSList));
             if(!newelm) {
                 if(error) *error = 1;
@@ -300,23 +301,18 @@
             }
             newelm->namespace = ns;
             newelm->next = NULL;
-            newelm->prev = end; // NULL or the end of list
-            if(end) {
-                end->next = newelm; // append new element
-            } else {
-                list = newelm; // start new list
-            }
-            end = newelm;
+            newelm->prev = NULL;
+            cx_linked_list_add((void**)&list, (void**)&end, offsetof(WebdavNSList, prev), offsetof(WebdavNSList, next), newelm);
         }
     }
     
-    ucx_map_free(nsmap);
+    cxMapDestroy(nsmap);
     return list;
 }
 
 
 static ssize_t buf_writefunc(void *buf, const char *s, size_t len) {
-    int w = ucx_buffer_write(s, 1, len, buf);
+    int w = cxBufferWrite(s, 1, len, buf);
     return w == 0 ? IO_ERROR : w;
 }
 
@@ -324,8 +320,8 @@
         pool_handle_t *pool,
         WSXmlNode *node)
 {
-    UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND);
-    if(!buf) {
+    CxBuffer buf;
+    if(cxBufferInit(&buf, NULL, 1024, pool_allocator(pool), CX_BUFFER_AUTO_EXTEND|CX_BUFFER_FREE_CONTENTS)) {
         return NULL;
     }
     
@@ -337,23 +333,23 @@
     
     Writer writer;
     char buffer[512];
-    writer_init_with_stream(&writer, buf, buf_writefunc, buffer, 512);
+    writer_init_with_stream(&writer, &buf, buf_writefunc, buffer, 512);
     
     WSXmlData *data = NULL;
     if(!wsxml_write_nodes(pool, &writer, NULL, node) && !writer_flush(&writer)) {
         data = pool_malloc(pool, sizeof(WSXmlData));
         if(data) {
-            data->data = pool_malloc(pool, buf->size + 1);
+            data->data = pool_malloc(pool, buf.size + 1);
             if(data->data) {
-                memcpy(data->data, buf->space, buf->size);
-                data->data[buf->size] = '\0';
-                data->length = buf->size;
+                memcpy(data->data, buf.space, buf.size);
+                data->data[buf.size] = '\0';
+                data->length = buf.size;
                 data->namespaces = nslist;
             }
         }
     }
     
-    ucx_buffer_free(buf);
+    cxBufferDestroy(&buf);
     
     return data;
 }
@@ -439,8 +435,8 @@
                     break;
                 }
                 memset(ns, 0, sizeof(WSNamespace));
-                ns->prefix = prefix_len > 0 ? (xmlChar*)sstrdup_pool(pool, sstrn(prefix, prefix_len)).ptr : NULL;
-                ns->href = (xmlChar*)sstrdup_pool(pool, sstrn(href, i-href_start)).ptr;
+                ns->prefix = prefix_len > 0 ? (xmlChar*)cx_strdup_pool(pool, cx_mutstrn(prefix, prefix_len)).ptr : NULL;
+                ns->href = (xmlChar*)cx_strdup_pool(pool, cx_mutstrn(href, i-href_start)).ptr;
                 if(list_current) {
                     list_current->next = elm;
                 } else {
@@ -503,7 +499,7 @@
      * key: (char*) namespace prefix
      * value: WSNamespace*
      */
-    UcxMap *namespaces;
+    CxMap *namespaces;
     
     /*
      * Should namespace definitions be created
@@ -519,22 +515,22 @@
 static void xml_ser_text(Writer *out, int type, const char *text) {
     size_t start = 0;
     size_t i;
-    sstr_t entityref = { NULL, 0 };
+    cxstring entityref = { NULL, 0 };
     for(i=0;text[i]!='\0';i++) {
         char c = text[i];
         if(c == '&') {
-            entityref = S("&amp;");
+            entityref = (cxstring)CX_STR("&amp;");
         } else if(type == 0) {
             if(c == '<') {
-                entityref = S("&lt;");
+                entityref = (cxstring)CX_STR("&lt;");
             } else if(c == '>') {
-                entityref = S("&gt;");
+                entityref = (cxstring)CX_STR("&gt;");
             }
         } else {
             if(c == '\"') {
-                entityref = S("&quot;");
+                entityref = (cxstring)CX_STR("&quot;");
             } else if(c == '\'') {
-                entityref = S("&apos;");
+                entityref = (cxstring)CX_STR("&apos;");
             }
         }
         
@@ -564,12 +560,12 @@
     
     // write prefix and ':'
     if(node->ns && node->ns->prefix) {
-        writer_puts(out, sstr((char*)node->ns->prefix));
+        writer_puts(out, cx_str((char*)node->ns->prefix));
         writer_putc(out, ':');
     }
     
     // node name
-    writer_puts(out, sstr((char*)node->name));
+    writer_puts(out, cx_str((char*)node->name));
     
     // namespace definitions
     if(xw->define_namespaces) {
@@ -580,19 +576,19 @@
             // xw->namespaces contains all namespace, that were defined
             // before xml serialization
             if(!nsdef->prefix) {
-                writer_puts(out, S(" xmlns=\""));
-                writer_puts(out, sstr((char*)nsdef->href));
-                writer_putc(out, '"');
+                writer_put_lit(out, " xmlns=\"");
+                writer_put_str(out, (char*)nsdef->href);
+                writer_putc   (out, '"');
             } else {
                 WSNamespace *n = xw->namespaces ?
-                    ucx_map_cstr_get(xw->namespaces, (char*)nsdef->prefix) :
+                    cxMapGet(xw->namespaces, cx_hash_key_str((const char*)nsdef->prefix)) :
                     NULL;
                 if(!n) {
-                    writer_puts(out, S(" xmlns:"));
-                    writer_puts(out, sstr((char*)nsdef->prefix));
-                    writer_puts(out, S("=\""));
-                    writer_puts(out, sstr((char*)nsdef->href));
-                    writer_putc(out, '"');
+                    writer_put_lit(out, " xmlns:");
+                    writer_put_str(out, (const char*)nsdef->prefix);
+                    writer_put_lit(out, "=\"");
+                    writer_put_str(out, (const char*)nsdef->href);
+                    writer_putc   (out, '"');
                 }
             }
             
@@ -607,12 +603,12 @@
         writer_putc(out, ' ');
         // optional namespace
         if(attr->ns && attr->ns->prefix) {
-            writer_puts(out, sstr((char*)attr->ns->prefix));
+            writer_puts(out, cx_str((char*)attr->ns->prefix));
             writer_putc(out, ':');
         }
         // <name>="
-        writer_puts(out, sstr((char*)attr->name));
-        writer_puts(out, S("=\""));
+        writer_put_str(out, (char*)attr->name);
+        writer_put_lit(out, "=\"");
         // value
         xmlNode *value = attr->children;
         while(value) {
@@ -630,7 +626,7 @@
     if(node->children) {
         writer_putc(out, '>');
     } else {
-        writer_puts(out, S("/>"));
+        writer_put_lit(out, "/>");
     }
 }
 
@@ -672,14 +668,14 @@
     Writer *out = xw->out;
     if(node->type == XML_ELEMENT_NODE) {
         if(node->children) {
-            writer_puts(xw->out, S("</"));
+            writer_put_lit(xw->out, "</");
             // write prefix and ':'
             if(node->ns && node->ns->prefix) {
-                writer_puts(out, sstr((char*)node->ns->prefix));
+                writer_puts(out, cx_str((char*)node->ns->prefix));
                 writer_putc(out, ':');
             }
             // name and close tag
-            writer_puts(out, sstr((char*)node->name));
+            writer_puts(out, cx_str((char*)node->name));
             writer_putc(out, '>');
             
         } // element was already closed in xml_ser_node_begin
@@ -691,7 +687,7 @@
 static int xml_write_nodes(
         pool_handle_t *pool,
         Writer *out,
-        UcxMap *nsdefs,
+        CxMap *nsdefs,
         WSBool createdefs,
         xmlNode *node)
 {
@@ -719,7 +715,7 @@
 int wsxml_write_nodes(
         pool_handle_t *pool,
         Writer *out,
-        UcxMap *nsdefs,
+        CxMap *nsdefs,
         xmlNode *node)
 {
     return xml_write_nodes(pool, out, nsdefs, TRUE, node);
--- a/src/server/webdav/xml.h	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/server/webdav/xml.h	Sun Nov 06 15:53:32 2022 +0100
@@ -32,8 +32,7 @@
 #include "../public/webdav.h"
 #include <libxml/tree.h>
 
-#include <ucx/map.h>
-#include <ucx/ucx.h>
+#include <cx/map.h>
 
 #include "../util/writer.h"
 
@@ -47,7 +46,7 @@
 int wsxml_write_nodes(
         pool_handle_t *pool,
         Writer *out,
-        UcxMap *nsdefs,
+        CxMap *nsdefs,
         xmlNode *node);
 
 /*
--- a/src/tools/Makefile	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/tools/Makefile	Sun Nov 06 15:53:32 2022 +0100
@@ -31,7 +31,7 @@
 include $(BUILD_ROOT)/config.mk
 
 CFLAGS += -I../ucx/
-LDFLAGS += -L../../build/lib -lucx -lwscfg
+LDFLAGS += -lucx -lwscfg
 
 # list of source files
 WSTOOL_SRC = wstool.c
@@ -42,7 +42,7 @@
 all: $(BUILD_ROOT)/build/tools $(BUILD_ROOT)/build/bin/wstool
 
 $(BUILD_ROOT)/build/bin/wstool: $(BUILD_ROOT)/build/tools $(WSTOOL_OBJ)
-	$(CC) -o $@ $(WSTOOL_OBJ) $(LDFLAGS)
+	$(CC) -o $@ $(WSTOOL_OBJ) -L$(BUILD_ROOT)/build/lib $(LDFLAGS)
 
 $(BUILD_ROOT)/build/tools/%$(OBJ_EXT): %.c
 	$(CC) $(CFLAGS) -c -o $@ $<
--- a/src/tools/wstool.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/tools/wstool.c	Sun Nov 06 15:53:32 2022 +0100
@@ -62,19 +62,19 @@
 
 int tool_get_tmpdir(char *configfile) {
     ServerConfig *serverconf = serverconfig_load(configfile);
-    UcxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Runtime"));
+    CxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, cx_str("Runtime"));
     if(!list) {
         fprintf(stderr, "Error: No Runtime element in %s\n", configfile);
         return -1;
     }
-    if(ucx_list_size(list) != 1) {
+    if(list->size != 1) {
         fprintf(stderr, "Error: Multiple Runtime elements in %s\n", configfile);
         return -1;
     }
-    ConfigNode *runtime = list->data;
-    scstr_t tmp = serverconfig_directive_value(runtime, SC("Temp"));
+    ConfigNode *runtime = cxListAt(list, 0);
+    cxstring tmp = serverconfig_directive_value(runtime, cx_str("Temp"));
     
-    ucx_list_free(list);
+    cxListDestroy(list);
     
     if(!tmp.ptr) {
         fprintf(stderr, "Error: No Temp directive in Runtime Object\n");
--- a/src/ucx/Makefile	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/ucx/Makefile	Sun Nov 06 15:53:32 2022 +0100
@@ -31,20 +31,18 @@
 include $(BUILD_ROOT)/config.mk
 
 # list of source files
-SRC  = ucx.c
-SRC += utils.c
+SRC  = allocator.c
+SRC += basic_mempool.c
+SRC += buffer.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 += string.c
-SRC += test.c
-SRC += allocator.c
-SRC += logging.c
-SRC += buffer.c
-SRC += stack.c
-SRC += array.c
+SRC += tree.c
+SRC += utils.c
+SRC += printf.c
+SRC += compare.c
 
 OBJ = $(SRC:%.c=$(BUILD_ROOT)/build/ucx/%$(OBJ_EXT))
 
--- a/src/ucx/allocator.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/ucx/allocator.c	Sun Nov 06 15:53:32 2022 +0100
@@ -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,99 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx/allocator.h"
+#include "cx/allocator.h"
 
 #include <stdlib.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);
+}
--- a/src/ucx/array.c	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,467 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2019 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.
- */
-
-#define _GNU_SOURCE /* we want to use qsort_r(), if available */
-#define __STDC_WANT_LIB_EXT1__ 1 /* use qsort_s, if available */
-
-
-#include "ucx/array.h"
-#include "ucx/utils.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#ifndef UCX_ARRAY_DISABLE_QSORT
-#ifdef __GLIBC__
-#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
-#define ucx_array_sort_impl qsort_r
-#endif /* glibc version >= 2.8 */
-#elif /* not  __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__)
-#define ucx_array_sort_impl ucx_qsort_r
-#define USE_UCX_QSORT_R
-#elif /* not (__APPLE || __FreeBSD__) */ defined(__sun)
-#if __STDC_VERSION__ >= 201112L
-#define ucx_array_sort_impl qsort_s
-#endif
-#endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */
-#endif /* UCX_ARRAY_DISABLE_QSORT */
-
-#ifndef ucx_array_sort_impl
-#define ucx_array_sort_impl ucx_mergesort
-#endif
-
-static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) {
-    size_t required_capacity = array->capacity;
-    while (reqcap > required_capacity) {
-        if (required_capacity * 2 < required_capacity)
-            return 1;
-        required_capacity <<= 1;
-    }
-    if (ucx_array_reserve(array, required_capacity)) {
-        return 1;
-    }
-    return 0;
-}
-
-int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
-    size_t elmsize, size_t index, void* data) {
-    
-    if(!alloc || !capacity || !array) {
-        errno = EINVAL;
-        return 1;
-    }
-    
-    size_t newcapacity = *capacity;
-    while(index >= newcapacity) {
-        if(ucx_szmul(newcapacity, 2, &newcapacity)) {
-            errno = EOVERFLOW;
-            return 1;
-        }        
-    }
-
-    size_t memlen, offset;
-    if(ucx_szmul(newcapacity, elmsize, &memlen)) {
-        errno = EOVERFLOW;
-        return 1;
-    }
-    /* we don't need to check index*elmsize - it is smaller than memlen */
-    
-    
-    void* newptr = alrealloc(alloc, *array, memlen);
-    if(newptr == NULL) {
-        errno = ENOMEM; /* we cannot assume that every allocator sets this */
-        return 1;
-    }
-    *array = newptr;
-    *capacity = newcapacity;
-    
-    
-    char* dest = *array;
-    dest += elmsize*index;
-    memcpy(dest, data, elmsize);
-    
-    return 0;
-}
-
-int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
-    size_t index, void* data) {
-    
-    return ucx_array_util_set_a(alloc, array, capacity, sizeof(void*),
-            index, &data);
-}
-
-UcxArray* ucx_array_new(size_t capacity, size_t elemsize) {
-    return ucx_array_new_a(capacity, elemsize, ucx_default_allocator());
-}
-
-UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
-        UcxAllocator* allocator) {
-    UcxArray* array = almalloc(allocator, sizeof(UcxArray));
-    if(array) {
-        ucx_array_init_a(array, capacity, elemsize, allocator);
-    }
-    return array;
-}
-
-void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize) {
-    ucx_array_init_a(array, capacity, elemsize, ucx_default_allocator());
-}
-
-void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
-        UcxAllocator* allocator) {
-    
-    array->allocator = allocator;
-    array->elemsize = elemsize;
-    array->size = 0;
-    array->data = alcalloc(allocator, capacity, elemsize);
-    
-    if (array->data) {
-        array->capacity = capacity;
-    } else {
-        array->capacity = 0;
-    }
-}
-
-int ucx_array_clone(UcxArray* dest, UcxArray const* src) {
-    if (ucx_array_ensurecap(dest, src->capacity)) {
-        return 1;
-    }
-    
-    dest->elemsize = src->elemsize;
-    dest->size = src->size;
-    
-    if (dest->data) {
-        memcpy(dest->data, src->data, src->size*src->elemsize);
-    }
-    
-    return 0;
-}
-
-int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
-        cmp_func cmpfnc, void* data) {
-    
-    if (array1->size != array2->size || array1->elemsize != array2->elemsize) {
-        return 0;
-    } else {
-        if (array1->size == 0)
-            return 1;
-        
-        size_t elemsize;
-        if (cmpfnc == NULL) {
-            cmpfnc = ucx_cmp_mem;
-            elemsize = array1->elemsize;
-            data = &elemsize;
-        }
-        
-        for (size_t i = 0 ; i < array1->size ; i++) {
-            int r = cmpfnc(
-                    ucx_array_at(array1, i),
-                    ucx_array_at(array2, i),
-                    data);
-            if (r != 0)
-                return 0;
-        }
-        return 1;
-    }
-}
-
-void ucx_array_destroy(UcxArray *array) {
-    if(array->data)
-        alfree(array->allocator, array->data);
-    array->data = NULL;
-    array->capacity = array->size = 0;
-}
-
-void ucx_array_free(UcxArray *array) {
-    ucx_array_destroy(array);
-    alfree(array->allocator, array);
-}
-
-int ucx_array_append_from(UcxArray *array, void *data, size_t count) {
-    if (ucx_array_ensurecap(array, array->size + count))
-        return 1;
-    
-    void* dest = ucx_array_at(array, array->size);
-    if (data) {
-        memcpy(dest, data, array->elemsize*count);
-    } else {
-        memset(dest, 0, array->elemsize*count);
-    }
-    array->size += count;
-    
-    return 0;
-}
-
-int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) {
-    if (ucx_array_ensurecap(array, array->size + count))
-        return 1;
-    
-    if (array->size > 0) {
-        void *dest = ucx_array_at(array, count);
-        memmove(dest, array->data, array->elemsize*array->size);
-    }
-    
-    if (data) {
-        memcpy(array->data, data, array->elemsize*count);
-    } else {
-        memset(array->data, 0, array->elemsize*count);
-    }
-    array->size += count;
-        
-    return 0;
-}
-
-int ucx_array_set_from(UcxArray *array, size_t index,
-        void *data, size_t count) {
-    if (ucx_array_ensurecap(array, index + count))
-        return 1;
-    
-    if (index+count > array->size) {
-        array->size = index+count;
-    }
-    
-    void *dest = ucx_array_at(array, index);
-    if (data) {
-        memcpy(dest, data, array->elemsize*count);
-    } else {
-        memset(dest, 0, array->elemsize*count);
-    }
-    
-    return 0;
-}
-
-int ucx_array_concat(UcxArray *array1, const UcxArray *array2) {
-    
-    if (array1->elemsize != array2->elemsize)
-        return 1;
-    
-    size_t capacity = array1->capacity+array2->capacity;
-        
-    if (array1->capacity < capacity) {
-        if (ucx_array_reserve(array1, capacity)) {
-            return 1;
-        }
-    }
-    
-    void* dest = ucx_array_at(array1, array1->size);
-    memcpy(dest, array2->data, array2->size*array2->elemsize);
-    
-    array1->size += array2->size;
-    
-    return 0;
-}
-
-void *ucx_array_at(UcxArray const *array, size_t index) {
-    char* memory = array->data;
-    char* loc = memory + index*array->elemsize;
-    return loc;
-}
-
-size_t ucx_array_find(UcxArray const *array, void *elem,
-        cmp_func cmpfnc, void *data) {
-    
-    size_t elemsize;
-    if (cmpfnc == NULL) {
-        cmpfnc = ucx_cmp_mem;
-        elemsize = array->elemsize;
-        data = &elemsize;
-    }
-
-    if (array->size > 0) {
-        for (size_t i = 0 ; i < array->size ; i++) {
-            void* ptr = ucx_array_at(array, i);
-            if (cmpfnc(ptr, elem, data) == 0) {
-                return i;
-            }
-        }
-        return array->size;
-    } else {
-        return 0;
-    }
-}
-
-int ucx_array_contains(UcxArray const *array, void *elem,
-        cmp_func cmpfnc, void *data) {
-    return ucx_array_find(array, elem, cmpfnc, data) != array->size;
-}
-
-static void ucx_mergesort_merge(void *arrdata,size_t elemsize,
-        cmp_func cmpfnc, void *data,
-        size_t start, size_t mid, size_t end) { 
-    
-    char* array = arrdata;
-    
-    size_t rightstart = mid + 1; 
-  
-    if (cmpfnc(array + mid*elemsize,
-            array + rightstart*elemsize, data) <= 0) {
-        /* already sorted */
-        return;
-    }
-  
-    /* we need memory for one element */
-    void *value = malloc(elemsize);
-    
-    while (start <= mid && rightstart <= end) { 
-        if (cmpfnc(array + start*elemsize,
-                array + rightstart*elemsize, data) <= 0) { 
-            start++; 
-        } else {
-            /* save the value from the right */
-            memcpy(value, array + rightstart*elemsize, elemsize);
-                        
-            /* shift all left elements one element to the right */
-            size_t shiftcount = rightstart-start;
-            void *startptr = array + start*elemsize;
-            void *dest = array + (start+1)*elemsize;
-            memmove(dest, startptr, shiftcount*elemsize);
-            
-            /* bring the first value from the right to the left */
-            memcpy(startptr, value, elemsize);
-  
-            start++; 
-            mid++; 
-            rightstart++; 
-        }
-    }
-    
-    /* free the temporary memory */
-    free(value);
-} 
-  
-static void ucx_mergesort_impl(void *arrdata, size_t elemsize,
-        cmp_func cmpfnc, void *data, size_t l, size_t r) { 
-    if (l < r) {
-        size_t m = l + (r - l) / 2; 
-  
-        ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m); 
-        ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r); 
-        ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r);
-    } 
-}
-
-static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize,
-        cmp_func cmpfnc, void *data) {
-    
-    ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1);
-}
-
-#ifdef USE_UCX_QSORT_R
-struct cmpfnc_swapargs_info {
-    cmp_func func;
-    void *data;
-};
-
-static int cmp_func_swap_args(void *data, const void *x, const void *y) {
-    struct cmpfnc_swapargs_info* info = data;
-    return info->func(x, y, info->data);
-}
-
-static void ucx_qsort_r(void *array, size_t count, size_t elemsize,
-		     cmp_func cmpfnc, void *data) {
-    struct cmpfnc_swapargs_info info;
-    info.func = cmpfnc;
-    info.data = data;
-    qsort_r(array, count, elemsize, &info, cmp_func_swap_args);
-}
-#endif /* USE_UCX_QSORT_R */
-
-void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data) {
-    ucx_array_sort_impl(array->data, array->size, array->elemsize,
-            cmpfnc, data);
-}
-
-void ucx_array_remove(UcxArray *array, size_t index) {
-    array->size--;
-    if (index < array->size) {
-        void* dest = ucx_array_at(array, index);
-        void* src = ucx_array_at(array, index+1);
-        memmove(dest, src, (array->size - index)*array->elemsize);
-    }
-}
-
-void ucx_array_remove_fast(UcxArray *array, size_t index) {
-    array->size--;
-    if (index < array->size) {       
-        void* dest = ucx_array_at(array, index);
-        void* src = ucx_array_at(array, array->size);
-        memcpy(dest, src, array->elemsize);
-    }
-}
-
-int ucx_array_shrink(UcxArray* array) {
-    void* newptr = alrealloc(array->allocator, array->data,
-                array->size*array->elemsize);
-    if (newptr) {
-        array->data = newptr;
-        array->capacity = array->size;
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-int ucx_array_resize(UcxArray* array, size_t capacity) {
-    if (array->capacity >= capacity) {
-        void* newptr = alrealloc(array->allocator, array->data,
-                capacity*array->elemsize);
-        if (newptr) {
-            array->data = newptr;
-            array->capacity = capacity;
-            if (array->size > array->capacity) {
-                array->size = array->capacity;
-            }
-            return 0;
-        } else {
-            return 1;
-        }
-    } else {
-        return ucx_array_reserve(array, capacity);
-    }
-}
-
-int ucx_array_reserve(UcxArray* array, size_t capacity) {
-    if (array->capacity > capacity) {
-        return 0;
-    } else {
-        void* newptr = alrealloc(array->allocator, array->data,
-                capacity*array->elemsize);
-        if (newptr) {
-            array->data = newptr;
-            array->capacity = capacity;
-            return 0;
-        } else {
-            return 1;
-        }
-    }
-}
-
-int ucx_array_grow(UcxArray* array, size_t count) {
-    return ucx_array_reserve(array, array->size+count);
-}
--- a/src/ucx/avl.c	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/basic_mempool.c	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,236 @@
+/*
+ * 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 <stdint.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/src/ucx/buffer.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/ucx/buffer.c	Sun Nov 06 15:53:32 2022 +0100
@@ -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,78 @@
  * 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>
+#include <stdint.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
+) {
+    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)
-    {
-        return NULL;
+void cxBufferDestroy(CxBuffer *buffer) {
+    if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) {
+        cxFree(buffer->allocator, buffer->bytes);
     }
-
-    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);
-    }
-    return dst;
 }
 
-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 +107,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 +347,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 +367,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/src/ucx/compare.c	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,182 @@
+/*
+ * 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 <stdint.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_ptr(void const *ptr1, void const *ptr2) {
+    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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/cx/allocator.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,260 @@
+/*
+ * 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)));
+
+/**
+ * Structure holding an advanced destructor function and the desired payload.
+ * Invocations of func should use data as first argument.
+ */
+typedef struct {
+    /**
+     * A pointer to the data that SHALL be used to invoke func.
+     */
+    void *data;
+    /**
+     * A pointer to the function to invoke.
+     */
+    cx_destructor_func2 func;
+} cx_advanced_destructor;
+
+/**
+ * Specifies the type of destructor to use.
+ */
+enum cx_destructor_type {
+    /**
+     * Do not use a destructor function.
+     */
+    CX_DESTRUCTOR_NONE,
+    /**
+     * Use a simple destructor.
+     * @see cx_destructor_func
+     */
+    CX_DESTRUCTOR_SIMPLE,
+    /**
+     * Use an advanced destructor.
+     * @see cx_advanced_destructor
+     */
+    CX_DESTRUCTOR_ADVANCED
+};
+
+/**
+ * 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/src/ucx/cx/basic_mempool.h	Sun Nov 06 15:53:32 2022 +0100
@@ -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/src/ucx/cx/buffer.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,414 @@
+/*
+ * 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
+ * @param flags buffer features (see cx_buffer_s.flags)
+ * @return zero on success, non-zero if a required allocation failed
+ */
+__attribute__((__nonnull__(1, 4)))
+int cxBufferInit(
+        CxBuffer *buffer,
+        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.
+ *
+ * @param buffer the buffer which contents shall be destroyed
+ */
+__attribute__((__nonnull__))
+void cxBufferDestroy(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/src/ucx/cx/common.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,127 @@
+/*
+ * 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)
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+/**
+ * Function pointer compatible with fwrite-like functions.
+ */
+typedef size_t (*cx_write_func)(
+        void const *,
+        size_t,
+        size_t,
+        void *
+);
+
+#ifdef _WIN32
+#ifndef __WORDSIZE
+#ifdef _WIN64
+#define __WORDSIZE 64
+#else
+#define __WORDSIZE 32
+#endif
+#endif /* __WORDSIZE */
+#else /* !_WIN32 */
+
+#include <sys/types.h>
+
+#endif /* _WIN32 */
+
+#ifndef __GNUC__
+/**
+ * Removes GNU C attributes where they are not supported.
+ */
+#define __attribute__(x)
+#endif
+
+#endif /* UCX_COMMON_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/cx/compare.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,199 @@
+/*
+ * 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
+
+#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 two pointers.
+ *
+ * @param ptr1 pointer one
+ * @param ptr2 pointer two
+ * @return -1 if ptr1 is less than ptr2, 0 if both are equal,
+ * 1 if ptr1 is greater than ptr2
+ */
+int cx_cmp_ptr(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/src/ucx/cx/hash_key.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,125 @@
+/*
+ * 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 "stddef.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Internal structure for a key within a hash map. */
+struct cx_hash_key_s {
+    /** The key data. */
+    union {
+        unsigned char *bytes;
+        unsigned char const *cbytes;
+        char const *cstr;
+        char *str;
+        void *obj;
+    } 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 murmur hash-2.
+ *
+ * You need to initialize data and len in the key struct.
+ * The hash is then directly written to that struct.
+ *
+ * @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 *obj,
+        size_t len
+);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* UCX_HASH_KEY_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/cx/hash_map.h	Sun Nov 06 15:53:32 2022 +0100
@@ -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 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 {
+    /** The value data. */
+    void *data;
+
+    /** A pointer to the next element in the current bucket. */
+    struct cx_hash_map_element_s *next;
+
+    /** The corresponding key. */
+    CxHashKey key;
+};
+
+/**
+ * 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.
+ *
+ * @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 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 *allocator,
+        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/src/ucx/cx/iterator.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,167 @@
+/*
+ * 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"
+
+/**
+ * Internal iterator struct - use CxIterator.
+ */
+struct cx_iterator_s {
+    /**
+     * True iff the iterator points to valid data.
+     */
+    __attribute__ ((__nonnull__))
+    bool (*valid)(struct cx_iterator_s const *);
+
+    /**
+     * Returns a pointer to the current element.
+     */
+    __attribute__ ((__nonnull__))
+    void *(*current)(struct cx_iterator_s const *);
+
+    /**
+     * Advances the iterator.
+     */
+    __attribute__ ((__nonnull__))
+    void (*next)(struct cx_iterator_s *);
+
+    /**
+     * 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 *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;
+
+    /**
+     * Users may set this to true, if the current element shall be removed from the underlying collection
+     * when the iterator advances.
+     * Has no effect on iterators that are not based on a collection.
+     */
+    bool remove;
+};
+
+/**
+ * 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 (elements added or removed),
+ * the iterator becomes invalid (regardless of what cxIteratorValid() returns) and MUST be re-obtained
+ * from the collection.
+ */
+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 a pointer to the iterator
+ * @return true iff the iterator points to valid data
+ */
+__attribute__ ((__nonnull__))
+static inline bool cxIteratorValid(CxIterator const *iter) {
+    return iter->valid(iter);
+}
+
+/**
+ * Returns a pointer to the current element.
+ *
+ * The behavior is undefined if this iterator is invalid.
+ *
+ * @param iter a pointer to the iterator
+ * @return a pointer to the current element
+ */
+__attribute__ ((__nonnull__))
+static inline void *cxIteratorCurrent(CxIterator const *iter) {
+    return iter->current(iter);
+}
+
+/**
+ * Advances the iterator to the next element.
+ *
+ * @param iter a pointer to the iterator
+ */
+__attribute__ ((__nonnull__))
+static inline void cxIteratorNext(CxIterator *iter) {
+    iter->next(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)) // NOLINT(bugprone-macro-parentheses)
+
+#endif /* UCX_ITERATOR_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/cx/linked_list.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,450 @@
+/*
+ * 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
+
+/**
+ * Allocates a linked list for storing elements with \p item_size bytes each.
+ *
+ * @remark Elements added to the list are copied, therefore a possible destructor
+ * MUST NOT free the memory pointed to by its argument.
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * @param comparator the comparator for the elements
+ * @param item_size the size of each element in bytes
+ * @return the created list
+ */
+CxList *cxLinkedListCreate(
+        CxAllocator const *allocator,
+        CxListComparator comparator,
+        size_t item_size
+) __attribute__((__nonnull__));
+
+/**
+ * Allocates a linked list for storing pointers.
+ *
+ * If you want to store the elements directly in this list, use cxLinkedListCreate().
+ *
+ * @remark Since only pointers are stored in this list, a possible destructor
+ * MAY free the memory pointed to by its argument in order to prevent memory leaks.
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * @param comparator the comparator for the elements
+ * @return the created list
+ */
+CxList *cxPointerLinkedListCreate(
+        CxAllocator const *allocator,
+        CxListComparator comparator
+) __attribute__((__nonnull__));
+
+/**
+ * Creates a linked list using the data from an array.
+ *
+ * @remark Elements added to the list are copied, therefore a possible destructor
+ * MUST NOT free the memory pointed to by its argument.
+ *
+ * @param allocator the allocator for allocating the list nodes
+ * @param comparator the comparator for the elements
+ * @param item_size the size of one item in the array
+ * @param num_items the number of items
+ * @param array the array data
+ * @return the created list
+ */
+CxList *cxLinkedListFromArray(
+        CxAllocator const *allocator,
+        CxListComparator comparator,
+        size_t item_size,
+        size_t num_items,
+        void const *array
+) __attribute__((__nonnull__));
+
+/**
+ * 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 follow_ptr \c false if the pointer denoted by \p loc_data shall be passed to the \p cmp_func.
+ * If \c true, the data at \p loc_data is assumed to be a pointer, dereferenced, and then passed to \p cmp_func.
+ * @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,
+        bool follow_ptr,
+        CxListComparator 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 structures:
+ * \code
+ * typedef struct node node;
+ * struct node {
+ *   node* prev;
+ *   node* next;
+ *   my_payload data; // in this case set follow_ptr = 0
+ * }
+ *
+ * typedef struct ptr_node ptr_node;
+ * struct ptr_node {
+ *   ptr_node* prev;
+ *   ptr_node* next;
+ *   my_payload* data; // in this case set follow_ptr = 1
+ * }
+ * \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 follow_ptr \c false if the pointer denoted by \p loc_data shall be passed to the \p cmp_func.
+ * If \c true, the data at \p loc_data is assumed to be a pointer, dereferenced, and then passed to \p cmp_func.
+ * @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,
+        bool follow_ptr,
+        CxListComparator cmp_func
+) __attribute__((__nonnull__(1, 7)));
+
+
+/**
+ * Compares two lists element wise.
+ *
+ * The \c follow_ptr flags have the following meaning: if \c false, the pointer denoted by \p loc_data shall
+ * directly be passed to the \p cmp_func.
+ * If \c true, the data at \p loc_data is assumed to be a pointer, dereferenced, and then passed to \p cmp_func.
+ *
+ * \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 follow_ptr_left indicates whether pointers in the left list shall be dereferenced
+ * @param follow_ptr_right indicates whether pointers in the right list shall be dereferenced
+ * @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,
+        bool follow_ptr_left,
+        bool follow_ptr_right,
+        CxListComparator cmp_func
+) __attribute__((__nonnull__(7)));
+
+/**
+ * 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/src/ucx/cx/list.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,425 @@
+/*
+ * 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 "allocator.h"
+#include "iterator.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * A comparator function comparing two list elements.
+ */
+typedef int(*CxListComparator)(
+        void const *left,
+        void const *right
+);
+
+/**
+ * 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 {
+    /**
+     * The list class definition.
+     */
+    cx_list_class *cl;
+    /**
+     * The allocator to use.
+     */
+    CxAllocator const *allocator;
+    /**
+     * The comparator function for the elements.
+     */
+    CxListComparator cmpfunc;
+    /**
+     * The size of each element (payload only).
+     */
+    size_t itemsize;
+    /**
+     * The size of the list (number of currently stored elements).
+     */
+    size_t size;
+    /**
+     * The capacity of the list (maximum number of elements).
+     */
+    size_t capacity;
+    union {
+        /**
+         * An optional simple destructor for the list contents that admits the free() interface.
+         *
+         * @remark Set content_destructor_type to #CX_DESTRUCTOR_SIMPLE.
+         *
+         * @attention Read the documentation of the particular list 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 list contents providing additional data.
+         *
+         * @remark Set content_destructor_type to #CX_DESTRUCTOR_ADVANCED.
+         *
+         * @attention Read the documentation of the particular list implementation
+         * whether this destructor shall only destroy the contents or also free the memory.
+         */
+        cx_advanced_destructor advanced_destructor;
+    };
+    /**
+     * The type of destructor to use.
+     */
+    enum cx_destructor_type content_destructor_type;
+};
+
+/**
+ * The class definition for arbitrary lists.
+ */
+struct cx_list_class_s {
+    /**
+     * Destructor function.
+     */
+    void (*destructor)(struct cx_list_s *list);
+
+    /**
+     * Member function for adding an element.
+     */
+    int (*add)(
+            struct cx_list_s *list,
+            void const *elem
+    );
+
+    /**
+     * Member function for inserting an element.
+     */
+    int (*insert)(
+            struct cx_list_s *list,
+            size_t index,
+            void const *elem
+    );
+
+    /**
+     * Member function for inserting an element relative to an iterator position.
+     */
+    int (*insert_iter)(
+            struct cx_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 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);
+
+    /**
+     * Returns an iterator pointing to the specified index.
+     */
+    struct cx_iterator_s (*iterator)(
+            struct cx_list_s *list,
+            size_t index
+    );
+};
+
+/**
+ * Common type for all list implementations.
+ */
+typedef struct cx_list_s CxList;
+
+/**
+ * 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
+ */
+__attribute__((__nonnull__))
+static inline int cxListAdd(
+        CxList *list,
+        void const *elem
+) {
+    return list->cl->add(list, elem);
+}
+
+/**
+ * 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(list, index, elem);
+}
+
+/**
+ * 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(
+        CxIterator *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(
+        CxIterator *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.
+ * @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);
+}
+
+/**
+ * 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 cxListIterator(
+        CxList *list,
+        size_t index
+) {
+    return list->cl->iterator(list, 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 cxListBegin(CxList *list) {
+    return list->cl->iterator(list, 0);
+}
+
+/**
+ * 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 *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, zero if the first list is larger
+ */
+__attribute__((__nonnull__))
+static inline int cxListCompare(
+        CxList *list,
+        CxList *other
+) {
+    return list->cl->compare(list, 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/src/ucx/cx/map.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,270 @@
+/*
+ * 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 "allocator.h"
+#include "iterator.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 {
+    /** The map class definition. */
+    cx_map_class *cl;
+    /** An allocator that is used for the map elements. */
+    CxAllocator *allocator;
+    /** The number of elements currently stored. */
+    size_t size;
+    // TODO: elemsize + a flag if values shall be copied to the map
+};
+
+/**
+ * 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__, __warn_unused_result__))
+    void *(*remove)(
+            CxMap *map,
+            CxHashKey key
+    );
+
+    /**
+     * Iterator over the key/value pairs.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxIterator (*iterator)(CxMap *map);
+
+    /**
+     * Iterator over the keys.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxIterator (*iterator_keys)(CxMap *map);
+
+    /**
+     * Iterator over the values.
+     */
+    __attribute__((__nonnull__, __warn_unused_result__))
+    CxIterator (*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;
+};
+
+
+/**
+ * Deallocates the memory of the specified map.
+ *
+ * @param map the map to be destroyed
+ */
+__attribute__((__nonnull__))
+static inline void cxMapDestroy(CxMap *map) {
+    // TODO: likely to add auto-free feature for contents in the future
+    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);
+}
+
+/**
+ * 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 key,
+        void *value
+) {
+    return map->cl->put(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 *cxMapGet(
+        CxMap const *map,
+        CxHashKey key
+) {
+    return map->cl->get(map, 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
+ */
+__attribute__((__nonnull__, __warn_unused_result__))
+static inline void *cxMapRemove(
+        CxMap *map,
+        CxHashKey key
+) {
+    return map->cl->remove(map, key);
+}
+
+// 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);
+}
+
+#ifdef    __cplusplus
+}
+#endif
+
+#endif // UCX_MAP_H
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/cx/mempool.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,121 @@
+/*
+ * 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 "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/src/ucx/cx/printf.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,144 @@
+/*
+ * 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
+ */
+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()
+ */
+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()
+ */
+cxmutstr cx_asprintf_a(CxAllocator *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()
+ */
+cxmutstr cx_vasprintf_a(CxAllocator *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 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/src/ucx/cx/string.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,835 @@
+/*
+ * 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;
+
+/**
+ * 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) {literal, sizeof(literal) - 1}
+
+#ifdef __cplusplus
+extern "C" {
+#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 *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 *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 *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 *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
+);
+
+
+/**
+ * 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 *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 *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)
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif //UCX_STRING_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/cx/tree.h	Sun Nov 06 15:53:32 2022 +0100
@@ -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/src/ucx/cx/utils.h	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,125 @@
+/*
+ * 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)++)
+
+/* ----------------------
+ * cx_szmul() definition.
+ * ---------------------- */
+
+#if (__GNUC__ >= 5 || defined(__clang__)) && !defined(CX_NO_SZMUL_BUILTIN)
+#define CX_SZMUL_BUILTIN
+
+#if __WORDSIZE == 32
+/**
+ * Alias for \c __builtin_umul_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_umul_overflow(a, b, result)
+#else /* __WORDSIZE != 32 */
+/**
+ * Alias for \c __builtin_umull_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_umull_overflow(a, b, result)
+#endif /* __WORDSIZE */
+
+#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
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_UTILS_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/hash_key.c	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,107 @@
+/*
+ * 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) {
+    size_t len = key->len;
+    unsigned char const *data = key->data.cbytes;
+
+    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.cstr = 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.cbytes = bytes;
+    key.len = len;
+    cx_hash_murmur(&key);
+    return key;
+}
+
+CxHashKey cx_hash_key(
+        void *obj,
+        size_t len
+) {
+    CxHashKey key;
+    key.data.obj = obj;
+    key.len = len;
+    cx_hash_murmur(&key);
+    return key;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/hash_map.c	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,402 @@
+/*
+ * 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"
+
+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;
+                // free the key data
+                cxFree(map->allocator, elem->key.data.obj);
+                // 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 *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.obj, key.data.obj, key.len) == 0) {
+        // overwrite existing element
+        elm->data = value;
+    } else {
+        // allocate new element
+        struct cx_hash_map_element_s *e = cxMalloc(allocator, sizeof(struct cx_hash_map_element_s));
+        if (e == NULL) {
+            return -1;
+        }
+
+        // write the value
+        // TODO: depending on future map features, we may want to copy here
+        e->data = value;
+
+        // copy the key
+        void *kd = cxMalloc(allocator, key.len);
+        if (kd == NULL) {
+            return -1;
+        }
+        memcpy(kd, key.data.obj, key.len);
+        e->key.data.obj = 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, elm->key.data.obj);
+    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
+ * @return the value corresponding to the key or \c NULL
+ */
+static void *cx_hash_map_get_remove(
+        CxMap *map,
+        CxHashKey key,
+        bool remove
+) {
+    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.obj, key.data.obj, key.len) == 0) {
+                void *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 when remove=false, the map stays untouched
+    return cx_hash_map_get_remove((CxMap *) map, key, false);
+}
+
+static void *cx_hash_map_remove(
+        CxMap *map,
+        CxHashKey key
+) {
+    return cx_hash_map_get_remove(map, key, true);
+}
+
+static void *cx_hash_map_iter_current_entry(CxIterator const *iter) {
+    // struct has to have a compatible signature
+    return (struct cx_map_entry_s *) &(iter->kv_data);
+}
+
+static void *cx_hash_map_iter_current_key(CxIterator const *iter) {
+    struct cx_hash_map_element_s *elm = iter->elem_handle;
+    return &elm->key;
+}
+
+static void *cx_hash_map_iter_current_value(CxIterator const *iter) {
+    struct cx_hash_map_element_s *elm = iter->elem_handle;
+    // TODO: return a pointer to data if this map is storing copies
+    return elm->data;
+}
+
+static bool cx_hash_map_iter_valid(CxIterator const *iter) {
+    return iter->elem_handle != NULL;
+}
+
+static void cx_hash_map_iter_next(CxIterator *iter) {
+    struct cx_hash_map_s *map = iter->src_handle;
+    struct cx_hash_map_element_s *elm = iter->elem_handle;
+
+    // remove current element, if asked
+    if (iter->remove) {
+        // clear the flag
+        iter->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;
+            }
+        }
+
+        // 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
+    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;
+        // TODO: pointer to data if this map is storing copies
+        iter->kv_data.value = elm->data;
+    }
+}
+
+static CxIterator cx_hash_map_iterator(CxMap *map) {
+    CxIterator iter;
+
+    iter.src_handle = map;
+    iter.valid = cx_hash_map_iter_valid;
+    iter.next = cx_hash_map_iter_next;
+    iter.current = cx_hash_map_iter_current_entry;
+
+    iter.slot = 0;
+    iter.index = 0;
+    iter.remove = false;
+
+    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];
+        for (; elm == NULL; iter.slot++) {
+            elm = hash_map->buckets[iter.slot];
+        }
+        iter.elem_handle = elm;
+        iter.kv_data.key = &elm->key;
+        // TODO: pointer to data if this map is storing copies
+        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 *map) {
+    CxIterator iter = cx_hash_map_iterator(map);
+    iter.current = cx_hash_map_iter_current_key;
+    return iter;
+}
+
+static CxIterator cx_hash_map_iterator_values(CxMap *map) {
+    CxIterator iter = cx_hash_map_iterator(map);
+    iter.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,
+};
+
+CxMap *cxHashMapCreate(
+        CxAllocator *allocator,
+        size_t buckets
+) {
+    if (buckets == 0) {
+        // implementation defined default
+        buckets = 16;
+    }
+
+    struct cx_hash_map_s *map = cxMalloc(allocator, 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;
+    map->base.size = 0;
+
+    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/src/ucx/linked_list.c	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,807 @@
+/*
+ * 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 <stdint.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_f(node, follow_ptr) ((follow_ptr)?CX_LL_PTR(node, loc_data):(((char*)(node))+loc_data))
+#define ll_data(node) ll_data_f(node,follow_ptr)
+
+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,
+        bool follow_ptr,
+        CxListComparator 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;
+}
+
+static void *cx_linked_list_sort_merge(
+        ptrdiff_t loc_prev,
+        ptrdiff_t loc_next,
+        ptrdiff_t loc_data,
+        bool follow_ptr,
+        size_t length,
+        void *ls,
+        void *le,
+        void *re,
+        CxListComparator cmp_func
+) {
+    const size_t sbo_len = 1024;
+    void *sbo[sbo_len];
+    void **sorted = (length >= sbo_len) ? 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,
+        bool follow_ptr,
+        CxListComparator 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, follow_ptr,
+                                                 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, follow_ptr, cmp_func);
+
+            // merge sorted list with (also sorted) remainder
+            *begin = cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, follow_ptr,
+                                               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,
+        bool follow_ptr_left,
+        bool follow_ptr_right,
+        CxListComparator cmp_func
+) {
+    void const *left = begin_left, *right = begin_right;
+
+    while (left != NULL && right != NULL) {
+        void const *left_data = ll_data_f(left, follow_ptr_left);
+        void const *right_data = ll_data_f(right, follow_ptr_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 */
+
+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;
+    bool follow_ptr;
+} 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->itemsize);
+
+    // 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->itemsize);
+
+    // 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 int cx_ll_insert(
+        struct cx_list_s *list,
+        size_t index,
+        void const *elem
+) {
+    // out-of bounds check
+    if (index > list->size) return 1;
+
+    // find position efficiently
+    cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1);
+
+    // perform insert
+    return cx_ll_insert_at(list, node, elem);
+}
+
+static int cx_ll_add(
+        struct cx_list_s *list,
+        void const *elem
+) {
+    return cx_ll_insert(list, list->size, elem);
+}
+
+static int cx_pll_insert(
+        struct cx_list_s *list,
+        size_t index,
+        void const *elem
+) {
+    return cx_ll_insert(list, index, &elem);
+}
+
+static int cx_pll_add(
+        struct cx_list_s *list,
+        void const *elem
+) {
+    return cx_ll_insert(list, list->size, &elem);
+}
+
+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;
+
+    // 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_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 void *cx_pll_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 : *(void **) node->payload;
+}
+
+static size_t cx_ll_find(
+        struct cx_list_s const *list,
+        void const *elem
+) {
+    cx_linked_list *ll = (cx_linked_list *) list;
+    return cx_linked_list_find(((cx_linked_list *) list)->begin,
+                               CX_LL_LOC_NEXT, CX_LL_LOC_DATA,
+                               ll->follow_ptr, 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,
+                        ll->follow_ptr, 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,
+                                  left->follow_ptr, right->follow_ptr, list->cmpfunc);
+}
+
+static bool cx_ll_iter_valid(CxIterator const *iter) {
+    return iter->elem_handle != NULL;
+}
+
+static void cx_ll_iter_next(CxIterator *iter) {
+    if (iter->remove) {
+        iter->remove = false;
+        cx_linked_list *ll = iter->src_handle;
+        cx_linked_list_node *node = iter->elem_handle;
+        iter->elem_handle = node->next;
+        cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end,
+                              CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node);
+        ll->base.size--;
+        cxFree(ll->base.allocator, node);
+    } else {
+        iter->index++;
+        cx_linked_list_node *node = iter->elem_handle;
+        iter->elem_handle = node->next;
+    }
+}
+
+static void *cx_ll_iter_current(CxIterator const *iter) {
+    cx_linked_list_node *node = iter->elem_handle;
+    return node->payload;
+}
+
+static void *cx_pll_iter_current(CxIterator const *iter) {
+    cx_linked_list_node *node = iter->elem_handle;
+    return *(void **) node->payload;
+}
+
+static CxIterator cx_ll_iterator(
+        struct cx_list_s *list,
+        size_t index
+) {
+    CxIterator iter;
+    iter.index = index;
+    iter.src_handle = list;
+    iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index);
+    iter.valid = cx_ll_iter_valid;
+    iter.current = cx_ll_iter_current;
+    iter.next = cx_ll_iter_next;
+    iter.remove = false;
+    return iter;
+}
+
+static CxIterator cx_pll_iterator(
+        struct cx_list_s *list,
+        size_t index
+) {
+    CxIterator iter = cx_ll_iterator(list, index);
+    iter.current = cx_pll_iter_current;
+    return iter;
+}
+
+static int cx_ll_insert_iter(
+        CxIterator *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(list, list->size, elem);
+        iter->index = list->size;
+        return result;
+    }
+}
+
+static int cx_pll_insert_iter(
+        CxIterator *iter,
+        void const *elem,
+        int prepend
+) {
+    return cx_ll_insert_iter(iter, &elem, prepend);
+}
+
+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_add,
+        cx_ll_insert,
+        cx_ll_insert_iter,
+        cx_ll_remove,
+        cx_ll_at,
+        cx_ll_find,
+        cx_ll_sort,
+        cx_ll_compare,
+        cx_ll_reverse,
+        cx_ll_iterator
+};
+
+static cx_list_class cx_pointer_linked_list_class = {
+        cx_ll_destructor,
+        cx_pll_add,
+        cx_pll_insert,
+        cx_pll_insert_iter,
+        cx_ll_remove,
+        cx_pll_at,
+        cx_ll_find,
+        cx_ll_sort,
+        cx_ll_compare,
+        cx_ll_reverse,
+        cx_pll_iterator,
+};
+
+CxList *cxLinkedListCreate(
+        CxAllocator const *allocator,
+        CxListComparator comparator,
+        size_t item_size
+) {
+    cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
+    if (list == NULL) return NULL;
+
+    list->follow_ptr = false;
+    list->base.cl = &cx_linked_list_class;
+    list->base.allocator = allocator;
+    list->base.cmpfunc = comparator;
+    list->base.itemsize = item_size;
+    list->base.capacity = SIZE_MAX;
+
+    return (CxList *) list;
+}
+
+CxList *cxPointerLinkedListCreate(
+        CxAllocator const *allocator,
+        CxListComparator comparator
+) {
+    cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list));
+    if (list == NULL) return NULL;
+
+    list->follow_ptr = true;
+    list->base.cl = &cx_pointer_linked_list_class;
+    list->base.allocator = allocator;
+    list->base.cmpfunc = comparator;
+    list->base.itemsize = sizeof(void *);
+    list->base.capacity = SIZE_MAX;
+
+    return (CxList *) list;
+}
+
+CxList *cxLinkedListFromArray(
+        CxAllocator const *allocator,
+        CxListComparator comparator,
+        size_t item_size,
+        size_t num_items,
+        void const *array
+) {
+    CxList *list = cxLinkedListCreate(allocator, comparator, item_size);
+    if (list == NULL) return NULL;
+    cx_for_n (i, num_items) {
+        if (0 != cxListAdd(list, ((const unsigned char *) array) + i * item_size)) {
+            cx_ll_destructor(list);
+            cxFree(allocator, list);
+            return NULL;
+        }
+    }
+    return list;
+}
--- a/src/ucx/list.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/ucx/list.c	Sun Nov 06 15:53:32 2022 +0100
@@ -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,403 +26,28 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx/list.h"
-
-UcxList *ucx_list_clone(const UcxList *l, copy_func fnc, void *data) {
-    return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data);
-}
-
-UcxList *ucx_list_clone_a(UcxAllocator *alloc, const 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;
-    }
-    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);
-}
-
-void ucx_list_free(UcxList *l) {
-    ucx_list_free_a(ucx_default_allocator(), l);
-}
-
-void ucx_list_free_a(UcxAllocator *alloc, UcxList *l) {
-    UcxList *e = l, *f;
-    while (e != NULL) {
-        f = e;
-        e = e->next;
-        alfree(alloc, f);
-    }
-}
-
-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);
-}
-
-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);
-}
+#include "cx/list.h"
 
-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;
-}
-
-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;
-    }
-}
-
-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;
+void cxListDestroy(CxList *list) {
+    switch (list->content_destructor_type) {
+        case CX_DESTRUCTOR_SIMPLE: {
+            CxIterator iter = cxListBegin(list);
+            cx_foreach(void*, elem, iter) {
+                list->simple_destructor(elem);
+            }
+            break;
         }
-        list = list->next;
-        index++;
-    }
-    return -1;
-}
-
-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);
-}
-
-ssize_t ucx_list_find(const 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;
+        case CX_DESTRUCTOR_ADVANCED: {
+            CxIterator iter = cxListBegin(list);
+            cx_foreach(void*, elem, iter) {
+                list->advanced_destructor.func(list->advanced_destructor.data, elem);
             }
-        } else {
-            if (elem == e->data) {
-                return index;
-            }
+            break;
         }
-        index++;
-    }
-    return -1;
-}
-
-int ucx_list_contains(const 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++;
+        case CX_DESTRUCTOR_NONE:
+            break; // nothing
     }
 
-    return s;
-}
-
-static UcxList *ucx_list_sort_merge(size_t 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;
-    size_t n = 0;
-    while (lc && lc != le && rc != re) {
-        if (fnc(lc->data, rc->data, data) <= 0) {
-            sorted[n] = lc;
-            lc = lc->next;
-        } else {
-            sorted[n] = rc;
-            rc = rc->next;
-        }
-        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;
-    size_t 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;
-        size_t 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;
-    }
-}
-
-UcxList *ucx_list_first(const UcxList *l) {
-    if (!l) {
-        return NULL;
-    }
-    
-    const UcxList *e = l;
-    while (e->prev) {
-        e = e->prev;
-    }
-    return (UcxList *)e;
+    list->cl->destructor(list);
+    cxFree(list->allocator, list);
 }
-
-UcxList *ucx_list_remove(UcxList *l, UcxList *e) {
-    return ucx_list_remove_a(ucx_default_allocator(), l, e);
-}
-    
-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;
-}
-
-
-static UcxList* ucx_list_setoperation_a(UcxAllocator *allocator,
-        UcxList const *left, UcxList const *right,
-        cmp_func cmpfnc, void* cmpdata,
-        copy_func cpfnc, void* cpdata,
-        int op) {
-    
-    UcxList *res = NULL;
-    UcxList *cur = NULL;
-    const UcxList *src = left;
-    
-    do {
-        UCX_FOREACH(node, src) {
-            void* elem = node->data;
-            if (
-                (op == 0 && !ucx_list_contains(res, elem, cmpfnc, cmpdata)) ||
-                (op == 1 && ucx_list_contains(right, elem, cmpfnc, cmpdata)) ||
-                (op == 2 && !ucx_list_contains(right, elem, cmpfnc, cmpdata))) {
-                UcxList *nl = almalloc(allocator, sizeof(UcxList));
-                nl->prev = cur;
-                nl->next = NULL;
-                if (cpfnc) {
-                    nl->data = cpfnc(elem, cpdata);
-                } else {
-                    nl->data = elem;
-                }
-                if (cur != NULL)
-                    cur->next = nl;
-                cur = nl;
-                if (res == NULL)
-                    res = cur;
-            }
-        }
-        if (op == 0 && src == left)
-            src = right;
-        else
-            src = NULL;
-    } while (src != NULL);
-    
-    return res;
-}
-
-UcxList* ucx_list_union(UcxList const *left, UcxList const *right,
-        cmp_func cmpfnc, void* cmpdata,
-        copy_func cpfnc, void* cpdata) {
-    return ucx_list_union_a(ucx_default_allocator(),
-            left, right, cmpfnc, cmpdata, cpfnc, cpdata);
-}
-
-UcxList* ucx_list_union_a(UcxAllocator *allocator,
-        UcxList const *left, UcxList const *right,
-        cmp_func cmpfnc, void* cmpdata,
-        copy_func cpfnc, void* cpdata) {
-    
-    return ucx_list_setoperation_a(allocator, left, right,
-            cmpfnc, cmpdata, cpfnc, cpdata, 0);
-}
-
-UcxList* ucx_list_intersection(UcxList const *left, UcxList const *right,
-        cmp_func cmpfnc, void* cmpdata,
-        copy_func cpfnc, void* cpdata) {
-    return ucx_list_intersection_a(ucx_default_allocator(), left, right,
-            cmpfnc, cmpdata, cpfnc, cpdata);
-}
-
-UcxList* ucx_list_intersection_a(UcxAllocator *allocator,
-        UcxList const *left, UcxList const *right,
-        cmp_func cmpfnc, void* cmpdata,
-        copy_func cpfnc, void* cpdata) {
-    
-    return ucx_list_setoperation_a(allocator, left, right,
-            cmpfnc, cmpdata, cpfnc, cpdata, 1);
-}
-
-UcxList* ucx_list_difference(UcxList const *left, UcxList const *right,
-        cmp_func cmpfnc, void* cmpdata,
-        copy_func cpfnc, void* cpdata) {
-    return ucx_list_difference_a(ucx_default_allocator(), left, right,
-            cmpfnc, cmpdata, cpfnc, cpdata);
-}
-
-UcxList* ucx_list_difference_a(UcxAllocator *allocator,
-        UcxList const *left, UcxList const *right,
-        cmp_func cmpfnc, void* cmpdata,
-        copy_func cpfnc, void* cpdata) {
-    
-    return ucx_list_setoperation_a(allocator, left, right,
-            cmpfnc, cmpdata, cpfnc, cpdata, 2);
-}
--- a/src/ucx/logging.c	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +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) {
-            char *fpart = strrchr(file, '/');
-            if (fpart) file = fpart+1;
-            fpart = strrchr(file, '\\');
-            if (fpart) file = fpart+1;
-            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/src/ucx/map.c	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,402 +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 const *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 const *map, copy_func fnc, void *data) {
-    return ucx_map_clone_a(ucx_default_allocator(), map, fnc, data);
-}
-
-UcxMap *ucx_map_clone_a(UcxAllocator *allocator,
-        UcxMap const *map, copy_func fnc, void *data) {
-    size_t bs = (map->count * 5) >> 1;
-    UcxMap *newmap = ucx_map_new_a(allocator, 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 const *map, UcxKey key) {
-    return ucx_map_get_and_remove((UcxMap *)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 const *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;
-}
-
-UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second,
-                      copy_func cpfnc, void* cpdata) {
-    return ucx_map_union_a(ucx_default_allocator(),
-            first, second, cpfnc, cpdata);
-}
-
-UcxMap* ucx_map_union_a(UcxAllocator *allocator,
-                        const UcxMap *first, const UcxMap *second,
-                        copy_func cpfnc, void* cpdata) {
-    UcxMap* result = ucx_map_clone_a(allocator, first, cpfnc, cpdata);
-    ucx_map_copy(second, result, cpfnc, cpdata);
-    return result;
-}
-
-UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second,
-                             copy_func cpfnc, void* cpdata) {
-    return ucx_map_intersection_a(ucx_default_allocator(),
-            first, second, cpfnc, cpdata);
-}
-
-UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
-                               const UcxMap *first, const UcxMap *second,
-                               copy_func cpfnc, void* cpdata) {
-    UcxMap *result = ucx_map_new_a(allocator, first->size < second->size ?
-            first->size : second->size);
-
-    UcxMapIterator iter = ucx_map_iterator(first);
-    void* value;
-    UCX_MAP_FOREACH(key, value, iter) {
-        if (ucx_map_get(second, key)) {
-            ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
-        }
-    }
-
-    return result;
-}
-
-UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second,
-                           copy_func cpfnc, void* cpdata) {
-    return ucx_map_difference_a(ucx_default_allocator(),
-            first, second, cpfnc, cpdata);
-}
-
-UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
-                             const UcxMap *first, const UcxMap *second,
-                             copy_func cpfnc, void* cpdata) {
-
-    UcxMap *result = ucx_map_new_a(allocator, first->size - second->count);
-
-    UcxMapIterator iter = ucx_map_iterator(first);
-    void* value;
-    UCX_MAP_FOREACH(key, value, iter) {
-        if (!ucx_map_get(second, key)) {
-            ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
-        }
-    }
-
-    ucx_map_rehash(result);
-    return result;
-}
\ No newline at end of file
--- a/src/ucx/mempool.c	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/printf.c	Sun Nov 06 15:53:32 2022 +0100
@@ -0,0 +1,109 @@
+/*
+ * 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>
+
+#define UCX_PRINTF_BUFSIZE 256
+
+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[UCX_PRINTF_BUFSIZE];
+    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 {
+        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 *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 *a, char const *fmt, va_list ap) {
+    cxmutstr s;
+    s.ptr = NULL;
+    s.length = 0;
+    char buf[UCX_PRINTF_BUFSIZE];
+    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 = 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/src/ucx/properties.c	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/stack.c	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/string.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/ucx/string.c	Sun Nov 06 15:53:32 2022 +0100
@@ -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,63 +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>
 
 #ifndef _WIN32
+
 #include <strings.h> /* for strncasecmp() */
+
 #endif /* _WIN32 */
 
-sstr_t sstr(char *cstring) {
-    sstr_t string;
-    string.ptr = cstring;
-    string.length = strlen(cstring);
-    return string;
+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)};
+}
+
+cxstring cx_strn(
+        const char *cstring,
+        size_t length
+) {
+    return (cxstring) {cstring, length};
 }
 
-scstr_t scstr(const char *cstring) {
-    scstr_t string;
-    string.ptr = cstring;
-    string.length = strlen(cstring);
-    return string;
+cxstring cx_strcast(cxmutstr str) {
+    return (cxstring) {str.ptr, str.length};
 }
 
-scstr_t scstrn(const char *cstring, size_t length) {
-    scstr_t string;
-    string.ptr = cstring;
-    string.length = length;
-    return string;
+void cx_strfree(cxmutstr *str) {
+    free(str->ptr);
+    str->ptr = NULL;
+    str->length = 0;
 }
 
+void cx_strfree_a(
+        CxAllocator *alloc,
+        cxmutstr *str
+) {
+    cxFree(alloc, str->ptr);
+    str->ptr = NULL;
+    str->length = 0;
+}
 
-size_t scstrnlen(size_t n, ...) {
-    if (n == 0) return 0;
-    
+size_t cx_strlen(
+        size_t count,
+        ...
+) {
+    if (count == 0) return 0;
+
     va_list ap;
-    va_start(ap, n);
-    
+    va_start(ap, count);
     size_t size = 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;
-        }
+    cx_for_n(i, count) {
+        cxstring str = va_arg(ap, cxstring);
         size += str.length;
     }
     va_end(ap);
@@ -90,257 +98,196 @@
     return size;
 }
 
-static sstr_t sstrvcat_a(
-        UcxAllocator *a,
+cxmutstr cx_strcat_a(
+        CxAllocator *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);
-}
-
-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;
-    }
-    return ret;
+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};
 }
 
-scstr_t scstrsubs(scstr_t string, size_t start) {
-    return scstrsubsl(string, start, string.length-start);
+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 (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;
-}
-
-
-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;
+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_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;
+    return (cxstring) {NULL, 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);
-}
-
-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);
+cxmutstr cx_strrchr_m(
+        cxmutstr string,
+        int chr
+) {
+    cxstring result = cx_strrchr(cx_strcast(string), chr);
+    return (cxmutstr) {(char *) result.ptr, result.length};
 }
 
-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);
-}
-
-#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);
+#define STRSTR_SBO_BUFLEN 512
 
+cxstring cx_strstr(
+        cxstring haystack,
+        cxstring needle
+) {
+    if (needle.length == 0) {
+        return haystack;
+    }
 
-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;
+    /* optimize for single-char needles */
+    if (needle.length == 1) {
+        return cx_strchr(haystack, *needle.ptr);
     }
-    
-    const char *result = NULL;
-    size_t resultlen = 0;
-    
+
     /*
      * 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 */
+
+    /* local prefix table */
+    size_t s_prefix_table[STRSTR_SBO_BUFLEN];
+
+    /* check needle 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;
-    
+    bool useheap = needle.length >= STRSTR_SBO_BUFLEN;
+    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);
+    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);
+    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;
         }
     }
@@ -349,151 +296,132 @@
     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 *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 *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) {
@@ -503,7 +431,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);
@@ -517,173 +448,123 @@
     }
 }
 
-sstr_t scstrdup(scstr_t s) {
-    return sstrdup_a(ucx_default_allocator(), s);
+cxmutstr cx_strdup_a(
+        CxAllocator *allocator,
+        cxstring string
+) {
+    cxmutstr result = {
+            cxMalloc(allocator, string.length + 1),
+            string.length
+    };
+    if (result.ptr == NULL) {
+        result.length = 0;
+        return result;
+    }
+    memcpy(result.ptr, string.ptr, string.length);
+    result.ptr[string.length] = '\0';
+    return result;
 }
 
-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;
+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--;
     }
-    
-    return newstring;
+    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;
+}
 
-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_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;
 }
 
-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_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
 }
 
-scstr_t scstrtrim(scstr_t string) {
-    scstr_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
 }
 
-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;
+void cx_strlower(cxmutstr string) {
+    cx_for_n(i, string.length) {
+        string.ptr[i] = (char) tolower(string.ptr[i]);
     }
 }
 
-int scstrsuffix(scstr_t string, scstr_t suffix) {
-    if (string.length == 0) {
-        return suffix.length == 0;
-    }
-    if (suffix.length == 0) {
-        return 1;
-    }
-    
-    if (suffix.length > string.length) {
-        return 0;
-    } else {
-        return memcmp(string.ptr+string.length-suffix.length,
-            suffix.ptr, suffix.length) == 0;
-    }
-}
-
-int scstrcaseprefix(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 {
-        scstr_t subs = scstrsubsl(string, 0, prefix.length);
-        return scstrcasecmp(subs, prefix) == 0;
-    }
-}
-
-int scstrcasesuffix(scstr_t string, scstr_t suffix) {
-    if (string.length == 0) {
-        return suffix.length == 0;
-    }
-    if (suffix.length == 0) {
-        return 1;
+void cx_strupper(cxmutstr string) {
+    cx_for_n(i, string.length) {
+        string.ptr[i] = (char) toupper(string.ptr[i]);
     }
-    
-    if (suffix.length > string.length) {
-        return 0;
-    } else {
-        scstr_t subs = scstrsubs(string, string.length-suffix.length);
-        return scstrcasecmp(subs, suffix) == 0;
-    }
-}
-
-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;
-}
-
-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;
-}
-
-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;
-}
-
-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]);
-    }
-    return ret;
 }
 
 #define REPLACE_INDEX_BUFFER_MAX 100
 
-struct scstrreplace_ibuf {
-    size_t* buf;
-    unsigned int len; /* small indices */
-    struct scstrreplace_ibuf* next;
+struct cx_strreplace_ibuf {
+    size_t *buf;
+    struct cx_strreplace_ibuf *next;
+    unsigned int len;
 };
 
-static void scstrrepl_free_ibuf(struct scstrreplace_ibuf *buf) {
+static void cx_strrepl_free_ibuf(struct cx_strreplace_ibuf *buf) {
     while (buf) {
-        struct scstrreplace_ibuf *next = buf->next;
+        struct cx_strreplace_ibuf *next = buf->next;
         free(buf->buf);
         free(buf);
         buf = next;
     }
 }
 
-sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str,
-                     scstr_t pattern, scstr_t replacement, size_t replmax) {
+cxmutstr cx_strreplacen_a(
+        CxAllocator *allocator,
+        cxstring str,
+        cxstring pattern,
+        cxstring replacement,
+        size_t replmax
+) {
 
     if (pattern.length == 0 || pattern.length > str.length || replmax == 0)
-        return sstrdup(str);
+        return cx_strdup_a(allocator, str);
 
     /* Compute expected buffer length */
     size_t ibufmax = str.length / pattern.length;
@@ -693,34 +574,34 @@
     }
 
     /* Allocate first index buffer */
-    struct scstrreplace_ibuf *firstbuf, *curbuf;
-    firstbuf = curbuf = calloc(1, sizeof(struct scstrreplace_ibuf));
-    if (!firstbuf) return sstrn(NULL, 0);
+    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 sstrn(NULL, 0);
+        return cx_mutstrn(NULL, 0);
     }
 
     /* Search occurrences */
-    scstr_t searchstr = str;
+    cxstring searchstr = str;
     size_t found = 0;
     do {
-        scstr_t match = scstrscstr(searchstr, pattern);
+        cxstring match = cx_strstr(searchstr, pattern);
         if (match.length > 0) {
             /* Allocate next buffer in chain, if required */
             if (curbuf->len == ibuflen) {
-                struct scstrreplace_ibuf *nextbuf =
-                        calloc(1, sizeof(struct scstrreplace_ibuf));
+                struct cx_strreplace_ibuf *nextbuf =
+                        calloc(1, sizeof(struct cx_strreplace_ibuf));
                 if (!nextbuf) {
-                    scstrrepl_free_ibuf(firstbuf);
-                    return sstrn(NULL, 0);
+                    cx_strrepl_free_ibuf(firstbuf);
+                    return cx_mutstrn(NULL, 0);
                 }
                 nextbuf->buf = calloc(ibuflen, sizeof(size_t));
                 if (!nextbuf->buf) {
                     free(nextbuf);
-                    scstrrepl_free_ibuf(firstbuf);
-                    return sstrn(NULL, 0);
+                    cx_strrepl_free_ibuf(firstbuf);
+                    return cx_mutstrn(NULL, 0);
                 }
                 curbuf->next = nextbuf;
                 curbuf = nextbuf;
@@ -738,7 +619,7 @@
     } while (searchstr.length > 0 && found < replmax);
 
     /* Allocate result string */
-    sstr_t result;
+    cxmutstr result;
     {
         ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length;
         size_t rcount = 0;
@@ -748,24 +629,24 @@
             curbuf = curbuf->next;
         } while (curbuf);
         result.length = str.length + rcount * adjlen;
-        result.ptr = almalloc(allocator, result.length);
+        result.ptr = cxMalloc(allocator, result.length + 1);
         if (!result.ptr) {
-            scstrrepl_free_ibuf(firstbuf);
-            return sstrn(NULL, 0);
+            cx_strrepl_free_ibuf(firstbuf);
+            return cx_mutstrn(NULL, 0);
         }
     }
 
     /* Build result string */
     curbuf = firstbuf;
     size_t srcidx = 0;
-    char* destptr = result.ptr;
+    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);
+                memcpy(destptr, str.ptr + srcidx, srclen);
                 destptr += srclen;
                 srcidx += srclen;
             }
@@ -777,31 +658,15 @@
         }
         curbuf = curbuf->next;
     } while (curbuf);
-    memcpy(destptr, str.ptr+srcidx, str.length-srcidx);
+    memcpy(destptr, str.ptr + srcidx, str.length - srcidx);
+
+    /* Result is guaranteed to be zero-terminated */
+    result.ptr[result.length] = '\0';
 
     /* Free index buffer */
-    scstrrepl_free_ibuf(firstbuf);
+    cx_strrepl_free_ibuf(firstbuf);
 
     return result;
 }
 
-sstr_t scstrreplacen(scstr_t str, scstr_t pattern,
-        scstr_t replacement, size_t replmax) {
-    return scstrreplacen_a(ucx_default_allocator(),
-            str, pattern, replacement, replmax);
-}
 
-
-// type adjustment functions
-scstr_t ucx_sc2sc(scstr_t str) {
-    return str;
-}
-scstr_t ucx_ss2sc(sstr_t str) {
-    scstr_t cs;
-    cs.ptr = str.ptr;
-    cs.length = str.length;
-    return cs;
-}
-scstr_t ucx_ss2c_s(scstr_t c) {
-    return c;
-}
--- a/src/ucx/test.c	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/tree.c	Sun Nov 06 15:53:32 2022 +0100
@@ -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/src/ucx/ucx.c	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/ucx/allocator.h	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/ucx/array.h	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,460 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2019 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.
- */
-/**
- * Dynamically allocated array implementation.
- * 
- * @file   array.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_ARRAY_H
-#define	UCX_ARRAY_H
-
-#include "ucx.h"
-#include "allocator.h"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * UCX array type.
- */
-typedef struct {
-    /**
-     * The current capacity of the array.
-     */
-    size_t capacity;
-    /**
-     * The actual number of elements in the array.
-     */
-    size_t size;
-    /**
-     * The size of an individual element in bytes.
-     */
-    size_t elemsize;
-    /**
-     * A pointer to the data.
-     */
-    void* data;
-    /**
-     * The allocator used for the data.
-     */
-    UcxAllocator* allocator;
-} UcxArray;
-
-/**
- * Sets an element in an arbitrary user defined array.
- * The data is copied from the specified data location.
- * 
- * If the capacity is insufficient, the array is automatically reallocated and
- * the possibly new pointer is stored in the <code>array</code> argument.
- * 
- * On reallocation the capacity of the array is doubled until it is sufficient.
- * The new capacity is stored back to <code>capacity</code>.
- *  
- * @param array a pointer to location of the array pointer
- * @param capacity a pointer to the capacity
- * @param elmsize the size of each element
- * @param idx the index of the element to set
- * @param data a pointer to the element data
- * @return zero on success or non-zero on error (errno will be set)
- */
-#define ucx_array_util_set(array, capacity, elmsize, idx, data) \
-    ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \
-                         elmsize, idx, data)
-
-/**
- * Sets an element in an arbitrary user defined array.
- * The data is copied from the specified data location.
- * 
- * If the capacity is insufficient, the array is automatically reallocated
- * using the specified allocator and the possibly new pointer is stored in
- * the <code>array</code> argument.
- * 
- * On reallocation the capacity of the array is doubled until it is sufficient.
- * The new capacity is stored back to <code>capacity</code>. 
- * 
- * @param alloc the allocator that shall be used to reallocate the array
- * @param array a pointer to location of the array pointer
- * @param capacity a pointer to the capacity
- * @param elmsize the size of each element
- * @param idx the index of the element to set
- * @param data a pointer to the element data
- * @return zero on success or non-zero on error (errno will be set)
- */
-int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
-    size_t elmsize, size_t idx, void* data);
-
-/**
- * Stores a pointer in an arbitrary user defined array.
- * The element size of the array must be sizeof(void*).
- * 
- * If the capacity is insufficient, the array is automatically reallocated and
- * the possibly new pointer is stored in the <code>array</code> argument.
- * 
- * On reallocation the capacity of the array is doubled until it is sufficient.
- * The new capacity is stored back to <code>capacity</code>.
- *  
- * @param array a pointer to location of the array pointer
- * @param capacity a pointer to the capacity
- * @param idx the index of the element to set
- * @param ptr the pointer to store
- * @return zero on success or non-zero on error (errno will be set)
- */
-#define ucx_array_util_setptr(array, capacity, idx, ptr) \
-    ucx_array_util_setptr_a(ucx_default_allocator(), (void**)(array), \
-                            capacity, idx, ptr)
-
-/**
- * Stores a pointer in an arbitrary user defined array.
- * The element size of the array must be sizeof(void*).
- * 
- * If the capacity is insufficient, the array is automatically reallocated
- * using the specified allocator and the possibly new pointer is stored in
- * the <code>array</code> argument.
- * 
- * On reallocation the capacity of the array is doubled until it is sufficient.
- * The new capacity is stored back to <code>capacity</code>. 
- * 
- * @param alloc the allocator that shall be used to reallocate the array
- * @param array a pointer to location of the array pointer
- * @param capacity a pointer to the capacity
- * @param idx the index of the element to set
- * @param ptr the pointer to store
- * @return zero on success or non-zero on error (errno will be set)
- */
-int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
-    size_t idx, void* ptr);
-
-
-/**
- * Creates a new UCX array with the given capacity and element size.
- * @param capacity the initial capacity
- * @param elemsize the element size
- * @return a pointer to a new UCX array structure
- */
-UcxArray* ucx_array_new(size_t capacity, size_t elemsize);
-
-/**
- * Creates a new UCX array using the specified allocator.
- * 
- * @param capacity the initial capacity
- * @param elemsize the element size
- * @param allocator the allocator to use
- * @return a pointer to new UCX array structure
- */
-UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
-        UcxAllocator* allocator);
-
-/**
- * Initializes a UCX array structure with the given capacity and element size.
- * The structure must be uninitialized as the data pointer will be overwritten.
- * 
- * @param array the structure to initialize
- * @param capacity the initial capacity
- * @param elemsize the element size
- */
-void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize);
-
-/**
- * Initializes a UCX array structure using the specified allocator.
- * The structure must be uninitialized as the data pointer will be overwritten.
- * 
- * @param array the structure to initialize
- * @param capacity the initial capacity
- * @param elemsize the element size
- * @param allocator the allocator to use
- */
-void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
-        UcxAllocator* allocator);
-
-/**
- * Creates an shallow copy of an array.
- * 
- * This function clones the specified array by using memcpy().
- * If the destination capacity is insufficient, an automatic reallocation is
- * attempted.
- * 
- * Note: if the destination array is uninitialized, the behavior is undefined.
- * 
- * @param dest the array to copy to
- * @param src the array to copy from
- * @return zero on success, non-zero on reallocation failure.
- */
-int ucx_array_clone(UcxArray* dest, UcxArray const* src);
-
-
-/**
- * Compares two UCX arrays element-wise by using a compare function.
- *
- * Elements of the two specified arrays 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.
- * 
- * This function always returns zero, if the element sizes of the arrays do
- * not match and performs no comparisons in this case.
- * 
- * @param array1 the first array
- * @param array2 the second array
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return 1, if and only if the two arrays equal element-wise, 0 otherwise
- */
-int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
-        cmp_func cmpfnc, void* data);
-
-/**
- * Destroys the array.
- * 
- * The data is freed and both capacity and count are reset to zero.
- * If the array structure itself has been dynamically allocated, it has to be
- * freed separately.
- * 
- * @param array the array to destroy
- */
-void ucx_array_destroy(UcxArray *array);
-
-/**
- * Destroys and frees the array.
- * 
- * @param array the array to free
- */
-void ucx_array_free(UcxArray *array);
-
-/**
- * Inserts elements at the end of the array.
- * 
- * This is an O(1) operation.
- * The array will automatically grow, if the capacity is exceeded.
- * If a pointer to data is provided, the data is copied into the array with
- * memcpy(). Otherwise the new elements are completely zeroed.
- * 
- * @param array a pointer the array where to append the data
- * @param data a pointer to the data to insert (may be <code>NULL</code>)
- * @param count number of elements to copy from data (if data is
- * <code>NULL</code>, zeroed elements are appended)
- * @return zero on success, non-zero if a reallocation was necessary but failed
- * @see ucx_array_set_from()
- * @see ucx_array_append()
- */
-int ucx_array_append_from(UcxArray *array, void *data, size_t count);
-
-
-/**
- * Inserts elements at the beginning of the array.
- * 
- * This is an expensive operation, because the contents must be moved.
- * If there is no particular reason to prepend data, you should use
- * ucx_array_append_from() instead.
- * 
- * @param array a pointer the array where to prepend the data
- * @param data a pointer to the data to insert (may be <code>NULL</code>)
- * @param count number of elements to copy from data (if data is
- * <code>NULL</code>, zeroed elements are inserted)
- * @return zero on success, non-zero if a reallocation was necessary but failed
- * @see ucx_array_append_from()
- * @see ucx_array_set_from()
- * @see ucx_array_prepend()
- */
-int ucx_array_prepend_from(UcxArray *array, void *data, size_t count);
-
-
-/**
- * Sets elements starting at the specified index.
- * 
- * If the any index is out of bounds, the array automatically grows.
- * The pointer to the data may be NULL, in which case the elements are zeroed. 
- * 
- * @param array a pointer the array where to set the data
- * @param index the index of the element to set
- * @param data a pointer to the data to insert (may be <code>NULL</code>)
- * @param count number of elements to copy from data (if data is
- * <code>NULL</code>, the memory in the array is zeroed)
- * @return zero on success, non-zero if a reallocation was necessary but failed
- * @see ucx_array_append_from()
- * @see ucx_array_set()
- */
-int ucx_array_set_from(UcxArray *array, size_t index, void *data, size_t count);
-
-/**
- * Concatenates two arrays.
- * 
- * The contents of the second array are appended to the first array in one
- * single operation. The second array is otherwise left untouched.
- * 
- * The first array may grow automatically. If this fails, both arrays remain
- * unmodified.
- * 
- * @param array1 first array
- * @param array2 second array
- * @return zero on success, non-zero if reallocation was necessary but failed 
- * or the element size does not match
- */
-int ucx_array_concat(UcxArray *array1, const UcxArray *array2);
-
-/**
- * Returns a pointer to the array element at the specified index.
- * 
- * @param array the array to retrieve the element from
- * @param index index of the element to return
- * @return a pointer to the element at the specified index or <code>NULL</code>,
- * if the index is greater than the array size
- */
-void *ucx_array_at(UcxArray const* array, size_t index);
-
-/**
- * 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, memcmp() is used.
- * 
- * If the array contains the data more than once, the index of the first
- * occurrence is returned.
- * If the array does not contain the data, the size of array is returned.
- *  
- * @param array the array 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 the size of
- * the array, if the data is not found in this array
- */
-size_t ucx_array_find(UcxArray const *array, void *elem,
-    cmp_func cmpfnc, void *data);
-
-/**
- * Checks, if an array contains a specific element.
- * 
- * An element is found, if ucx_array_find() returns a value less than the size.
- * 
- * @param array the array 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 array contains the specified element data
- * @see ucx_array_find()
- */
-int ucx_array_contains(UcxArray const *array, void *elem,
-    cmp_func cmpfnc, void *data);
-
-/**
- * Sorts a UcxArray with the best available sort algorithm.
- * 
- * The qsort_r() function is used, if available (glibc, FreeBSD or MacOS).
- * The order of arguments is automatically adjusted for the FreeBSD and MacOS
- * version of qsort_r().
- * 
- * If qsort_r() is not available, a merge sort algorithm is used, which is
- * guaranteed to use no more additional memory than for exactly one element.
- * 
- * @param array the array to sort
- * @param cmpfnc the function that shall be used to compare the element data
- * @param data additional data for the cmp_func() or <code>NULL</code>
- */
-void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data);
-
-/**
- * Removes an element from the array.
- * 
- * This is in general an expensive operation, because several elements may
- * be moved. If the order of the elements is not relevant, use
- * ucx_array_remove_fast() instead.
- * 
- * @param array pointer to the array from which the element shall be removed
- * @param index the index of the element to remove
- */
-void ucx_array_remove(UcxArray *array, size_t index);
-
-/**
- * Removes an element from the array.
- * 
- * This is an O(1) operation, but does not maintain the order of the elements.
- * The last element in the array is moved to the location of the removed
- * element.
- * 
- * @param array pointer to the array from which the element shall be removed
- * @param index the index of the element to remove
- */
-void ucx_array_remove_fast(UcxArray *array, size_t index);
-
-/**
- * Shrinks the memory to exactly fit the contents.
- * 
- * After this operation, the capacity equals the size.
- * 
- * @param array a pointer to the array
- * @return zero on success, non-zero if reallocation failed
- */
-int ucx_array_shrink(UcxArray* array);
-
-/**
- * Sets the capacity of the array.
- * 
- * If the new capacity is smaller than the size of the array, the elements
- * are removed and the size is adjusted accordingly.
- * 
- * @param array a pointer to the array
- * @param capacity the new capacity
- * @return zero on success, non-zero if reallocation failed
- */
-int ucx_array_resize(UcxArray* array, size_t capacity);
-
-/**
- * Resizes the array only, if the capacity is insufficient.
- * 
- * If the requested capacity is smaller than the current capacity, this
- * function does nothing.
- * 
- * @param array a pointer to the array
- * @param capacity the guaranteed capacity
- * @return zero on success, non-zero if reallocation failed
- */
-int ucx_array_reserve(UcxArray* array, size_t capacity);
-
-/**
- * Resizes the capacity, if the specified number of elements would not fit.
- * 
- * A call to ucx_array_grow(array, count) is effectively the same as
- * ucx_array_reserve(array, array->size+count).
- * 
- * @param array a pointer to the array
- * @param count the number of elements that should additionally fit
- * into the array
- * @return zero on success, non-zero if reallocation failed
- */
-int ucx_array_grow(UcxArray* array, size_t count);
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_ARRAY_H */
-
--- a/src/ucx/ucx/avl.h	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/ucx/buffer.h	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/ucx/list.h	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,512 +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 = (UcxList*) 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(const 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, const 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(const 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(const 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);
-
-/**
- * Returns the union of two lists.
- * 
- * The union is a list of unique elements regarding cmpfnc obtained from
- * both source lists.
- * 
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the union
- */
-UcxList* ucx_list_union(const UcxList *left, const UcxList *right,
-    cmp_func cmpfnc, void* cmpdata,
-    copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the union of two lists.
- * 
- * The union is a list of unique elements regarding cmpfnc obtained from
- * both source lists.
- * 
- * @param allocator allocates the new list elements
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the union
- */
-UcxList* ucx_list_union_a(UcxAllocator *allocator,
-    const UcxList *left, const UcxList *right,
-    cmp_func cmpfnc, void* cmpdata,
-    copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the intersection of two lists.
- * 
- * The intersection contains all elements of the left list
- * (including duplicates) that can be found in the right list.
- * 
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the intersection
- */
-UcxList* ucx_list_intersection(const UcxList *left, const UcxList *right,
-    cmp_func cmpfnc, void* cmpdata,
-    copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the intersection of two lists.
- * 
- * The intersection contains all elements of the left list
- * (including duplicates) that can be found in the right list.
- * 
- * @param allocator allocates the new list elements
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the intersection
- */
-UcxList* ucx_list_intersection_a(UcxAllocator *allocator,
-    const UcxList *left, const UcxList *right,
-    cmp_func cmpfnc, void* cmpdata,
-    copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the difference of two lists.
- * 
- * The difference contains all elements of the left list
- * (including duplicates) that are not equal to any element of the right list.
- * 
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the difference
- */
-UcxList* ucx_list_difference(const UcxList *left, const UcxList *right,
-    cmp_func cmpfnc, void* cmpdata,
-    copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the difference of two lists.
- * 
- * The difference contains all elements of the left list
- * (including duplicates) that are not equal to any element of the right list.
- * 
- * @param allocator allocates the new list elements
- * @param left the left source list
- * @param right the right source list
- * @param cmpfnc a function to compare elements
- * @param cmpdata additional data for the compare function
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the difference
- */
-UcxList* ucx_list_difference_a(UcxAllocator *allocator,
-    const UcxList *left, const UcxList *right,
-    cmp_func cmpfnc, void* cmpdata,
-    copy_func cpfnc, void* cpdata);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_LIST_H */
-
--- a/src/ucx/ucx/logging.h	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,253 +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>
- *
- * The source file name is reduced to the actual file name. This is necessary to
- * get consistent behavior over different definitions of the __FILE__ macro.
- *
- * <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/src/ucx/ucx/map.h	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,549 +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 const  *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 const *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 const *map, 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 allocator the allocator to use for the cloned map
- * @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_a(UcxAllocator *allocator,
-                        UcxMap const *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 const *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 const *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);
-
-/**
- * Returns the union of two maps.
- *
- * The union is a fresh map which is filled by two successive calls of
- * ucx_map_copy() on the two input maps.
- *
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new map containing the union
- */
-UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second,
-                      copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the union of two maps.
- *
- * The union is a fresh map which is filled by two successive calls of
- * ucx_map_copy() on the two input maps.
- *
- * @param allocator the allocator that shall be used by the new map
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new map containing the union
- */
-UcxMap* ucx_map_union_a(UcxAllocator *allocator,
-                        const UcxMap *first, const UcxMap *second,
-                        copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the intersection of two maps.
- *
- * The intersection is defined as a copy of the first map with every element
- * removed that has no valid key in the second map.
- *
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new map containing the intersection
- */
-UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second,
-                             copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the intersection of two maps.
- *
- * The intersection is defined as a copy of the first map with every element
- * removed that has no valid key in the second map.
- *
- * @param allocator the allocator that shall be used by the new map
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new map containing the intersection
- */
-UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
-                               const UcxMap *first, const UcxMap *second,
-                               copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the difference of two maps.
- *
- * The difference contains a copy of all elements of the first map
- * for which the corresponding keys cannot be found in the second map.
- *
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the difference
- */
-UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second,
-                           copy_func cpfnc, void* cpdata);
-
-/**
- * Returns the difference of two maps.
- *
- * The difference contains a copy of all elements of the first map
- * for which the corresponding keys cannot be found in the second map.
- *
- * @param allocator the allocator that shall be used by the new map
- * @param first the first source map
- * @param second the second source map
- * @param cpfnc a function to copy the elements
- * @param cpdata additional data for the copy function
- * @return a new list containing the difference
- */
-UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
-                             const UcxMap *first, const UcxMap *second,
-                             copy_func cpfnc, void* cpdata);
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_MAP_H */
-
--- a/src/ucx/ucx/mempool.h	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/ucx/properties.h	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/ucx/stack.h	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/ucx/string.h	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1201 +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 an 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 an 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 an immutable version of the provided string
- */
-#define SCSTR(s) s2scstr(s)
-#else
-
-/**
- * One of two type adjustment functions that return an 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 an 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 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 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 share the specified pointer 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 share the specified pointer 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 share the specified pointer 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 share the specified pointer 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 accumulated length of all specified strings.
- * 
- * <b>Attention:</b> if the count argument is larger than the count 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
- */
-size_t scstrnlen(size_t count, ...);
-
-/**
- * Returns the accumulated length of all specified strings.
- * 
- * <b>Attention:</b> if the count argument is larger than the count of the
- * specified strings, the behavior is undefined.
- * 
- * @param count    the total number of specified strings
- * @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, ...);
-
-/**
- * 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
- */
-#define sstrcat(count, s1, ...) scstrcat(count, SCSTR(s1), __VA_ARGS__)
-
-/**
- * Concatenates two or more strings using a UcxAllocator.
- * 
- * The resulting string must be freed by the allocators <code>free()</code>
- * implementation.
- * 
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated.
- *
- * @param alloc   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
- * 
- * @see scstrcat()
- */
-sstr_t scstrcat_a(UcxAllocator *alloc, size_t count, scstr_t s1, ...);
-
-/**
- * Concatenates two or more strings using a UcxAllocator.
- * 
- * The resulting string must be freed by the allocators <code>free()</code>
- * implementation.
- * 
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated.
- *
- * @param alloc   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
- * 
- * @see sstrcat()
- */
-#define sstrcat_a(alloc, count, s1, ...) \
-    scstrcat_a(alloc, 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 is <b>NOT</b> required to 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 the given length starting at the specified location.
- * 
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and is <b>NOT</b> required to 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 is <b>NOT</b> required to 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 is <b>NOT</b> required to 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);
-
-/**
- * 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>
- */
-#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);
-
-/**
- * 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>
- */
-#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 scstrsplit_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);
-
-/**
- * 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 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.
- * 
- * @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);
-
-/**
- * Performing sstrsplit() using a UcxAllocator.
- * 
- * <i>Read the description of sstrsplit() 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.
- * 
- * @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);
-
-/**
- * Compares two UCX strings with standard <code>memcmp()</code>.
- * 
- * At first it compares the sstr_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)
- */
-#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);
-
-/**
- * Compares two UCX strings ignoring the case.
- * 
- * At first it compares the sstr_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.
- */
-#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);
-
-/**
- * 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, regardless of 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);
-
-/**
- * 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, 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()
- */
-#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);
-
-/**
- * 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
- */
-#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);
-
-/**
- * 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
- */
-#define sstrsuffix(string, suffix) scstrsuffix(SCSTR(string), SCSTR(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 1, if and only if the string has the specified prefix, 0 otherwise
- */
-int scstrcaseprefix(scstr_t string, scstr_t prefix);
-
-/**
- * Checks, if a string has a specific prefix, ignoring the case.
- * 
- * @param string the string to check
- * @param prefix the prefix the string should have
- * @return 1, if and only if the string has the specified prefix, 0 otherwise
- */
-#define sstrcaseprefix(string, prefix) \
-  scstrcaseprefix(SCSTR(string), SCSTR(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 1, if and only if the string has the specified suffix, 0 otherwise
- */
-int scstrcasesuffix(scstr_t string, scstr_t suffix);
-
-/**
- * Checks, if a string has a specific suffix, ignoring the case.
- *
- * @param string the string to check
- * @param suffix the suffix the string should have
- * @return 1, if and only if the string has the specified suffix, 0 otherwise
- */
-#define sstrcasesuffix(string, suffix) \
-  scstrcasesuffix(SCSTR(string), SCSTR(suffix))
-
-/**
- * Returns a lower case version of a string.
- * 
- * This function creates a duplicate of the input string, first
- * (see scstrdup()).
- * 
- * @param string the input string
- * @return the resulting lower case string
- * @see scstrdup()
- */
-sstr_t scstrlower(scstr_t string);
-
-/**
- * Returns a lower case version of a string.
- * 
- * This function creates a duplicate of the input string, first
- * (see sstrdup()).
- * 
- * @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 scstrdup_a()).
- * 
- * @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);
-
-
-/**
- * Returns a lower case version of a string.
- * 
- * This function creates a duplicate of the input string, first
- * (see sstrdup_a()).
- * 
- * @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 scstrdup()).
- * 
- * @param string the input string
- * @return the resulting upper case string
- * @see scstrdup()
- */
-sstr_t scstrupper(scstr_t string);
-
-/**
- * Returns a upper case version of a string.
- * 
- * This function creates a duplicate of the input string, first
- * (see sstrdup()).
- * 
- * @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 scstrdup_a()).
- * 
- * @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);
-
-/**
- * Returns a upper case version of a string.
- * 
- * This function creates a duplicate of the input string, first
- * (see sstrdup_a()).
- * 
- * @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)
-
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The resulting string is allocated by the specified allocator. I.e. it
- * depends on the used allocator, whether the sstr_t.ptr must be freed
- * manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @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
- */
-sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str,
-        scstr_t pattern, scstr_t 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 <code>replmax</code> occurrences.
- *
- * The sstr_t.ptr of the resulting string must be freed manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @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
- */
-sstr_t scstrreplacen(scstr_t str, scstr_t pattern,
-        scstr_t 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 <code>replmax</code> occurrences.
- *
- * The resulting string is allocated by the specified allocator. I.e. it
- * depends on the used allocator, whether the sstr_t.ptr must be freed
- * manually.
- *
- * @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
- */
-#define sstrreplacen_a(allocator, str, pattern, replacement, replmax) \
-        scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \
-            SCSTR(replacement), replmax)
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The sstr_t.ptr of the resulting string must be freed manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @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 sstrreplacen(str, pattern, replacement, replmax) \
-        scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), replmax)
-
-/**
- * Replaces a pattern in a string with another string.
- *
- * The pattern is taken literally and is no regular expression.
- * Replaces at most <code>replmax</code> occurrences.
- *
- * The resulting string is allocated by the specified allocator. I.e. it
- * depends on the used allocator, whether the sstr_t.ptr must be freed
- * manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @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 sstrreplace_a(allocator, str, pattern, replacement) \
-        scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \
-            SCSTR(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 <code>replmax</code> occurrences.
- *
- * The sstr_t.ptr of the resulting string must be freed manually.
- *
- * If allocation fails, the sstr_t.ptr of the return value is NULL.
- *
- * @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 sstrreplace(str, pattern, replacement) \
-        scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), SIZE_MAX)
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_STRING_H */
--- a/src/ucx/ucx/test.h	Wed Nov 02 19:19:01 2022 +0100
+++ /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/src/ucx/ucx/ucx.h	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +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   1
-
-/** 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.
-  *
- * @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)
-
-/**
- * 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 ucx_szmul_impl(size_t a, size_t b, size_t *result);
-
-#endif
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_H */
-
--- a/src/ucx/ucx/utils.h	Wed Nov 02 19:19:01 2022 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,508 +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);
-
-/**
- * Compares two integers of type long long.
- * @param i1 pointer to long long one
- * @param i2 pointer to long long 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_longlong(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type int16_t.
- * @param i1 pointer to int16_t one
- * @param i2 pointer to int16_t 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_int16(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type int32_t.
- * @param i1 pointer to int32_t one
- * @param i2 pointer to int32_t 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_int32(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type int64_t.
- * @param i1 pointer to int64_t one
- * @param i2 pointer to int64_t 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_int64(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type unsigned int.
- * @param i1 pointer to unsigned integer one
- * @param i2 pointer to unsigned 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_uint(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type unsigned long int.
- * @param i1 pointer to unsigned long integer one
- * @param i2 pointer to unsigned 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_ulongint(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type unsigned long long.
- * @param i1 pointer to unsigned long long one
- * @param i2 pointer to unsigned long long 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_ulonglong(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type uint16_t.
- * @param i1 pointer to uint16_t one
- * @param i2 pointer to uint16_t 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_uint16(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type uint32_t.
- * @param i1 pointer to uint32_t one
- * @param i2 pointer to uint32_t 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_uint32(const void *i1, const void *i2, void *data);
-
-/**
- * Compares two integers of type uint64_t.
- * @param i1 pointer to uint64_t one
- * @param i2 pointer to uint64_t 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_uint64(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);
-
-/**
- * Distance function for integers of type long long.
- * @param i1 pointer to long long one
- * @param i2 pointer to long long two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type int16_t.
- * @param i1 pointer to int16_t one
- * @param i2 pointer to int16_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type int32_t.
- * @param i1 pointer to int32_t one
- * @param i2 pointer to int32_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type int64_t.
- * @param i1 pointer to int64_t one
- * @param i2 pointer to int64_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type unsigned int.
- * @param i1 pointer to unsigned integer one
- * @param i2 pointer to unsigned integer two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type unsigned long int.
- * @param i1 pointer to unsigned long integer one
- * @param i2 pointer to unsigned long integer two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type unsigned long long.
- * @param i1 pointer to unsigned long long one
- * @param i2 pointer to unsigned long long two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type uint16_t.
- * @param i1 pointer to uint16_t one
- * @param i2 pointer to uint16_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type uint32_t.
- * @param i1 pointer to uint32_t one
- * @param i2 pointer to uint32_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data);
-
-/**
- * Distance function for integers of type uint64_t.
- * @param i1 pointer to uint64_t one
- * @param i2 pointer to uint64_t two
- * @param data omitted
- * @return i1 minus i2
- */
-intmax_t ucx_dist_uint64(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/src/ucx/utils.c	Wed Nov 02 19:19:01 2022 +0100
+++ b/src/ucx/utils.c	Sun Nov 06 15:53:32 2022 +0100
@@ -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,423 +26,21 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx/utils.h"
-
-#include <math.h>
-#include <stdio.h>
-#include <limits.h>
-#include <errno.h>
+#include "cx/utils.h"
 
-/* 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) {
+#ifndef CX_SZMUL_BUILTIN
+int cx_szmul_impl(size_t a, size_t b, size_t *result) {
+    if(a == 0 || b == 0) {
+        *result = 0;
         return 0;
     }
-    
-    char *lbuf;    
-    size_t ncp = 0;
-    
-    if(buf) {
-        lbuf = buf;
-    } else {
-        lbuf = (char*)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);
-        ncp += r;
-        n -= r;
-        rn = bufsize > n ? n : bufsize;
-        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) {
-   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 ucx_cmp_longlong(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_int16(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_int32(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_int64(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_uint(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_ulongint(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_ulonglong(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_uint16(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_uint32(const void *i1, const void *i2, void *data) {
-   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 ucx_cmp_uint64(const void *i1, const void *i2, void *data) {
-   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;
-   }
-}
-
-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;
-}
-
-intmax_t ucx_dist_longlong(const void *i1, const void *i2, void *data) {
-   intmax_t a = *((const long long*) i1);
-   intmax_t b = *((const long long*) i2);
-   return a - b;
-}
-
-intmax_t ucx_dist_int16(const void *i1, const void *i2, void *data) {
-   intmax_t a = *((const int16_t*) i1);
-   intmax_t b = *((const int16_t*) i2);
-   return a - b;
-}
-
-intmax_t ucx_dist_int32(const void *i1, const void *i2, void *data) {
-   intmax_t a = *((const int32_t*) i1);
-   intmax_t b = *((const int32_t*) i2);
-   return a - b;
-}
-
-intmax_t ucx_dist_int64(const void *i1, const void *i2, void *data) {
-   intmax_t a = *((const int64_t*) i1);
-   intmax_t b = *((const int64_t*) i2);
-   return a - b;
-}
-
-intmax_t ucx_dist_uint(const void *i1, const void *i2, void *data) {
-   uintmax_t a = *((const unsigned int*) i1);
-   uintmax_t b = *((const unsigned int*) i2);
-   return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_ulongint(const void *i1, const void *i2, void *data) {
-   uintmax_t a = *((const unsigned long int*) i1);
-   uintmax_t b = *((const unsigned long int*) i2);
-   return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_ulonglong(const void *i1, const void *i2, void *data) {
-   uintmax_t a = *((const unsigned long long*) i1);
-   uintmax_t b = *((const unsigned long long*) i2);
-   return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_uint16(const void *i1, const void *i2, void *data) {
-   uintmax_t a = *((const uint16_t*) i1);
-   uintmax_t b = *((const uint16_t*) i2);
-   return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_uint32(const void *i1, const void *i2, void *data) {
-   uintmax_t a = *((const uint32_t*) i1);
-   uintmax_t b = *((const uint32_t*) i2);
-   return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-intmax_t ucx_dist_uint64(const void *i1, const void *i2, void *data) {
-   uintmax_t a = *((const uint64_t*) i1);
-   uintmax_t b = *((const uint64_t*) i2);
-   return a > b ? (intmax_t)(a - b) : -(intmax_t)(b - a);
-}
-
-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) {
+    size_t r = a * b;
+    if(r / b == a) {
+        *result = r;
         return 0;
     } else {
-        return p1  < p2 ? -1 : 1;
+        *result = 0;
+        return 1;
     }
 }
-
-int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) {
-    return memcmp(ptr1, ptr2, *((size_t*)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
 #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