merge branch config into webdav webdav

Tue, 25 Aug 2020 12:07:56 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 25 Aug 2020 12:07:56 +0200
branch
webdav
changeset 259
0b8692959d37
parent 252
5653a9626cc0 (current diff)
parent 258
134279e804b6 (diff)
child 260
4779a6fb4fbe

merge branch config into webdav

src/server/config/serverconf.c file | annotate | diff | comparison | revisions
src/server/config/serverconf.h file | annotate | diff | comparison | revisions
src/server/test/main.c file | annotate | diff | comparison | revisions
src/ucx/allocator.h file | annotate | diff | comparison | revisions
src/ucx/avl.h file | annotate | diff | comparison | revisions
src/ucx/buffer.h file | annotate | diff | comparison | revisions
src/ucx/list.h file | annotate | diff | comparison | revisions
src/ucx/logging.h file | annotate | diff | comparison | revisions
src/ucx/map.h file | annotate | diff | comparison | revisions
src/ucx/mempool.h file | annotate | diff | comparison | revisions
src/ucx/properties.h file | annotate | diff | comparison | revisions
src/ucx/stack.h file | annotate | diff | comparison | revisions
src/ucx/string.h file | annotate | diff | comparison | revisions
src/ucx/test.h file | annotate | diff | comparison | revisions
src/ucx/ucx.h file | annotate | diff | comparison | revisions
src/ucx/utils.h file | annotate | diff | comparison | revisions
--- a/src/server/Makefile	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/Makefile	Tue Aug 25 12:07:56 2020 +0200
@@ -62,7 +62,7 @@
 OBJ_DIRS = daemon safs ucx util webdav config admin plugins test
 MK_OBJ_DIRS = $(OBJ_DIRS:%=$(OBJ_DIR)server/%)
 
-CFLAGS += -I../
+CFLAGS += -I../ucx/
 LDFLAGS += -lucx
 
 preparation: $(MK_OBJ_DIRS)
--- a/src/server/admin/admin.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/admin/admin.c	Tue Aug 25 12:07:56 2020 +0200
@@ -34,7 +34,6 @@
 #include "../util/pblock.h"
 
 #include "../config/conf.h"
-#include "../config/serverconf.h"
 #include "../config/objconf.h"
 
 static Page *root_page;
--- a/src/server/config/keyfile.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/config/keyfile.c	Tue Aug 25 12:07:56 2020 +0200
@@ -31,7 +31,7 @@
 
 #include "keyfile.h"
 
-KeyfileConfig *load_keyfile_config(char *file) {
+KeyfileConfig *load_keyfile_config(const char *file) {
     FILE *in = fopen(file, "r");
     if(in == NULL) {
         return NULL;
@@ -39,7 +39,7 @@
     
     KeyfileConfig *conf = malloc(sizeof(KeyfileConfig));
     conf->parser.parse = keyfile_parse;
-    conf->file = file;
+    conf->file = strdup(file);
     conf->users = NULL;
 
     int r = cfg_parse_basic_file((ConfigParser*)conf, in);
--- a/src/server/config/keyfile.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/config/keyfile.h	Tue Aug 25 12:07:56 2020 +0200
@@ -50,7 +50,7 @@
     size_t               numgroups;
 } KeyfileEntry;
 
-KeyfileConfig *load_keyfile_config(char *file);
+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);
 
--- a/src/server/config/mimeconf.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/config/mimeconf.c	Tue Aug 25 12:07:56 2020 +0200
@@ -38,7 +38,7 @@
     void           *ptr;
 } ucx_regdestr;
 
-MimeConfig *load_mime_config(char *file) {
+MimeConfig *load_mime_config(const char *file) {
     FILE *in = fopen(file, "r");
     if(in == NULL) {
         return NULL;
@@ -46,7 +46,6 @@
     
     MimeConfig *conf = malloc(sizeof(MimeConfig));
     conf->parser.parse = mimeconf_parse;
-    conf->file = file;
     conf->directives = NULL;
     conf->ntypes = 0;
     int r = cfg_parse_basic_file((ConfigParser*)conf, in);
--- a/src/server/config/mimeconf.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/config/mimeconf.h	Tue Aug 25 12:07:56 2020 +0200
@@ -37,7 +37,6 @@
 
 typedef struct _mime_conf {
     ConfigParser parser;
-    char         *file;
     UcxList      *directives; // MimeDirective list
     int          ntypes;
 } MimeConfig;
@@ -47,7 +46,7 @@
     UcxList *exts; // char*
 } MimeDirective;
 
-MimeConfig *load_mime_config(char *file);
+MimeConfig *load_mime_config(const char *file);
 
 void free_mime_config(MimeConfig *conf);
 
--- a/src/server/config/objs.mk	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/config/objs.mk	Tue Aug 25 12:07:56 2020 +0200
@@ -33,10 +33,10 @@
 CONFOBJ = objconf.o
 CONFOBJ += conf.o
 CONFOBJ += initconf.o
-CONFOBJ += serverconf.o
 CONFOBJ += mimeconf.o
 CONFOBJ += acl.o
 CONFOBJ += keyfile.o
+CONFOBJ += serverconfig.o
 
 CONFOBJS = $(CONFOBJ:%=$(CONF_OBJPRE)%)
 CONFSOURCE = $(CONFOBJ:%.o=config/%.c)
--- a/src/server/config/serverconf.c	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   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 <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "serverconf.h"
-
-ServerConfig *load_server_config(char *file) {
-    FILE *in = fopen(file, "r");
-    if(in == NULL) {
-        return NULL;
-    }
-
-    ServerConfig *conf = malloc(sizeof(ServerConfig));
-    conf->parser.parse = serverconf_parse;
-    conf->file = file;
-    conf->obj = NULL;
-    
-    conf->objects = ucx_map_new(16);
-    int r = cfg_parse_basic_file((ConfigParser*)conf, in);
-    cfg_map_destr(conf->parser.mp->pool, conf->objects);
-    if(r != 0) {
-        // TODO: free
-        return NULL;
-    }
-    
-    fclose(in);
-
-    return conf;
-}
-
-void free_server_config(ServerConfig *conf) {
-    ucx_mempool_destroy(conf->parser.mp->pool);
-    free(conf);
-}
-
-int serverconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line){
-    ServerConfig *conf = p;
-    UcxAllocator *mp = conf->parser.mp;
-    
-    begin->type = cfg_get_line_type(line);
-    switch(begin->type) {
-        case LINE_BEGIN_TAG: {
-            ConfigTag *tag = cfg_parse_begin_tag(line, conf->parser.mp);
-
-            // create server config object
-            ServerConfigObject *obj = OBJ_NEW(
-                    conf->parser.mp,
-                    ServerConfigObject);
-            obj->type = tag->name;
-            obj->begin = begin;
-            obj->end = end;
-            obj->directives = NULL;
-
-            // add object to server config
-            UcxList *list = ucx_map_sstr_get(conf->objects, obj->type);
-            list = ucx_list_append_a(mp, list, obj);
-            ucx_map_sstr_put(conf->objects, obj->type, list);
-            conf->obj = obj;
-
-            break;
-        }
-        case LINE_END_TAG: {
-            sstr_t tag = cfg_get_end_tag_name(line);
-            if(sstrcmp(tag, conf->obj->type) != 0) {
-                log_ereport(LOG_FAILURE, "server.conf: syntax error: wrong close tag");
-                log_ereport(LOG_FAILURE, "open tag: %s   close tag: %s", sstrdup(tag).ptr, sstrdup(conf->obj->type).ptr);
-                exit(-1);
-            }
-            conf->obj = NULL;
-
-            break;
-        }
-        case LINE_DIRECTIVE: {
-            if(conf->obj == NULL) {
-                log_ereport(LOG_FAILURE, "server.conf: directive outside of object");
-                exit(-1);
-            }
-
-            ConfigDirective *d = cfg_parse_directive(line, conf->parser.mp);
-            d->begin = begin;
-            d->end = end;
-
-            //printf("%s.%s\n", conf->obj->type.ptr, d->directive_type.ptr);
-            conf->obj->directives = ucx_list_append_a(mp, conf->obj->directives, d);
-        }
-    }
-    return 0;
-}
-
-
-UcxList* srvcfg_get_listeners(ServerConfig *cfg, UcxAllocator *mp, int *error) {
-    mp = mp ? mp : cfg->parser.mp;
-    
-    UcxList *list = ucx_map_sstr_get(cfg->objects, sstrn("Listener", 8));
-    UcxList *lslist = NULL;
-    UCX_FOREACH(elm, list) {
-        ServerConfigObject *ls = elm->data;
-        sstr_t name = cfg_directivelist_get_str(ls->directives, sstr("Name"));
-        sstr_t port = cfg_directivelist_get_str(ls->directives, sstr("Port"));
-        sstr_t vs = cfg_directivelist_get_str(
-                ls->directives,
-                sstr("DefaultVS"));
-        sstr_t threadpool = cfg_directivelist_get_str(
-                ls->directives,
-                sstr("Threadpool"));
-        
-        CfgListener *listener = OBJ_NEW_N(mp, CfgListener);
-        // threadpool is optional, all other configs must be set
-        if(!name.ptr || !port.ptr || !vs.ptr) {
-            // TODO: log error
-            *error = 1;
-            listener->cfg_correct = 0;
-        } else {
-            listener->cfg_correct = 1;
-        }
-        
-        if(name.ptr) {
-            listener->name = sstrdup_a(mp, name);
-        }
-        if(port.ptr) {
-            // don't expect that port is null terminated, sstrdup it to be sure
-            sstr_t portdp = sstrdup(port);
-            listener->port = atoi(portdp.ptr);
-            free(portdp.ptr);
-        }
-        if(vs.ptr) {
-            listener->vs = sstrdup_a(mp, vs);
-        }
-        if(threadpool.ptr) {
-            listener->threadpool = sstrdup_a(mp, threadpool);
-        }
-        
-        lslist = ucx_list_append_a(mp, lslist, listener);
-    }
-    
-    return lslist;
-}
--- a/src/server/config/serverconf.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2013 Olaf Wintermann. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   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 SERVERCONF_H
-#define	SERVERCONF_H
-
-#include "conf.h"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-typedef struct _server_conf_obj {
-    ConfigLine   *begin;
-    ConfigLine   *end;
-
-    sstr_t       type;
-    UcxList     *directives;
-} ServerConfigObject;
-
-typedef struct _server_conf {
-    ConfigParser parser;
-    char         *file;
-    UcxMap       *objects;  // contains UcxList of ServerConfigObject
-    // parser temp vars
-    ServerConfigObject *obj;
-} ServerConfig;
-
-// server.conf objects
-
-typedef struct _cfg_listener {
-    ServerConfigObject   *cfgobj;
-    sstr_t               name;
-    sstr_t               vs;
-    sstr_t               threadpool;
-    sstr_t               address;
-    int                  port;
-    int                  nacceptors;
-    int                  cfg_correct;
-} CfgListener;
-
-typedef struct _cfg_keyfile_authdb {
-    sstr_t file;
-} CfgKeyfileAuthDB;
-
-typedef struct _cfg_ldap_authdb {
-    sstr_t host;
-    int    port;
-    sstr_t basedn;
-    sstr_t binddn;
-    sstr_t bindpw;
-} CfgLDAPAuthDB;
-
-union authdb {
-    CfgKeyfileAuthDB keyfile;
-    CfgLDAPAuthDB    ldap;
-};
-
-enum authdb_type {
-    AUTHDB_TYPE_KEYFILE,
-    AUTHDB_TYPE_LDAP
-};
-
-typedef struct _cfg_authdb {
-    sstr_t           name;
-    enum authdb_type type;
-    union authdb     cfg;
-    int              cfg_correct;
-} CfgAuthDB;
-
-
-/*
- * Loads and parses a server configuration file. The function only structures
- * the file content to configuration objects with directives. The semantics
- * of the objects and directives can be extracted with the srvcfg_* funcsions.
- */
-ServerConfig *load_server_config(char *file);
-
-/*
- * frees the ServerConfig object
- */
-void free_server_config(ServerConfig *conf);
-
-// private - parses a server.conf line
-int serverconf_parse(void *p, ConfigLine *begin, ConfigLine *end, sstr_t line);
-
-
-UcxList* srvcfg_get_listeners(ServerConfig *cfg, UcxAllocator *mp, int *error);
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* SERVERCONF_H */
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/config/serverconfig.c	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,323 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2020 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 "serverconfig.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ucx/buffer.h>
+#include <ucx/utils.h>
+
+ServerConfig* serverconfig_load(const char *file) {
+    FILE *in = fopen(file, "r");
+    if(in == NULL) {
+        return NULL;
+    }
+    
+    UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
+    if(!buf) {
+        fclose(in);
+        return NULL;
+    }
+    
+    ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write);
+    fclose(in);
+    
+    ServerConfig *scfg = serverconfig_parse(scstrn(buf->space, buf->size));
+    
+    ucx_buffer_free(buf);
+    return scfg;
+}
+
+
+static CFGToken get_next_token(scstr_t content, int *pos) {
+    CFGToken token = { {NULL, 0}, CFG_NO_TOKEN };
+    CFGTokenType type = CFG_TOKEN;
+    
+    int start = *pos;
+    
+    int token_begin = -1;
+    int token_end = content.length-1;
+    
+    int quote = 0;
+    int comment = 0;
+    
+    int i;
+    char prev = 0;
+    for(i=start;i<content.length;i++) {
+        char c = content.ptr[i];
+        if(c == '\n') {
+            if(quote) {
+                *pos = i;
+                return token; // error
+            } else if(start == i) {
+                // single newline char token
+                type = CFG_TOKEN_NEWLINE;
+                token_begin = i;
+                token_end = i+1;
+                break;
+            }
+            
+            token_end = i;
+            if(token_begin < 0) {
+                // only space/comment token
+                token_begin = start;
+                type = comment ? CFG_TOKEN_COMMENT : CFG_TOKEN_SPACE;
+            }
+            // make sure next run will return current newline char as token
+            i--;
+            break;
+        } else if(quote) {
+            if(c == '"' && prev != '\\') {
+                quote = 0;
+            }
+        } else if(comment) {
+            // ignore
+            if(c == '\n') {
+                comment = 0;
+            }
+        } else if(c == '#') {
+            comment = 1;
+        } else if(isspace(c)) {
+            if(token_begin >= 0) {
+                token_end = i;
+                break;
+            }
+        } else if(c == '"') {
+            quote = 1;
+            if(token_begin < 0) {
+                token_begin = i;
+            }
+        } else if(token_begin < 0) {
+            token_begin = i;
+        }
+        prev = c;
+    }
+    
+    *pos = i + 1;
+    
+    if(token_begin < 0) {
+        return token; // error
+    }
+    
+    token.type = type;
+    token.content = scstrsubsl(content, token_begin, token_end - token_begin);
+    return token;
+}
+
+/*
+static void test_print_config(ConfigNode *parent) {
+    UCX_FOREACH(elm, parent->children) {
+        ConfigNode *node = elm->data;
+        
+        if(node->type == CONFIG_NODE_SPACE) {
+            printf("sp: %s", node->text_begin.ptr);
+        } else if(node->type == CONFIG_NODE_COMMENT) {
+            printf("cm: %s", node->text_begin.ptr);
+        } else if(node->type == CONFIG_NODE_OBJECT) {
+            printf("o{: %s : %s", node->name.ptr, node->text_begin.ptr);
+            test_print_config(node);
+            printf("o}: %s", node->text_end.ptr);
+        } else if(node->type == CONFIG_NODE_DIRECTIVE) {
+            printf("di: %s", node->text_begin.ptr);
+        } else {
+            printf("fk: %s", node->text_begin.ptr);
+        }
+    }
+}
+*/
+
+ServerConfig* serverconfig_parse(scstr_t content) {
+    UcxMempool *mp = ucx_mempool_new(512);
+    if(!mp) return NULL;
+    UcxAllocator *a = mp->allocator;
+    
+    ServerConfig *config = ucx_mempool_malloc(mp, sizeof(ServerConfig));
+    if(!config) {
+        ucx_mempool_destroy(mp);
+        return NULL;
+    }
+    config->mp = mp;
+    
+    // PARSE:
+    // first non space/comment token is directive/object name
+    // following tokens are arguments
+    // newline starts new directive
+    // '{' converts directive to object and following directives will
+    // be placed into the object
+    int pos = 0; // needed for tokenizer
+    CFGToken token;
+    
+    ConfigNode *root_obj = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
+    root_obj->type = CONFIG_NODE_OBJECT;
+    
+    UcxList *node_stack = ucx_list_prepend(NULL, root_obj);
+    
+    ConfigNode *current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
+    current->type = CONFIG_NODE_SPACE;
+    ConfigNode *obj = NULL;
+    int obj_closed = 0;
+    
+    int text_start = 0;
+    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) {
+            CFG_NO_TOKEN: break;
+            case CFG_TOKEN_COMMENT: {
+                if(current->type == CONFIG_NODE_SPACE) {
+                    current->type = CONFIG_NODE_COMMENT;
+                }
+                break;
+            }
+            case CFG_TOKEN_SPACE: break;
+            case CFG_TOKEN_NEWLINE: {
+                scstr_t line = scstrsubsl(content, text_start, pos - text_start);
+                text_start = pos;
+                
+                sstr_t line_cp = sstrdup_a(a, line);
+
+                ConfigNode *parent = node_stack->data;
+                if(current->type == CONFIG_NODE_CLOSE_OBJECT) {
+                    parent->text_end = line_cp;
+                    node_stack = ucx_list_remove_a(a, node_stack, node_stack);
+                } 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);
+                    obj->text_begin = new_textbegin;
+                } else {
+                    current->text_begin = line_cp;
+                    ConfigNode *parent = node_stack->data;
+                    parent->children = ucx_list_append_a(a, parent->children, current);
+                }
+                
+                if(obj && obj->type == CONFIG_NODE_OBJECT) {
+                    node_stack = ucx_list_prepend_a(a, node_stack, obj);
+                    obj = NULL;
+                }
+
+                current = ucx_mempool_calloc(mp, 1, sizeof(ConfigNode));
+                current->type = CONFIG_NODE_SPACE;
+                
+                obj_closed = 0;
+                break;
+            }
+            case CFG_TOKEN: {
+                if(!sstrcmp(token.content, S("{"))) {
+                    if(!obj) {
+                        err = 1;
+                        break;
+                    }
+                    obj->type = CONFIG_NODE_OBJECT;
+                    if(current != obj) {
+                        current->type = CONFIG_NODE_OPEN_OBJECT;
+                    }
+                } else if(!sstrcmp(token.content, S("}"))) {
+                    obj_closed = 1; // force newline before next directive
+                    obj = NULL;
+                    current->type = CONFIG_NODE_CLOSE_OBJECT;
+                } else {
+                    if(obj_closed) {
+                        err = 1;
+                        break;
+                    }
+                    
+                    if(!current->name.ptr) {
+                        current->name = sstrdup_a(a, token.content);
+                        current->type = CONFIG_NODE_DIRECTIVE;
+                        obj = current;
+                    } else {
+                        ConfigArg *arg = ucx_mempool_calloc(mp, 1, sizeof(ConfigArg));
+                        // TODO: add support for key/value
+                        arg->value = sstrdup_a(a, token.content);
+                        current->args = ucx_list_append_a(a, current->args, arg);
+                    }
+                }
+                break;
+            }
+        }
+        
+        if(err) {
+            break;
+        }
+    }
+    
+    if(pos < content.length || err) {
+        // content not fully parsed because of an error
+        ucx_mempool_destroy(mp);
+        return NULL;
+    }
+    
+    //test_print_config(&root_obj);
+    config->root = root_obj;
+    config->tab = sstrdup_a(a, SC("\t"));
+    
+    return config;
+}
+
+void serverconfig_free(ServerConfig *cfg) {
+    ucx_mempool_destroy(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 && !sstrcmp(node->name, name)) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name) {
+    UcxList *nodes = NULL;
+    
+    UCX_FOREACH(elm, parent->children) {
+        ConfigNode *node = elm->data;
+        if(node->type == type && !sstrcmp(node->name, name)) {
+            nodes = ucx_list_append(nodes, node);
+        }
+    }
+    
+    return nodes;
+}
+
+scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t 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);
+    }
+    return scstrn(NULL, 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/server/config/serverconfig.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,110 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2020 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 WS_CONFIG_SERVERCONFIG_H
+#define WS_CONFIG_SERVERCONFIG_H
+
+#include <ucx/list.h>
+#include <ucx/map.h>
+#include <ucx/mempool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+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;
+    ConfigNode *root;
+    sstr_t tab;
+};
+
+enum ConfigNodeType {
+    CONFIG_NODE_SPACE = 0,
+    CONFIG_NODE_COMMENT,
+    CONFIG_NODE_OBJECT,
+    CONFIG_NODE_DIRECTIVE,
+    CONFIG_NODE_OPEN_OBJECT,
+    CONFIG_NODE_CLOSE_OBJECT
+};
+
+struct ConfigNode {
+    sstr_t text_begin;
+    sstr_t text_end; 
+    ConfigNodeType type;
+    
+    sstr_t name;
+    UcxList *args;
+    UcxList *children;
+};
+
+struct ConfigArg {
+    sstr_t name;
+    sstr_t value;
+};
+
+
+enum CFGTokenType {
+    CFG_NO_TOKEN = 0,
+    CFG_TOKEN_COMMENT,
+    CFG_TOKEN_SPACE,
+    CFG_TOKEN_NEWLINE,
+    CFG_TOKEN
+};
+
+struct CFGToken {
+    scstr_t content;
+    CFGTokenType type;
+};
+
+ServerConfig* serverconfig_load(const char *file);
+
+ServerConfig* serverconfig_parse(scstr_t content);
+
+void serverconfig_free(ServerConfig *cfg);
+
+ConfigNode* serverconfig_get_node(ConfigNode *parent, ConfigNodeType type, scstr_t name);
+
+UcxList* serverconfig_get_node_list(ConfigNode *parent, ConfigNodeType type, scstr_t name);
+
+scstr_t serverconfig_directive_value(ConfigNode *obj, scstr_t name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WS_CONFIG_SERVERCONFIG_H */
+
--- a/src/server/daemon/acldata.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/acldata.c	Tue Aug 25 12:07:56 2020 +0200
@@ -32,39 +32,20 @@
 
 #include "acldata.h"
 
-ACLData* acl_data_new() {
-    ACLData *dat = malloc(sizeof(ACLData));
-    dat->ref = 1;
+ACLData* acl_data_new(UcxAllocator *a) {
+    ACLData *dat = almalloc(a, sizeof(ACLData));
+    if(!dat) {
+        return NULL;
+    }
     
-    dat->namedACLs = ucx_map_new(16);
+    dat->namedACLs = ucx_map_new_a(a, 16);
+    if(!dat->namedACLs) {
+        return NULL;
+    }
     
     return dat;
 }
 
-void acl_data_ref(ACLData *acldata) {
-    if(acldata) {
-        ws_atomic_inc32(&acldata->ref);
-    }
-}
-
-void acl_data_unref(ACLData *acldata) {
-    uint32_t ref = ws_atomic_dec32(&acldata->ref);
-    if(ref == 0) {
-        UcxMapIterator i = ucx_map_iterator(acldata->namedACLs);
-        WSAcl *acl;
-        UCX_MAP_FOREACH(key, acl, i) {
-            free(acl->ace);
-            free(acl->ece);
-            if(acl->acl.authprompt) {
-                free(acl->acl.authprompt);
-            }
-            free(acl);
-        }
-        ucx_map_free(acldata->namedACLs);
-        free(acldata);
-    }
-}
-
 ACLList* acl_get(ACLData *acldata, char *name) {
     ACLList *acl = ucx_map_cstr_get(acldata->namedACLs, name);
     return acl;
--- a/src/server/daemon/acldata.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/acldata.h	Tue Aug 25 12:07:56 2020 +0200
@@ -41,12 +41,9 @@
     
 typedef struct acl_data {
     UcxMap  *namedACLs;
-    uint32_t ref;
 } ACLData;
 
 ACLData* acl_data_new();
-void acl_data_ref(ACLData *acldata);
-void acl_data_unref(ACLData *acldata);
 
 ACLList* acl_get(ACLData *acldata, char *name);
 
--- a/src/server/daemon/auth.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/auth.h	Tue Aug 25 12:07:56 2020 +0200
@@ -33,6 +33,8 @@
 #include <inttypes.h>
 #include "../public/auth.h"
 
+#include <ucx/map.h>
+
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -48,12 +50,12 @@
 
 typedef struct user_cache_elm UserCacheElm;
 struct user_cache_elm {
-    CachedUser   *user;
-    UserCacheElm *next_user; // next elm in the cached user list
-    UcxKey       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;
+    CachedUser       *user;
+    UserCacheElm     *next_user; // next elm in the cached user list
+    UcxKey           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;
 };
 
 typedef struct {
--- a/src/server/daemon/config.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/config.c	Tue Aug 25 12:07:56 2020 +0200
@@ -55,10 +55,8 @@
 #include "../util/atomic.h"
 #include "ucx/buffer.h"
 
-pool_handle_t *cfg_pool;
+pool_handle_t *init_pool;
 
-// TODO: Funktion für ConfigDirective -> directive
-// TODO: Funktion für UcxList parameter list -> pblock
 
 int load_init_conf(char *file) {
     log_ereport(LOG_VERBOSE, "load_init_conf");
@@ -70,14 +68,14 @@
     }
     UcxAllocator *mp = cfg->parser.mp;
 
-    cfg_pool = pool_create(); // one pool for one Configuration
+    init_pool = pool_create(); // one pool for one Configuration
     UcxList *dirs = cfg->directives;
     while(dirs != NULL) {
         ConfigDirective *dir = dirs->data;
 
         /* create NSAPI directive */
         directive *d = malloc(sizeof(directive));
-        d->param = pblock_create_pool(cfg_pool, 8);
+        d->param = pblock_create_pool(init_pool, 8);
         UcxList *param = cfg_param_list(dir->value, mp);
         while(param != NULL) {
             ConfigParam *p = param->data;
@@ -127,19 +125,29 @@
     return 0;
 }
 
-ServerConfiguration* load_server_conf(ServerConfiguration *old, char *file) {
+ServerConfiguration* load_server_conf(char *file) {
     log_ereport(LOG_VERBOSE, "load_server_conf");
 
-    ServerConfig *serverconf = load_server_config(file);
-    if(serverconf == NULL) {
+    ServerConfig *serverconf = serverconfig_load(file);
+    if(!serverconf) {
         log_ereport(LOG_FAILURE, "Cannot load server.conf");
+        return NULL;
     }
-    ServerConfiguration *serverconfig = calloc(1, sizeof(ServerConfiguration));
+    
+    pool_handle_t *pool = pool_create();
+    
+    
+    ServerConfiguration *serverconfig = pool_calloc(pool, 1, sizeof(ServerConfiguration));
     serverconfig->ref = 1;
-    serverconfig->pool = pool_create();
+    serverconfig->pool = pool;
     serverconfig->listeners = NULL;
     serverconfig->host_vs = ucx_map_new(16);
     serverconfig->authdbs = ucx_map_new(16);
+    
+    UcxAllocator allocator = util_pool_allocator(serverconfig->pool);
+    serverconfig->a = pool_malloc(pool, sizeof(UcxAllocator));
+    *serverconfig->a = allocator;
+    
     // TODO: init serverconfig stuff
     
     
@@ -161,49 +169,54 @@
      */
     
     // init logfile first
-    UcxList *lfl = ucx_map_sstr_get(serverconf->objects, sstrn("LogFile", 7));
-    if(lfl != NULL) {
-        ServerConfigObject *logobj = lfl->data;
-        if(logobj == NULL) {
+    UcxList *list = NULL;
+    
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("LogFile"));
+    if(list) {
+        ConfigNode *logobj = list->data;
+        if(!logobj) {
             // error
-            return NULL;
+            return NULL; // TODO: fix memory leak
         }
         
         int ret = cfg_handle_logfile(serverconfig, logobj);
         if(ret != 0) {
             // cannot initialize log file
-            return NULL;
+            return NULL; // TODO: fix memory leak
         }
     } else {
         // horrible error
         return NULL;
     }
+    ucx_list_free(list);
      
-    UcxList *list = ucx_map_sstr_get(serverconf->objects, sstrn("Runtime", 7));
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Runtime"));
     UCX_FOREACH(elm, list) {
-        ServerConfigObject *scfgobj = elm->data;
-        if(cfg_handle_runtime(serverconfig, scfgobj)) {
+        ConfigNode *runtimeobj = elm->data;
+        if(cfg_handle_runtime(serverconfig, runtimeobj)) {
             // error
             return NULL;
         }
     }
+    ucx_list_free(list);
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("Threadpool", 10));
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Threadpool"));
     UCX_FOREACH(elm, list) {
         if(cfg_handle_threadpool(serverconfig, elm->data)) {
             return NULL;
         }
     }
+    ucx_list_free(list);
     // check thread pool config
     if(check_thread_pool_cfg() != 0) {
         /* critical error */
         return NULL;
     }
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("EventHandler", 12));
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("EventHandler"));
     UCX_FOREACH(elm, list) {
         if(cfg_handle_eventhandler(
-                serverconfig, (ServerConfigObject*)elm->data)) {
+                serverconfig, elm->data)) {
             // error            
             return NULL;
         }
@@ -213,38 +226,43 @@
         /* critical error */
         return NULL;
     }
+    ucx_list_free(list);
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("AccessLog", 9));
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("AccessLog"));
     UCX_FOREACH(elm, list) {
-        ServerConfigObject *scfgobj = elm->data;
+        ConfigNode *scfgobj = elm->data;
         if(cfg_handle_accesslog(serverconfig, scfgobj)) {
             return NULL;
         }
     }
+    ucx_list_free(list);
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("AuthDB", 6));
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("AuthDB"));
     UCX_FOREACH(elm, list) {
-        ServerConfigObject *scfgobj = elm->data;
+        ConfigNode *scfgobj = elm->data;
         if(cfg_handle_authdb(serverconfig, scfgobj)) {
             return NULL;
         }
     }
+    ucx_list_free(list);
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("Listener", 8));
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Listener"));
     UCX_FOREACH(elm, list) {
-        ServerConfigObject *scfgobj = elm->data;
+        ConfigNode *scfgobj = elm->data;
         if(cfg_handle_listener(serverconfig, scfgobj)) {
             return NULL;
         }
     }
+    ucx_list_free(list);
     
-    list = ucx_map_sstr_get(serverconf->objects, sstrn("VirtualServer", 13));
+    list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("VirtualServer"));
     UCX_FOREACH(elm, list) {
-        ServerConfigObject *scfgobj = elm->data;
+        ConfigNode *scfgobj = elm->data;
         if(cfg_handle_vs(serverconfig, scfgobj)) {
             return NULL;
         }
     }
+    ucx_list_free(list);
     
 
     // set VirtualServer for all listeners
@@ -268,7 +286,8 @@
         ls = ls->next;
     }
     
-    free_server_config(serverconf);
+    serverconfig_free(serverconf);
+    
     return serverconfig;
 }
 
@@ -279,8 +298,7 @@
 void cfg_unref(ServerConfiguration *cfg) {
     uint32_t ref = ws_atomic_dec32(&cfg->ref);
     if(ref == 0) {
-        // TODO: free configuration
-        printf("free ServerConfiguration %"PRIxPTR"\n", (intptr_t)cfg);
+        pool_destroy(cfg->pool);
     }
 }
 
@@ -289,53 +307,36 @@
     
 }
 
-int cfg_handle_runtime(ServerConfiguration *cfg, ServerConfigObject *obj) {
-    sstr_t user = cfg_directivelist_get_str(obj->directives, sstr("User"));
+int cfg_handle_runtime(ServerConfiguration *cfg, ConfigNode *obj) { 
+    scstr_t user = serverconfig_directive_value(obj, SC("User"));
     if(user.ptr) {
-        cfg->user = sstrdup_pool(cfg->pool, user);
+        cfg->user = sstrdup_a(cfg->a, user);
     }
-    sstr_t tmp = cfg_directivelist_get_str(obj->directives, sstr("Temp"));
+    scstr_t tmp = serverconfig_directive_value(obj, SC("Temp"));
     if(tmp.ptr) {
-        cfg->tmp = sstrdup_pool(cfg->pool, tmp);
+        cfg->tmp = sstrdup_a(cfg->a, tmp);
     } else {
+        // TODO: do this check after all config loading is done
         log_ereport(LOG_MISCONFIG, "no temporary directory specified");
         return -1;
     }
     
     // mime file
-    sstr_t mf = cfg_directivelist_get_str(obj->directives, sstr("MimeFile"));  
-    sstr_t base = sstr("config/"); 
+    scstr_t mf = serverconfig_directive_value(obj, SC("MimeFile"));  
+    scstr_t base = SC("config/"); 
     sstr_t file = sstrcat(2, base, mf);
     
-    ConfigFile *f = cfgmgr_get_file(file);
-    if(f == NULL) {
-        f = malloc(sizeof(ConfigFile));
-        f->data = NULL;
-        f->file = sstrdup(file);
-        f->reload = mime_conf_reload;
-        f->last_modified = 0;
-        
-        // load the file content
-        //f->reload(f, cfg);
-        if(cfgmgr_reload_file(f, cfg, NULL)) {
-            free(f->file.ptr);
-            free(f);
-            
-            free(file.ptr);
-            return -1;
-        }
-        cfgmgr_attach_file(f);
+    if(mime_conf_load(cfg, file)) {
+        return -1;
     }
     
-    cfg->mimetypes = f->data;
-    
     free(file.ptr);
     return 0;
 }
 
-int cfg_handle_logfile(ServerConfiguration *cfg, ServerConfigObject *obj) {
-    sstr_t file = cfg_directivelist_get_str(obj->directives, sstr("File"));
-    sstr_t lvl = cfg_directivelist_get_str(obj->directives, sstr("Level"));
+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"));
     
     if(file.ptr == NULL || lvl.ptr == NULL) {
         /* missing log file parameters */
@@ -343,21 +344,18 @@
     }
     
     LogConfig logcfg;
-    logcfg.file = sstrdup(file).ptr;
-    logcfg.level = sstrdup(lvl).ptr;
+    logcfg.file = file.ptr;
+    logcfg.level = lvl.ptr;
     logcfg.log_stdout = 0;
     logcfg.log_stderr = 0;
     /* TODO: stdout, stderr config */
     
     int ret = init_log_file(&logcfg);
-    
-    free(logcfg.file);
-    free(logcfg.level);
 
     return ret;
 }
 
-int cfg_handle_threadpool(ServerConfiguration *cfg, ServerConfigObject *obj) {
+int cfg_handle_threadpool(ServerConfiguration *cfg, ConfigNode *obj) {
     ThreadPoolConfig poolcfg;
     poolcfg.min_threads = 4;
     poolcfg.min_threads = 4;
@@ -365,21 +363,11 @@
     poolcfg.queue_size = 64;
     poolcfg.stack_size = 262144;
     
-    sstr_t name = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("Name"));
-    sstr_t min = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("MinThreads"));
-    sstr_t max = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("MaxThreads"));
-    sstr_t stack = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("StackSize"));
-    sstr_t queue = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("QueueSize"));
+    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"));
     // TODO: Type
     
     if(name.length == 0) {
@@ -388,27 +376,19 @@
     }
     
     if(min.length != 0) {
-        min = sstrdup(min);
         poolcfg.min_threads = atoi(min.ptr);
-        free(min.ptr);
     }
     
     if(max.length != 0) {
-        max = sstrdup(max);
         poolcfg.max_threads = atoi(max.ptr);
-        free(max.ptr);
     }
     
     if(stack.length != 0) {
-        stack = sstrdup(stack);
         poolcfg.stack_size = atoi(stack.ptr);
-        free(stack.ptr);
     }
     
     if(queue.length != 0) {
-        queue = sstrdup(queue);
         poolcfg.queue_size = atoi(queue.ptr);
-        free(queue.ptr);
     }
     
     create_threadpool(name, &poolcfg);
@@ -416,32 +396,26 @@
     return 0;
 }
 
-int cfg_handle_eventhandler(ServerConfiguration *c, ServerConfigObject *obj) {
+int cfg_handle_eventhandler(ServerConfiguration *c, ConfigNode *obj) {
     EventHandlerConfig evcfg;
     
-    sstr_t name      = cfg_directivelist_get_str(obj->directives, sstr("Name"));
-    sstr_t threads   = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("Threads"));
-    sstr_t isdefault = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("Default"));
+    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"));
     
     evcfg.name = name;
     
-    sstr_t s = sstrdup(threads);
-    evcfg.nthreads = atoi(s.ptr);
-    free(s.ptr);
+    evcfg.nthreads = atoi(threads.ptr);
     
     evcfg.isdefault = util_getboolean(isdefault.ptr, 0);
     
     return create_event_handler(&evcfg);
 }
 
-int cfg_handle_accesslog(ServerConfiguration *cfg, ServerConfigObject *obj) {
+int cfg_handle_accesslog(ServerConfiguration *cfg, ConfigNode *obj) {
     // TODO: use a name to identify the log file
     
-    sstr_t file = cfg_directivelist_get_str(obj->directives, sstr("File"));
+    scstr_t file = serverconfig_directive_value(obj, SC("File"));
     if(file.ptr == NULL) {
         return 0;
     }
@@ -456,10 +430,10 @@
         return 0;
     }
     AccessLog *log = pool_malloc(cfg->pool, sizeof(AccessLog));
-    log->file = sstrdup_pool(cfg->pool, file);
+    log->file = sstrdup_a(cfg->a, file);
     log->format = format;
     log->log = log_file;
-    cfg->logfiles = ucx_list_append(cfg->logfiles, log);
+    cfg->logfiles = ucx_list_append_a(cfg->a, cfg->logfiles, log);
     
     if(!cfg->default_log) {
         cfg->default_log = log;
@@ -468,61 +442,31 @@
     return 0;
 }
 
-int cfg_handle_authdb(ServerConfiguration *cfg, ServerConfigObject *obj) {
-    sstr_t name = cfg_directivelist_get_str(obj->directives, sstr("Name"));
-    sstr_t type = cfg_directivelist_get_str(obj->directives, sstr("Type"));
+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"));
+    
+    AuthDB *authdb = NULL;
     
     if(!sstrcmp(type, sstr("ldap"))) {
         LDAPConfig conf;
         
-        sstr_t host = cfg_directivelist_get_str(
-                obj->directives,
-                sstr("Host"));
-        sstr_t port = cfg_directivelist_get_str(
-                obj->directives,
-                sstr("Port"));
-        sstr_t basedn = cfg_directivelist_get_str(
-                obj->directives,
-                sstr("BaseDN"));
-        sstr_t binddn = cfg_directivelist_get_str(
-                obj->directives,
-                sstr("BindDN"));
-        sstr_t basepw = cfg_directivelist_get_str(
-                obj->directives,
-                sstr("BindPW"));
-        
-        host = sstrdup(host);
-        port = sstrdup(port);
-        basedn = sstrdup(basedn);
-        binddn = sstrdup(binddn);
-        basepw = sstrdup(basepw);
+        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"));
         
-        conf.hostname = host.ptr;
+        conf.hostname = sstrdup_a(cfg->a, host).ptr;
         conf.port = atoi(port.ptr);
-        conf.basedn = basedn.ptr;
-        conf.binddn = binddn.ptr;
-        conf.bindpw = basepw.ptr;
-        
-        name = sstrdup(name);
-        
-        AuthDB *authdb = create_ldap_authdb(name.ptr, &conf);
-        ucx_map_sstr_put(cfg->authdbs, name, authdb);
+        conf.basedn = sstrdup_a(cfg->a, basedn).ptr;
+        conf.binddn = sstrdup_a(cfg->a, binddn).ptr;
+        conf.bindpw = sstrdup_a(cfg->a, basepw).ptr;
         
-        // TODO: create_ldap_authdb should copy the strings
-        /*
-        free(host.ptr);
-        free(port.ptr);
-        free(basedn.ptr);
-        free(binddn.ptr);
-        free(basepw.ptr);
-        free(name.ptr);
-        */
-        
+        authdb = create_ldap_authdb(cfg, name.ptr, &conf);      
     } else if(!sstrcmp(type, sstr("keyfile"))) {
         // we only need the file parameter
-        sstr_t file = cfg_directivelist_get_str(
-                obj->directives,
-                sstr("File"));
+        scstr_t file = serverconfig_directive_value(obj, SC("File"));
         if(file.length == 0) {
             log_ereport(
                     LOG_MISCONFIG,
@@ -531,67 +475,45 @@
         }
         
         // load keyfile
-        ConfigFile *f = cfgmgr_get_file(file);
-        if(f == NULL) {
-            f = malloc(sizeof(ConfigFile));
-            f->data = NULL;
-            f->file = sstrdup(file);
-            f->reload = keyfile_reload;
-            f->last_modified = 0;
-            //f->reload(f, cfg);
-            if(cfgmgr_reload_file(f, cfg, NULL)) {
-                free(f->file.ptr);
-                free(f);
-                return -1;
-            }
-            cfgmgr_attach_file(f);
-        }
-        
-        // add keyfile authdb
-        Keyfile *keyfile = f->data;
-        keyfile->authdb.name = sstrdup(name).ptr;
-        ucx_map_sstr_put(cfg->authdbs, name, keyfile);
+        authdb = keyfile_load(cfg, file);
     }
 
+    if(authdb) {
+        if(ucx_map_sstr_put(cfg->authdbs, name, authdb)) {
+            return -1;
+        }
+    }
+    
     return 0;
 }
 
-int cfg_handle_listener(ServerConfiguration *cfg, ServerConfigObject *obj) {
+int cfg_handle_listener(ServerConfiguration *cfg, ConfigNode *obj) {
     ListenerConfig lc;
     ZERO(&lc, sizeof(ListenerConfig));
     lc.cfg = cfg;
     lc.port = 8080;
     lc.nacceptors = 1;
     
-    // TODO: use sstrdup_pool?
-    lc.name = sstrdup(cfg_directivelist_get_str(
-            obj->directives,
-            sstr("Name")));
-    lc.port = atoi(cfg_directivelist_get_str(
-            obj->directives,
-            sstr("Port")).ptr);
-    lc.vs = sstrdup(cfg_directivelist_get_str(
-            obj->directives,
-            sstr("DefaultVS")));
-    lc.threadpool = sstrdup(cfg_directivelist_get_str(
-            obj->directives,
-            sstr("Threadpool")));
+    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"));
     
-    sstr_t blockingio = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("BlockingIO"));
-    if(blockingio.ptr) {
-        lc.blockingio = util_getboolean_s(blockingio, WS_FALSE);
-    }
+    // TODO: use sstrdup_pool?
+    lc.name = sstrdup(name);
+    lc.port = atoi(port.ptr);
+    lc.vs = sstrdup(vs);
+    lc.threadpool = sstrdup(thrp);
     
-    sstr_t ssl = cfg_directivelist_get_str(obj->directives, S("SSL"));
+    lc.blockingio = util_getboolean_s(blck, WS_FALSE);
+    
+    scstr_t ssl = serverconfig_directive_value(obj, SC("SSL"));
     if(util_getboolean_s(ssl, WS_FALSE)) {
-        sstr_t cert = cfg_directivelist_get_str(obj->directives, S("Cert"));
-        sstr_t privkey = cfg_directivelist_get_str(obj->directives, S("Key"));
-        sstr_t chain = cfg_directivelist_get_str(obj->directives, S("CertChain"));
-        sstr_t disableprot = cfg_directivelist_get_str(
-                obj->directives,
-                S("SSLDisableProtocol"));
+        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"));
         
         WSBool config_ok = WS_TRUE;
         // TODO: log error
@@ -628,84 +550,50 @@
         return 1;
     }
     
-    listener->default_vs.vs_name = lc.vs.ptr;
-    cfg->listeners = ucx_list_append(cfg->listeners, listener); 
+    listener->default_vs.vs_name = sstrdup_a(cfg->a, lc.vs).ptr;
+    cfg->listeners = ucx_list_append_a(cfg->a, cfg->listeners, listener); 
     
     return 0;
 }
 
-int cfg_handle_vs(ServerConfiguration *cfg, ServerConfigObject *obj) {
+int cfg_handle_vs(ServerConfiguration *cfg, ConfigNode *obj) {
     VirtualServer *vs = vs_new();
 
-    vs->name = sstrdup(cfg_directivelist_get_str(
-            obj->directives,
-            sstr("Name")));
-    vs->host = sstrdup(cfg_directivelist_get_str(
-            obj->directives,
-            sstr("Host")));
-    vs->document_root = sstrdup(cfg_directivelist_get_str(
-            obj->directives,
-            sstr("DocRoot")));
-    sstr_t objfile = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("ObjectFile"));
-    sstr_t aclfile = cfg_directivelist_get_str(
-            obj->directives,
-            sstr("ACLFile"));
+    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")));
+    
+    scstr_t objfile = serverconfig_directive_value(obj, SC("ObjectFile"));
+    scstr_t aclfile = serverconfig_directive_value(obj, SC("ACLFile"));
     
     // load the object config file
     sstr_t base = sstr("config/");
     sstr_t file = sstrcat(2, base, objfile);
-    file = sstrcat(2, base, objfile);
+    // sstrcat with allocator because we want to keep the string
+    file = sstrcat_a(cfg->a, 2, base, objfile);
 
-    // the file is managed by the configuration manager
-    ConfigFile *f = cfgmgr_get_file(file);
-    if(f == NULL) {
-        f = malloc(sizeof(ConfigFile));
-        f->data = NULL;
-        f->file = sstrdup(file);
-        f->reload = object_conf_reload;
-        f->last_modified = 0;
-        //f->reload(f, cfg);
-        if(cfgmgr_reload_file(f, cfg, NULL)) {
-            free(f->file.ptr);
-            free(f);
-            
-            free(file.ptr);
-            return -1;
-        }
-        cfgmgr_attach_file(f);
+    HTTPObjectConfig *httpobj = objconf_load(cfg, file);
+    if(!httpobj) {
+        return -1;
     }
-    vs->objectfile = sstrdup(file);
-    vs->objects = (HTTPObjectConfig*)f->data;
-    free(file.ptr);
+    vs->objectfile = file;
+    vs->objects = httpobj;
     
     
     // load acl config file
     file = sstrcat(2, base, aclfile);
     
-    ConfigFile *aclf = cfgmgr_get_file(file);
-    if(aclf == NULL) {
-        aclf = malloc(sizeof(ConfigFile));
-        aclf->data = NULL;
-        aclf->file = sstrdup(file);
-        aclf->reload = acl_conf_reload;
-        aclf->last_modified = 0;
-        //aclf->reload(aclf, cfg);
-        if(cfgmgr_reload_file(aclf, cfg, NULL)) {
-            free(aclf->file.ptr);
-            free(aclf);
-            
-            free(file.ptr);
-            return -1;
-        }
-        cfgmgr_attach_file(aclf);
+    ACLData *acldata = acl_conf_load(cfg, file);
+    if(!acldata) {
+        return -1;
     }
-    vs->acls = aclf->data;
+    vs->acls = acldata;
+    
     free(file.ptr);
     
+    
     // set the access log for the virtual server
-    // TODO: don't use always the default
+    // TODO: don't always use the default
     vs->log = cfg->default_log;
 
     ucx_map_sstr_put(cfg->host_vs, vs->host, vs);
@@ -714,53 +602,8 @@
 }
 
 
-int object_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
-    HTTPObjectConfig *old_conf = file->data;
-    file->data = load_obj_conf(file->file.ptr);
-    if(old_conf) {
-        object_conf_unref(old_conf);
-    }
-    if(file->data) {
-        return 0;
-    } else {
-        return 1;
-    }
-}
-
-void object_conf_ref(HTTPObjectConfig *conf) {
-    if(conf) {
-        ws_atomic_inc32(&conf->ref);
-    }
-}
-
-void object_conf_unref(HTTPObjectConfig *conf) {
-    uint32_t ref = ws_atomic_dec32(&conf->ref);
-    if(ref == 0) {
-        printf("free HTTPObjectConfig %"PRIxPTR"\n", (intptr_t)conf);
-        pool_destroy(conf->pool);
-    }
-}
-
-HTTPObjectConfig* load_obj_conf(char *file) {
-    log_ereport(LOG_VERBOSE, "load_obj_conf");
-
-    // new conf function test
-    ObjectConfig *cfg = load_object_config(file);
-    UcxAllocator *mp = cfg->parser.mp;
-    if(cfg == NULL) {
-        return NULL;
-    }
-
-    // create object config
-    pool_handle_t *pool = pool_create();
-    HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1);
-    conf->pool = pool;
-
-    // convert ObjectConfig to HTTPObjectConfig
-
-    // add objects
-    conf->nobj = ucx_list_size(cfg->objects);
-    conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*));
+static int convert_objconf(ServerConfiguration *scfg, ObjectConfig *cfg, HTTPObjectConfig *conf, sstr_t file) {
+    pool_handle_t *pool = conf->pool;
     
     UcxList *objlist = cfg->objects;
     int i = 0;
@@ -772,13 +615,16 @@
         char *ppath = NULL;
         if(cob->name.length > 0) {
             name = sstrdup_pool(pool, cob->name).ptr;
+            if(!name) return -1;
         }
         if(cob->ppath.length > 0) {
             ppath = sstrdup_pool(pool, cob->ppath).ptr;
+            if(!ppath) return -1;
         }
 
         // create and add object
         httpd_object *obj = object_new(pool, name);
+        if(!obj) return -1;
         obj->path = NULL;
 
         conf->objects[i] = obj;
@@ -788,8 +634,9 @@
             UcxList *dirs = cob->directives[j];
             while(dirs != NULL) {
                 ConfigDirective *cfgdir = dirs->data;
-                
+
                 directive *d = pool_malloc(pool, sizeof(directive));
+                if(!d) return -1;
                 if(cfgdir->condition) {
                     sstr_t expr = cfgdir->condition->param_str;
                     d->cond = condition_from_str(pool, expr.ptr, expr.length);
@@ -799,7 +646,7 @@
                 d->param = pblock_create_pool(pool, 8);
 
                 // add params
-                UcxList *param = cfg_param_list(cfgdir->value, mp);
+                UcxList *param = cfg_param_list(cfgdir->value, scfg->a);
                 while(param != NULL) {
                     ConfigParam *p = param->data;
                     pblock_nvlinsert(
@@ -814,13 +661,13 @@
                 // get function
                 char *func_name = pblock_findval("fn", d->param);
                 if(!func_name) {
-                    log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file);
-                    return NULL;
+                    log_ereport(LOG_MISCONFIG, "%s: Missing fn parameter", file.ptr);
+                    return -1;
                 }
                 d->func = get_function(func_name);
                 if(!d->func) {
                     log_ereport(LOG_MISCONFIG, "func %s not found", func_name);
-                    return NULL;
+                    return -1;
                 }
 
                 dirs = dirs->next;
@@ -834,66 +681,94 @@
         i++;
         objlist = objlist->next;
     }
+    
+    return 0;
+}
+
+HTTPObjectConfig* objconf_load(ServerConfiguration *scfg, sstr_t file) {
+    log_ereport(LOG_VERBOSE, "load_obj_conf");
+    
+    int ret = 0;
+
+    // create object config
+    pool_handle_t *pool = scfg->pool;
+    HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1);
+    if(!conf) {
+        return NULL;
+    }
+    conf->pool = pool;
+    
+    // load obj config file
+    ObjectConfig *cfg = load_object_config(file.ptr);
+    if(!cfg) {
+        return NULL;
+    }
+
+    // convert ObjectConfig to HTTPObjectConfig
+
+    // add objects
+    conf->nobj = ucx_list_size(cfg->objects);
+    conf->objects = pool_calloc(pool, conf->nobj, sizeof(httpd_object*));
+    if(conf->objects) {
+        ret = convert_objconf(scfg, cfg, conf, file);
+    } else {
+        ret = -1;
+    }
 
     free_object_config(cfg);
 
-    return conf;
+    return !ret ? conf : NULL;
 }
 
-int mime_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
-    MimeConfig *mimecfg = load_mime_config(file->file.ptr);
-    MimeMap *old_conf = file->data;
-    
-    MimeMap *mimemap = malloc(sizeof(MimeMap));
-    mimemap->ref = 1;
-    UcxMap *map = ucx_map_new((mimecfg->ntypes * 3) / 2);
-    mimemap->map = map;
-    
-    // add ext type pairs
-    UCX_FOREACH(md, mimecfg->directives) {
-        MimeDirective *d = md->data;
-        // 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);
-            ucx_map_sstr_put(map, ext, value.ptr);
-        }
+int mime_conf_load(ServerConfiguration *cfg, sstr_t file) {
+    MimeConfig *mimecfg = load_mime_config(file.ptr);
+    if(!mimecfg) {
+        return -1;
     }
     
-    file->data = mimemap;
+    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);
     
-    if(old_conf) {
-        mime_conf_unref(old_conf);
+    if(mimemap && map) {
+        mimemap->map = map;
+        
+        // add ext type pairs
+        UCX_FOREACH(md, mimecfg->directives) {
+            MimeDirective *d = md->data;
+            // 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)) {
+                    ret = -1;
+                    break;
+                }
+            }
+            if(ret) {
+                break;
+            }
+        }
+        
+        cfg->mimetypes = mimemap;
+    } else {
+        ret = -1;
     }
     
     free_mime_config(mimecfg);
-    return 0;
-}
-
-void mime_conf_ref(MimeMap *conf) {
-    if(conf) {
-        ws_atomic_inc32(&conf->ref);
-    }
+    return ret;
 }
 
-void mime_conf_unref(MimeMap *conf) {
-    uint32_t ref = ws_atomic_dec32(&conf->ref);
-    if(ref == 0) {
-        printf("free MimeConfig %"PRIxPTR"\n", (intptr_t)conf);
-        UcxMapIterator i = ucx_map_iterator(conf->map);
-        char *str;
-        UCX_MAP_FOREACH(key, str, i) {
-            free(str);
-        }
-        ucx_map_free(conf->map);
-        free(conf);
-    }
-}
+
 
-int acl_conf_reload(ConfigFile *file, ServerConfiguration *cfg) {
-    ACLFile *aclfile = load_acl_file(file->file.ptr);
+ACLData* acl_conf_load(ServerConfiguration *cfg, sstr_t file) {
+    ACLFile *aclfile = load_acl_file(file.ptr);
     
-    ACLData *acldata = acl_data_new();
+    // TODO: malloc return checks
+    
+    ACLData *acldata = acl_data_new(cfg->a);
     UCX_FOREACH(elm, aclfile->namedACLs) {
         ACLConfig *ac = elm->data;
         ACLList *acl = acl_config_convert(cfg, ac);
@@ -902,17 +777,13 @@
     }
     free_acl_file(aclfile);
     
-    ACLData *old_data = file->data;
-    file->data = acldata;
-    if(old_data) {
-        acl_data_unref(old_data);
-    }
-    
-    return 0;
+    return acldata;
 }
 
 ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl) {
-    WSAcl *acllist = malloc(sizeof(WSAcl));
+    UcxAllocator *a = cfg->a;
+    
+    WSAcl *acllist = almalloc(cfg->a, sizeof(WSAcl));
     acllist->acl.check = (acl_check_f)wsacl_check;
     acllist->acl.authdb = NULL;
     acllist->acl.authprompt = NULL;
@@ -925,8 +796,8 @@
     }
     
     size_t s = ucx_list_size(acl->entries);
-    WSAce **aces = calloc(s, sizeof(WSAce*));
-    WSAce **eces = calloc(s, sizeof(WSAce*));
+    WSAce **tmp_aces = calloc(s, sizeof(WSAce*));
+    WSAce **tmp_eces = calloc(s, sizeof(WSAce*));
     int ai = 0;
     int ei = 0;
     
@@ -935,36 +806,36 @@
         ACEConfig *acecfg = elm->data;
         
         // copy data
-        WSAce *ace = malloc(sizeof(WSAce));
+        WSAce *ace = almalloc(a, sizeof(WSAce));
         ace->access_mask = acecfg->access_mask;
         ace->flags = acecfg->flags;
         ace->type = acecfg->type;
-        ace->who = sstrdup(acecfg->who).ptr;
+        ace->who = sstrdup_a(a, acecfg->who).ptr;
         
         // add the entry to the correct array
         if(ace->type >= ACL_TYPE_AUDIT) {
-            eces[ei] = ace;
+            tmp_eces[ei] = ace;
             ei++;
         } else {
-            aces[ai] = ace;
+            tmp_aces[ai] = ace;
             ai++;
         }
     }
     
     // create new entrie arrays with perfect fitting size
     if(ai > 0) {
-        acllist->ace = calloc(ai, sizeof(WSAce*));
+        acllist->ace = alcalloc(a, ai, sizeof(WSAce*));
     }
     if(ei > 0) {
-        acllist->ece = calloc(ei, sizeof(WSAce*));
+        acllist->ece = alcalloc(a, ei, sizeof(WSAce*));
     }
-    memcpy(acllist->ace, aces, ai*sizeof(WSAce*));
-    memcpy(acllist->ece, eces, ei*sizeof(WSAce*));
+    memcpy(acllist->ace, tmp_aces, ai*sizeof(WSAce*));
+    memcpy(acllist->ece, tmp_eces, ei*sizeof(WSAce*));
     acllist->acenum = ai;
     acllist->ecenum = ei;
     
-    free(aces);
-    free(eces);
+    free(tmp_aces);
+    free(tmp_eces);
     
     // get authentication information
     if(acl->authparam) {
@@ -975,7 +846,7 @@
             AuthDB *authdb = ucx_map_sstr_get(cfg->authdbs, authdb_str);
             acllist->acl.authdb = authdb;
             if(authdb && prompt_str.ptr) {
-                acllist->acl.authprompt = sstrdup(prompt_str).ptr;
+                acllist->acl.authprompt = sstrdup_a(a, prompt_str).ptr;
             }
         }
     }
@@ -983,69 +854,35 @@
     return &acllist->acl;
 }
 
-int keyfile_reload(ConfigFile *file, ServerConfiguration *cfg) {
-    KeyfileConfig *conf = load_keyfile_config(file->file.ptr);
-    if(!conf) {
-        return 1;
+AuthDB* keyfile_load(ServerConfiguration *cfg, scstr_t file) {
+    Keyfile *keyfile = keyfile_new(cfg->a);
+    if(!keyfile) {
+        return NULL;
     }
     
-    Keyfile *keyfile = keyfile_new();
+    KeyfileConfig *conf = load_keyfile_config(file.ptr);
+    if(!conf) {
+        return NULL;
+    }
+    
+    AuthDB *ret = &keyfile->authdb;
     
     UCX_FOREACH(elm, conf->users) {
         KeyfileEntry *user = elm->data;
-        keyfile_add_user(
+        if(keyfile_add_user(
                 keyfile,
                 user->name,
                 user->hashtype,
                 user->hashdata,
                 user->groups,
-                user->numgroups);
+                user->numgroups))
+        {
+            ret = NULL;
+            break;
+        }
     }
     
     free_keyfile_config(conf);
     
-    Keyfile *old_data = file->data;
-    file->data = keyfile;
-    if(old_data) {
-        keyfile_unref(old_data);
-    }
-    
-    return 0;
+    return ret;
 }
-
-
-sstr_t cfg_load_file(sstr_t file) {
-    sstr_t r;
-    r.ptr = NULL;
-    r.length = 0;
-    
-    if(!file.ptr) {
-        return r;
-    }
-    
-    sstr_t f = sstrdup(file);
-    FILE *in = fopen(f.ptr, "r");
-    if(!in) {
-        return r;
-    }
-    
-    UcxBuffer *buf = ucx_buffer_new(NULL, 4096, UCX_BUFFER_AUTOEXTEND);
-    if(!buf) {
-        fclose(in);
-        return r;
-    }
-    
-    if(ucx_stream_copy(in, buf, (read_func)fread, (write_func)ucx_buffer_write) == 0) {
-        fclose(in);
-        ucx_buffer_free(buf);
-        return r;
-    }
-    
-    r.ptr = buf->space;
-    r.length = buf->pos;
-    
-    free(buf);
-    fclose(in);
-    
-    return r;
-}
--- a/src/server/daemon/config.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/config.h	Tue Aug 25 12:07:56 2020 +0200
@@ -35,10 +35,10 @@
 
 #include "../config/objconf.h"
 #include "../config/initconf.h"
-#include "../config/serverconf.h"
 #include "../config/mimeconf.h"
 #include "../config/acl.h"
 #include "../config/keyfile.h"
+#include "../config/serverconfig.h"
 
 #include "acldata.h"
 #include "keyfile_auth.h"
@@ -57,6 +57,8 @@
     
 typedef struct _server_configuration {
     pool_handle_t   *pool;
+    UcxAllocator    *a;
+    
     UcxMap          *host_vs;   // map of all vservers. key is the host name
     UcxList         *listeners; // list of all listeners
     UcxList         *logfiles;
@@ -69,59 +71,42 @@
 } ServerConfiguration;
 
 
-typedef struct ConfigFile ConfigFile;
-
-typedef int(*cfg_reload_f)(ConfigFile*,ServerConfiguration*);
-
-struct ConfigFile {
-    sstr_t         file;
-    time_t         last_modified;
-    cfg_reload_f   reload;
-    void           *data;
-};
-
 struct mime_map {
     UcxMap   *map;
-    uint32_t ref;
 };
 
 int load_init_conf(char *file);
 
 void init_server_config_parser();
 
-int cfg_handle_runtime(ServerConfiguration *cfg, ServerConfigObject *obj);
+int cfg_handle_runtime(ServerConfiguration *cfg, ConfigNode *obj);
 
-int cfg_handle_logfile(ServerConfiguration *cfg, ServerConfigObject *obj);
+int cfg_handle_logfile(ServerConfiguration *cfg, ConfigNode *obj);
 
-int cfg_handle_threadpool(ServerConfiguration *cfg, ServerConfigObject *obj);
+int cfg_handle_threadpool(ServerConfiguration *cfg, ConfigNode *obj);
 
-int cfg_handle_eventhandler(ServerConfiguration *cfg, ServerConfigObject *obj);
+int cfg_handle_eventhandler(ServerConfiguration *cfg, ConfigNode *obj);
 
-int cfg_handle_accesslog(ServerConfiguration *cfg, ServerConfigObject *obj);
+int cfg_handle_accesslog(ServerConfiguration *cfg, ConfigNode *obj);
 
-int cfg_handle_authdb(ServerConfiguration *cfg, ServerConfigObject *obj);
+int cfg_handle_authdb(ServerConfiguration *cfg, ConfigNode *obj);
 
-int cfg_handle_listener(ServerConfiguration *cfg, ServerConfigObject *obj);
+int cfg_handle_listener(ServerConfiguration *cfg, ConfigNode *obj);
 
-int cfg_handle_vs(ServerConfiguration *cfg, ServerConfigObject *obj);
+int cfg_handle_vs(ServerConfiguration *cfg, ConfigNode *obj);
 
-ServerConfiguration* load_server_conf(ServerConfiguration *old, char *file);
+ServerConfiguration* load_server_conf(char *file);
 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);
 
-int object_conf_reload(ConfigFile *file, ServerConfiguration *cfg);
-void object_conf_ref(HTTPObjectConfig *conf);
-void object_conf_unref(HTTPObjectConfig *conf);
-HTTPObjectConfig* load_obj_conf(char *file);
-int mime_conf_reload(ConfigFile *file, ServerConfiguration *cfg);
-void mime_conf_ref(MimeMap *conf);
-void mime_conf_unref(MimeMap *conf);
-int acl_conf_reload(ConfigFile *file, ServerConfiguration *cfg);
+ACLData* acl_conf_load(ServerConfiguration *cfg, sstr_t file);
 ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl);
-int keyfile_reload(ConfigFile *file, ServerConfiguration *cfg);
+AuthDB* keyfile_load(ServerConfiguration *cfg, scstr_t file);
 
-sstr_t cfg_load_file(sstr_t file);
+
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/configmanager.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/configmanager.c	Tue Aug 25 12:07:56 2020 +0200
@@ -38,111 +38,39 @@
 #include "configmanager.h"
 
 static ServerConfiguration *current_config = NULL;
-static time_t sc_last_modified = 0;
 
-static UcxMap *config_files;
 
 static conf_global_vars_s global_vars;
 
 void init_configuration_manager() {
     /* init parser */
     init_server_config_parser();
-
-    config_files = ucx_map_new(16);
 }
 
 NSAPI_PUBLIC conf_global_vars_s* conf_getglobals() {
     return &global_vars;
 }
 
-void cfgmgr_attach_file(ConfigFile *cf) {
-    ucx_map_sstr_put(config_files, cf->file, cf);
-}
-
-ConfigFile* cfgmgr_get_file(sstr_t name) {
-    return ucx_map_sstr_get(config_files, name);
-}
-
-int cfgmgr_reload_file(ConfigFile *f, ServerConfiguration *conf, int *reload) {
-    struct stat s;
-    if(stat(f->file.ptr, &s) != 0) {
-        fprintf(
-                stderr,
-                "Error: Cannot get stat of file %s\n", f->file.ptr);
-        perror("cfgmgr_load_config: stat");
-        return -1;
-    }
+int cfgmgr_load_config(ServerConfiguration **set_cfg) {
+    ServerConfiguration *config = load_server_conf("config/server.conf");
     
-    //printf("1 time: %d - %d\n", f->last_modified, s.st_mtim.tv_sec);
-    if(f->last_modified != s.st_mtime) {
-        /* reload the file */
-        if(f->last_modified != 0) {
-            log_ereport(
-                    LOG_INFORM,
-                    "reload configuration file: %s",
-                    f->file.ptr);
-        }
-        if(f->reload(f, conf)) {
-            return -1;
-        }
-        f->last_modified = s.st_mtime;
-        if(reload) {
-            *reload = 1;
-        }
-    }
-    return 0;
-}
-
-int cfgmgr_load_config(ServerConfiguration **set_cfg) {
-    int cfgreload = 0;
-    
-    /* check config files */  
-    UcxMapIterator iter = ucx_map_iterator(config_files);
-    ConfigFile *f;
-    UCX_MAP_FOREACH(key, f, iter) {
-        if(cfgmgr_reload_file(f, current_config, &cfgreload) == -1) {
-            return -1;
-        }
-    }
-
-    struct stat s;
-    if(stat("config/server.conf", &s) != 0) {
-        perror("stat(\"config/server.conf\")");
+    if(!config) {
         return -1;
     }
-
-    ServerConfiguration *config;
-    if(cfgreload || !current_config || sc_last_modified != s.st_mtime) {
-        config = load_server_conf(
-            current_config,
-            "config/server.conf");
-
-        if(config == NULL) {
-            return -1;
-        }
-        
-        sc_last_modified = s.st_mtime;
-    } else {
-        log_ereport(LOG_VERBOSE, "no reconfig required");
-        config = current_config;
-    }
     
     if(set_cfg) {
          *set_cfg = config;
     }
-    ServerConfiguration *old_conf = NULL;
-    if(current_config != config) {
-        old_conf = current_config;
+    
+    if(current_config) {
+        cfg_unref(current_config);
     }
     current_config = config;
-    if(old_conf) {
-        cfg_unref(old_conf);
-    }
+    
     return 0;
 }
 
 ServerConfiguration *cfgmgr_get_server_config() {
-    //cfg_ref(current_config);
     return current_config;
 }
 
--- a/src/server/daemon/configmanager.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/configmanager.h	Tue Aug 25 12:07:56 2020 +0200
@@ -46,9 +46,6 @@
 
 void init_configuration_manager();
 
-void cfgmgr_attach_file(ConfigFile *cf);
-ConfigFile* cfgmgr_get_file(sstr_t name);
-int cfgmgr_reload_file(ConfigFile *f, ServerConfiguration *conf, int *reload);
 int cfgmgr_load_config(ServerConfiguration **cfg);
 
 ServerConfiguration* cfgmgr_get_server_config();
--- a/src/server/daemon/event.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/event.c	Tue Aug 25 12:07:56 2020 +0200
@@ -26,7 +26,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "../../ucx/map.h"
+#include <ucx/map.h>
 #include "../util/atomic.h"
 
 #include "event.h"
@@ -88,7 +88,7 @@
     }
     
     EventHandlerConfig cfg;
-    cfg.name = sstr("default");
+    cfg.name = SC("default");
     cfg.nthreads = 1;
     cfg.isdefault = 1;
     
--- a/src/server/daemon/event.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/event.h	Tue Aug 25 12:07:56 2020 +0200
@@ -43,7 +43,7 @@
 } EVHandler;
 
 typedef struct event_handler_conf {
-    sstr_t   name;
+    scstr_t  name;
     int      nthreads;
     int      isdefault;
 } EventHandlerConfig;
--- a/src/server/daemon/httplistener.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/httplistener.c	Tue Aug 25 12:07:56 2020 +0200
@@ -237,23 +237,18 @@
         }
         // TODO: cleanup on error
         
-        sstr_t file;
         int ret;
         char errbuf[512];
         
         if(!conf->chainfile.ptr) {
-            file = sstrdup(conf->certfile);
-            ret = SSL_CTX_use_certificate_file(ctx, file.ptr, SSL_FILETYPE_PEM);
-            free(file.ptr);
+            ret = SSL_CTX_use_certificate_file(ctx, conf->certfile.ptr, SSL_FILETYPE_PEM);
             if(!ret) {
                 ERR_error_string(ERR_get_error(), errbuf);
                 log_ereport(LOG_MISCONFIG, "Cannot load ssl chain file: %s", errbuf);
                 return NULL;
             }
         } else {
-            file = sstrdup(conf->chainfile);
-            ret = SSL_CTX_use_certificate_chain_file(ctx, file.ptr);
-            free(file.ptr);
+            ret = SSL_CTX_use_certificate_chain_file(ctx, conf->chainfile.ptr);
             if(!ret) { 
                 ERR_error_string(ERR_get_error(), errbuf);
                 log_ereport(LOG_MISCONFIG, "Cannot load ssl cert file: %s", errbuf);
@@ -261,9 +256,7 @@
             }
         }
         
-        file = sstrdup(conf->privkeyfile);
-        ret = SSL_CTX_use_PrivateKey_file(ctx, file.ptr, SSL_FILETYPE_PEM);
-        free(file.ptr);
+        ret = SSL_CTX_use_PrivateKey_file(ctx, conf->privkeyfile.ptr, SSL_FILETYPE_PEM);
         if(!ret) { 
             ERR_error_string(ERR_get_error(), errbuf);
             log_ereport(LOG_MISCONFIG, "Cannot load ssl key file: %s", errbuf);
--- a/src/server/daemon/httplistener.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/httplistener.h	Tue Aug 25 12:07:56 2020 +0200
@@ -64,10 +64,10 @@
     int                  nacceptors;
     WSBool               blockingio;
     WSBool               ssl;
-    sstr_t               certfile;
-    sstr_t               privkeyfile;
-    sstr_t               chainfile;
-    sstr_t               disable_proto;
+    scstr_t              certfile;
+    scstr_t              privkeyfile;
+    scstr_t              chainfile;
+    scstr_t              disable_proto;
 };
 
 struct _acceptor {
--- a/src/server/daemon/keyfile_auth.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/keyfile_auth.c	Tue Aug 25 12:07:56 2020 +0200
@@ -43,40 +43,18 @@
 
 #include "keyfile_auth.h"
 
-Keyfile* keyfile_new() {
-    Keyfile *keyfile = malloc(sizeof(Keyfile));
+Keyfile* keyfile_new(UcxAllocator *a) {
+    Keyfile *keyfile = alcalloc(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(16);
-    keyfile->ref = 1;
+    keyfile->users = ucx_map_new_a(a, 16);
     return keyfile;
 }
 
-void keyfile_ref(Keyfile *keyfile) {
-    ws_atomic_inc32(&keyfile->ref);
-}
-
-void keyfile_unref(Keyfile *keyfile) {
-    uint32_t ref = ws_atomic_dec32(&keyfile->ref);
-    if(ref == 0) { 
-        UcxMapIterator i = ucx_map_iterator(keyfile->users);
-        KeyfileUser *user;
-        UCX_MAP_FOREACH(key, user, i) {
-            free(user->user.name);
-            free(user->hash);
-            for(int n=0;n<user->numgroups;n++) {
-                free(user->groups[n].ptr);
-            }
-            free(user->groups);
-        }
-        ucx_map_free(keyfile->users);
-        
-        free(keyfile->authdb.name);
-        free(keyfile);
-    }
-}
-
-void keyfile_add_user(
+int keyfile_add_user(
         Keyfile *keyfile,
         sstr_t name,
         enum KeyfileHashType hash_type,
@@ -84,14 +62,16 @@
         sstr_t *groups,
         size_t ngroups)
 {
+    UcxAllocator *a = keyfile->users->allocator;
+    
     if(hash.length < 12) {
         // hash too short
         // TODO: log
-        return;
+        return -1;
     }
     
-    KeyfileUser *user = malloc(sizeof(KeyfileUser));
-    user->user.name = sstrdup(name).ptr;
+    KeyfileUser *user = almalloc(a, sizeof(KeyfileUser));
+    user->user.name = sstrdup_a(a, name).ptr;
     user->user.uid = -1;
     user->user.gid = -1;
     user->user.verify_password = keyfile_user_verify_password;
@@ -99,16 +79,29 @@
     user->user.free = keyfile_user_free;
     
     user->hash_type = hash_type;
-    user->hash = malloc(hash.length + 1);
+    user->hash = almalloc(a, hash.length + 1);
+    
+    if(!user->user.name || !user->hash) {
+        return -1;
+    }
+    
     user->hashlen = util_base64decode(hash.ptr, hash.length, user->hash);
     
-    user->groups = calloc(ngroups, sizeof(sstr_t));
-    for(int i=0;i<ngroups;i++) {
-        user->groups[i] = sstrdup(groups[i]);
+    if(ngroups > 0) {
+        user->groups = alcalloc(a, ngroups, sizeof(sstr_t));
+        if(!user->groups) {
+            return -1;
+        }
+        for(int i=0;i<ngroups;i++) {
+            user->groups[i] = sstrdup(groups[i]);
+        }
+        
+    } else {
+        user->groups = NULL;
     }
     
     // add to keyfile
-    ucx_map_sstr_put(keyfile->users, name, user);
+    return ucx_map_sstr_put(keyfile->users, name, user);
 }
 
 // authdb functions
--- a/src/server/daemon/keyfile_auth.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/keyfile_auth.h	Tue Aug 25 12:07:56 2020 +0200
@@ -48,9 +48,8 @@
 };
 
 struct keyfile {
-    AuthDB   authdb;
-    UcxMap   *users;
-    uint32_t ref;
+    AuthDB       authdb;
+    UcxMap       *users;
 };
 
 struct keyfile_user {
@@ -62,11 +61,9 @@
     size_t               hashlen;
 };
 
-Keyfile* keyfile_new();
-void keyfile_ref(Keyfile *keyfile);
-void keyfile_unref(Keyfile *keyfile);
+Keyfile* keyfile_new(UcxAllocator *a);
 
-void keyfile_add_user(
+int keyfile_add_user(
         Keyfile *keyfile,
         sstr_t user,
         enum KeyfileHashType hash_type,
--- a/src/server/daemon/ldap_auth.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/ldap_auth.c	Tue Aug 25 12:07:56 2020 +0200
@@ -47,9 +47,9 @@
 #endif
 }
 
-AuthDB* create_ldap_authdb(char *name, LDAPConfig *conf) {
-    LDAPAuthDB *authdb = malloc(sizeof(LDAPAuthDB));
-    authdb->authdb.name = strdup(name);
+AuthDB* create_ldap_authdb(ServerConfiguration *cfg, const char *name, LDAPConfig *conf) {
+    LDAPAuthDB *authdb = almalloc(cfg->a, sizeof(LDAPAuthDB));
+    authdb->authdb.name = pool_strdup(cfg->pool, name);
     authdb->authdb.get_user = ldap_get_user;
     authdb->authdb.use_cache = 1;
     authdb->config = *conf;
@@ -64,7 +64,7 @@
     // initialize group cache
     authdb->groups.first = NULL;
     authdb->groups.last = NULL;
-    authdb->groups.map = ucx_map_new(32);
+    authdb->groups.map = ucx_map_new_a(cfg->a, 32);
 
     return (AuthDB*) authdb;
 }
--- a/src/server/daemon/ldap_auth.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/ldap_auth.h	Tue Aug 25 12:07:56 2020 +0200
@@ -34,6 +34,8 @@
 #include <ldap.h>
 #include <ucx/map.h>
 
+#include "config.h"
+
 #ifdef	__cplusplus
 extern "C" {
 #endif
@@ -90,7 +92,7 @@
     LDAPGroup   *next;
 };
 
-AuthDB* create_ldap_authdb(char *name, LDAPConfig *conf);
+AuthDB* create_ldap_authdb(ServerConfiguration *cfg, const char *name, LDAPConfig *conf);
 
 LDAP* get_ldap_session(LDAPAuthDB *authdb);
 
--- a/src/server/daemon/log.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/log.c	Tue Aug 25 12:07:56 2020 +0200
@@ -335,7 +335,9 @@
  * This source file only manages access log files. IO is performed directly
  * by AddLog safs. 
  */
-LogFile* get_access_log_file(sstr_t file) {
+LogFile* get_access_log_file(scstr_t file) {
+    // TODO: this looks dubious
+    
     if(!access_log_files) {
         access_log_files = ucx_map_new(4);
     }
--- a/src/server/daemon/log.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/log.h	Tue Aug 25 12:07:56 2020 +0200
@@ -40,10 +40,10 @@
 #endif
 
 typedef struct {
-    char   *file;
-    char   *level;
-    int    log_stdout;
-    int    log_stderr;
+    const char   *file;
+    const char   *level;
+    int          log_stdout;
+    int          log_stderr;
 } LogConfig;
 
 typedef struct {
@@ -75,7 +75,7 @@
 void log_remove_logdup(LogDup *dup);
 
 // access logging
-LogFile* get_access_log_file(sstr_t file);
+LogFile* get_access_log_file(scstr_t file);
 
 #ifdef	__cplusplus
 }
--- a/src/server/daemon/main.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/main.c	Tue Aug 25 12:07:56 2020 +0200
@@ -39,7 +39,7 @@
 #include "../util/plist.h"
 #include "../util/date.h"
 
-#include "../../ucx/string.h"
+#include <ucx/string.h>
 
 #include "webserver.h"
 #include "log.h"
@@ -167,7 +167,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/srvctrl.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/srvctrl.c	Tue Aug 25 12:07:56 2020 +0200
@@ -37,8 +37,8 @@
 
 #include "../util/systhr.h"
 
-#include "../../ucx/utils.h"
-#include "../../ucx/buffer.h"
+#include <ucx/utils.h>
+#include <ucx/buffer.h>
 
 #include "srvctrl.h"
 
--- a/src/server/daemon/srvctrl.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/srvctrl.h	Tue Aug 25 12:07:56 2020 +0200
@@ -31,7 +31,7 @@
 
 #include "../public/nsapi.h"
 
-#include "../../ucx/string.h"
+#include <ucx/string.h>
 
 #include "config.h"
 
--- a/src/server/daemon/threadpools.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/threadpools.c	Tue Aug 25 12:07:56 2020 +0200
@@ -45,7 +45,7 @@
 static threadpool_t *default_io_pool;
 static threadpool_t *last_io_pool;
 
-int create_threadpool(sstr_t name, ThreadPoolConfig *cfg) { 
+int create_threadpool(scstr_t name, ThreadPoolConfig *cfg) { 
     if(thread_pool_map == NULL) {
         thread_pool_map = ucx_map_new(16);
     }
@@ -77,7 +77,7 @@
     }
 }
 
-int create_io_pool(sstr_t name, int numthreads) {
+int create_io_pool(scstr_t name, int numthreads) {
     if(io_pool_map == NULL) {
         io_pool_map = ucx_map_new(4);
     }
@@ -110,13 +110,13 @@
         cfg.max_threads = 8;
         cfg.queue_size = 64;
         cfg.stack_size = 262144;
-        if(create_threadpool(sstr("default"), &cfg)) {
+        if(create_threadpool(SC("default"), &cfg)) {
             return 1;
         }
     }
     
     if(num_iopools == 0) {
-        if(create_io_pool(sstr("default"), 8)) {
+        if(create_io_pool(SC("default"), 8)) {
             return 1;
         }
     }
--- a/src/server/daemon/threadpools.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/threadpools.h	Tue Aug 25 12:07:56 2020 +0200
@@ -45,7 +45,7 @@
     int queue_size;
 } ThreadPoolConfig;
 
-int create_threadpool(sstr_t name, ThreadPoolConfig *cfg);
+int create_threadpool(scstr_t name, ThreadPoolConfig *cfg);
 
 int check_thread_pool_cfg();
 
--- a/src/server/daemon/vserver.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/vserver.c	Tue Aug 25 12:07:56 2020 +0200
@@ -38,23 +38,6 @@
     return vs;
 }
 
-VirtualServer* vs_copy(VirtualServer *vs, pool_handle_t *pool) {
-    VirtualServer *newvs = malloc(sizeof(VirtualServer));
-    newvs->ref = 1;
-    newvs->document_root = sstrdup_pool(pool, vs->document_root);
-    newvs->host = sstrdup_pool(pool, vs->host);
-    newvs->name = sstrdup_pool(pool, vs->name);
-    newvs->objectfile = sstrdup_pool(pool, vs->objectfile);
-    newvs->acls = vs->acls;
-    acl_data_ref(newvs->acls);
-    newvs->log = vs->log; // TODO: ref
-    
-    newvs->objects = vs->objects;
-    
-    return newvs;
-}
-
-
 
 
 
--- a/src/server/daemon/vserver.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/vserver.h	Tue Aug 25 12:07:56 2020 +0200
@@ -58,7 +58,6 @@
 };
 
 VirtualServer* vs_new();
-VirtualServer* vs_copy(VirtualServer *vs, pool_handle_t *pool);
 
 
 #ifdef	__cplusplus
--- a/src/server/daemon/webserver.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/daemon/webserver.c	Tue Aug 25 12:07:56 2020 +0200
@@ -48,7 +48,7 @@
 #include "../util/io.h"
 #include "../util/util.h"
 
-#include "../../ucx/utils.h"
+#include <ucx/utils.h>
 
 #include "../safs/common.h"
 
@@ -87,7 +87,6 @@
     init_configuration_manager();
     ServerConfiguration *cfg;
     if(cfgmgr_load_config(&cfg) != 0) {
-        fprintf(stderr, "Cannot load configuration\n");
         return -1;
     }
     
--- a/src/server/safs/cgi.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/safs/cgi.c	Tue Aug 25 12:07:56 2020 +0200
@@ -36,9 +36,10 @@
 #include <signal.h>
 #include <sys/wait.h>
 
+#include <ucx/string.h>
+
 #include "../util/util.h"
 #include "../util/pblock.h"
-#include "../../ucx/string.h"
 #include "../daemon/netsite.h"
 #include "../util/io.h"
 
--- a/src/server/safs/cgi.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/safs/cgi.h	Tue Aug 25 12:07:56 2020 +0200
@@ -30,7 +30,7 @@
 #define CGI_H
 
 #include "../public/nsapi.h"
-#include "../../ucx/buffer.h"
+#include <ucx/buffer.h>
 
 #ifdef __cplusplus
 extern "C" {
--- a/src/server/safs/common.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/safs/common.c	Tue Aug 25 12:07:56 2020 +0200
@@ -33,7 +33,7 @@
 
 #include "../util/pblock.h"
 #include "../util/util.h"
-#include "../../ucx/map.h"
+#include <ucx/map.h>
 
 static UcxMap *var_names;
 
--- a/src/server/util/util.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/util/util.c	Tue Aug 25 12:07:56 2020 +0200
@@ -394,7 +394,7 @@
     return def;
 }
 
-int util_getboolean_s(sstr_t s, int def) {
+int util_getboolean_s(scstr_t s, int def) {
     if(s.length == 0) {
         return def;
     }
--- a/src/server/util/util.h	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/server/util/util.h	Tue Aug 25 12:07:56 2020 +0200
@@ -197,7 +197,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(sstr_t s, int def);
+int util_getboolean_s(scstr_t s, int def);
 
 // new
 NSAPI_PUBLIC int util_strtoint(char *str, int64_t *value);
--- a/src/tools/Makefile	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/tools/Makefile	Tue Aug 25 12:07:56 2020 +0200
@@ -30,7 +30,7 @@
 
 include $(BUILD_ROOT)/config.mk
 
-CFLAGS += -I..
+CFLAGS += -I../ucx/
 LDFLAGS += -L../../build/lib -lucx -lwscfg
 
 # list of source files
--- a/src/tools/wstool.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/tools/wstool.c	Tue Aug 25 12:07:56 2020 +0200
@@ -32,7 +32,7 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "../server/config/serverconf.h"
+#include "../server/config/serverconfig.h"
 
 #include "srvctrlsocket.h"
 
@@ -61,8 +61,8 @@
 }
 
 int tool_get_tmpdir(char *configfile) {
-    ServerConfig *serverconf = load_server_config(configfile);
-    UcxList *list = ucx_map_sstr_get(serverconf->objects, sstrn("Runtime", 7));
+    ServerConfig *serverconf = serverconfig_load(configfile);
+    UcxList *list = serverconfig_get_node_list(serverconf->root, CONFIG_NODE_OBJECT, SC("Runtime"));
     if(!list) {
         fprintf(stderr, "Error: No Runtime element in %s\n", configfile);
         return -1;
@@ -71,8 +71,11 @@
         fprintf(stderr, "Error: Multiple Runtime elements in %s\n", configfile);
         return -1;
     }
-    ServerConfigObject *runtime = list->data;
-    sstr_t tmp = cfg_directivelist_get_str(runtime->directives, sstr("Temp"));
+    ConfigNode *runtime = list->data;
+    scstr_t tmp = serverconfig_directive_value(runtime, SC("Temp"));
+    
+    ucx_list_free(list);
+    
     if(!tmp.ptr) {
         fprintf(stderr, "Error: No Temp directive in Runtime Object\n");
         return -1;
--- a/src/ucx/Makefile	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/Makefile	Tue Aug 25 12:07:56 2020 +0200
@@ -31,7 +31,8 @@
 include $(BUILD_ROOT)/config.mk
 
 # list of source files
-SRC  = utils.c
+SRC  = ucx.c
+SRC += utils.c
 SRC += list.c
 SRC += map.c
 SRC += avl.c
@@ -43,6 +44,7 @@
 SRC += logging.c
 SRC += buffer.c
 SRC += stack.c
+SRC += array.c
 
 OBJ = $(SRC:%.c=$(BUILD_ROOT)/build/ucx/%$(OBJ_EXT))
 
--- a/src/ucx/allocator.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/allocator.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,10 +26,11 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "ucx/allocator.h"
+
 #include <stdlib.h>
-#include "allocator.h"
 
-UcxAllocator default_allocator = {
+static UcxAllocator default_allocator = {
     NULL,
     ucx_default_malloc,
     ucx_default_calloc,
--- a/src/ucx/allocator.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,206 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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 */
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/array.c	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,488 @@
+/*
+ * 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, ...) {
+    
+    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;
+
+    va_list ap;
+    va_start(ap, index);
+    int elem = va_arg(ap, int);    
+    memcpy(dest, &elem, elmsize);
+    va_end(ap);
+    
+    return 0;
+}
+
+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_appendv(UcxArray *array, ...) {
+    va_list ap;
+    va_start(ap, array);
+    int elem = va_arg(ap, int);
+    int ret = ucx_array_append_from(array, &elem, 1);
+    va_end(ap);
+    return ret;
+}
+
+int ucx_array_prependv(UcxArray *array, ...) {
+    va_list ap;
+    va_start(ap, array);
+    int elem = va_arg(ap, int);
+    int ret = ucx_array_prepend_from(array, &elem, 1);
+    va_end(ap);
+    return ret;
+}
+
+int ucx_array_setv(UcxArray *array, size_t index, ...) {
+    va_list ap;
+    va_start(ap, index);
+    int elem = va_arg(ap, int);
+    int ret = ucx_array_set_from(array, index, &elem, 1);
+    va_end(ap);
+    return ret;
+}
+
+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) {
+    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;
+        }
+    }
+}
--- a/src/ucx/avl.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/avl.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,7 +26,9 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "avl.h"
+#include "ucx/avl.h"
+
+#include <limits.h>
 
 #define ptrcast(ptr) ((void*)(ptr))
 #define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree))
@@ -134,6 +136,23 @@
     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;
@@ -149,6 +168,46 @@
     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);
 }
@@ -272,3 +331,43 @@
 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;
+    }
+}
--- a/src/ucx/avl.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,249 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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_strcmp() 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.
- * @param tree the tree to destroy
- */
-void ucx_avl_free(UcxAVLTree *tree);
-
-/**
- * Macro for initializing a new UcxAVLTree with the default allocator and a
- * ucx_ptrcmp() compare function.
- * 
- * @return a new default UcxAVLTree object
- */
-#define ucx_avl_default_new() ucx_avl_new_a(ucx_ptrcmp, 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);
-
-/**
- * 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 it's 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);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_AVL_H */
-
--- a/src/ucx/buffer.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/buffer.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,7 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "buffer.h"
+#include "ucx/buffer.h"
+
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
@@ -64,8 +65,9 @@
 
 UcxBuffer* ucx_buffer_extract(
         UcxBuffer *src, size_t start, size_t length, int flags) {
-    
-    if (src->size == 0 || length == 0 || start+length > src->capacity) {
+    if (src->size == 0 || length == 0 ||
+        ((size_t)-1) - start < length || start+length > src->capacity)
+    {
         return NULL;
     }
 
@@ -149,7 +151,10 @@
 
 size_t ucx_buffer_write(const void *ptr, size_t size, size_t nitems,
         UcxBuffer *buffer) {
-    size_t len = size * nitems;
+    size_t len;
+    if(ucx_szmul(size, nitems, &len)) {
+        return 0;
+    }
     size_t required = buffer->pos + len;
     if (buffer->pos > required) {
         return 0;
@@ -183,7 +188,10 @@
 
 size_t ucx_buffer_read(void *ptr, size_t size, size_t nitems,
         UcxBuffer *buffer) {
-    size_t len = size * nitems;
+    size_t len;
+    if(ucx_szmul(size, nitems, &len)) {
+        return 0;
+    }
     if (buffer->pos + len > buffer->size) {
         len = buffer->size - buffer->pos;
         if (size > 1) len -= len%size;
@@ -223,12 +231,67 @@
     if (ucx_buffer_eof(buffer)) {
         return EOF;
     } else {
-        int c = buffer->space[buffer->pos];
+        int c = ((unsigned char*)buffer->space)[buffer->pos];
         buffer->pos++;
         return c;
     }
 }
 
-size_t ucx_buffer_puts(UcxBuffer *buffer, char *str) {
+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) {
+    if (shift >= buffer->size) {
+        buffer->pos = buffer->size = 0;
+    } else {
+        memmove(buffer->space, buffer->space + shift, buffer->size - shift);
+        buffer->size -= shift;
+        
+        if (buffer->pos >= shift) {
+            buffer->pos -= shift;
+        } else {
+            buffer->pos = 0;
+        }
+    }
+    return 0;
+}
+
+int ucx_buffer_shift_right(UcxBuffer* 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)) {
+                return 1;
+            }
+            movebytes = buffer->size;
+        } else {
+            movebytes = buffer->capacity - shift;
+        }
+    } else {
+        movebytes = buffer->size;
+    }
+    
+    memmove(buffer->space + shift, buffer->space, 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) {
+    if (shift < 0) {
+        return ucx_buffer_shift_left(buffer, (size_t) (-shift));
+    } else if (shift > 0) {
+        return ucx_buffer_shift_right(buffer, (size_t) shift);
+    } else {
+        return 0;
+    }
+}
--- a/src/ucx/buffer.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,270 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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)
-
-/**
- * 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, char *str);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_BUFFER_H */
-
--- a/src/ucx/list.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/list.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,7 +26,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "list.h"
+#include "ucx/list.h"
 
 UcxList *ucx_list_clone(UcxList *l, copy_func fnc, void *data) {
     return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data);
@@ -77,6 +77,7 @@
 }
 
 void ucx_list_free_content(UcxList* list, ucx_destructor destr) {
+    if (!destr) destr = free;
     while (list != NULL) {
         destr(list->data);
         list = list->next;
@@ -205,15 +206,15 @@
     return s;
 }
 
-static UcxList *ucx_list_sort_merge(int length,
-        UcxList* restrict ls, UcxList* restrict le, UcxList* restrict re,
+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;
-    int n = 0;
+    size_t n = 0;
     while (lc && lc != le && rc != re) {
         if (fnc(lc->data, rc->data, data) <= 0) {
             sorted[n] = lc;
@@ -254,9 +255,9 @@
     }
 
     UcxList *lc;
-    int ln = 1;
+    size_t ln = 1;
 
-    UcxList *restrict ls = l, *restrict le, *restrict re;
+    UcxList *ls = l, *le, *re;
     
     // check how many elements are already sorted
     lc = ls;
@@ -270,7 +271,7 @@
         return l; // this list is already sorted :)
     } else {
         UcxList *rc;
-        int rn = 1;
+        size_t rn = 1;
         rc = le;
         // skip already sorted elements
         while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) {
--- a/src/ucx/list.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,393 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above copyright
- *      notice, this list of conditions and the following disclaimer in the
- *      documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/**
- * Doubly linked list implementation.
- * 
- * @file   list.h
- * @author Mike Becker
- * @author Olaf Wintermann
- */
-
-#ifndef UCX_LIST_H
-#define	UCX_LIST_H
-
-#include "ucx.h"
-#include "allocator.h"
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * Loop statement for UCX lists.
- * 
- * The first argument is the name of the iteration variable. The scope of
- * this variable is limited to the <code>UCX_FOREACH</code> statement.
- * 
- * The second argument is a pointer to the list. In most cases this will be the
- * pointer to the first element of the list, but it may also be an arbitrary
- * element of the list. The iteration will then start with that element.
- * 
- * @param list The first element of the list
- * @param elem The variable name of the element
- */
-#define UCX_FOREACH(elem,list) \
-        for (UcxList* elem = list ; elem != NULL ; elem = elem->next)
-
-/**
- * UCX list type.
- * @see UcxList
- */
-typedef struct UcxList UcxList;
-
-/**
- * UCX list structure.
- */
-struct UcxList {
-    /**
-     * List element payload.
-     */
-    void    *data;
-    /**
-     * Pointer to the next list element or <code>NULL</code>, if this is the
-     * last element.
-     */
-    UcxList *next;
-    /**
-     * Pointer to the previous list element or <code>NULL</code>, if this is
-     * the first element.
-     */
-    UcxList *prev;
-};
-
-/**
- * Creates an element-wise copy of a list.
- * 
- * This function clones the specified list by creating new list elements and
- * copying the data with the specified copy_func(). If no copy_func() is
- * specified, a shallow copy is created and the new list will reference the
- * same data as the source list.
- * 
- * @param list the list to copy
- * @param cpyfnc a pointer to the function that shall copy an element (may be
- * <code>NULL</code>)
- * @param data additional data for the copy_func()
- * @return a pointer to the copy
- */
-UcxList *ucx_list_clone(UcxList *list, copy_func cpyfnc, void* data);
-
-/**
- * Creates an element-wise copy of a list using a UcxAllocator.
- * 
- * See ucx_list_clone() for details.
- * 
- * You might want to pass the allocator via the <code>data</code> parameter,
- * to access it within the copy function for making deep copies.
- * 
- * @param allocator the allocator to use
- * @param list the list to copy
- * @param cpyfnc a pointer to the function that shall copy an element (may be
- * <code>NULL</code>)
- * @param data additional data for the copy_func()
- * @return a pointer to the copy
- * @see ucx_list_clone()
- */
-UcxList *ucx_list_clone_a(UcxAllocator *allocator, UcxList *list,
-        copy_func cpyfnc, void* data);
-
-/**
- * Compares two UCX lists element-wise by using a compare function.
- * 
- * Each element of the two specified lists are compared by using the specified
- * compare function and the additional data. The type and content of this
- * additional data depends on the cmp_func() used.
- * 
- * If the list pointers denote elements within a list, the lists are compared
- * starting with the denoted elements. Thus any previous elements are not taken
- * into account. This might be useful to check, if certain list tails match
- * each other.
- * 
- * @param list1 the first list
- * @param list2 the second list
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return 1, if and only if the two lists equal element-wise, 0 otherwise
- */
-int ucx_list_equals(const UcxList *list1, const UcxList *list2,
-        cmp_func cmpfnc, void* data);
-
-/**
- * Destroys the entire list.
- * 
- * The members of the list are not automatically freed, so ensure they are
- * otherwise referenced or destroyed by ucx_list_free_contents().
- * Otherwise, a memory leak is likely to occur.
- * 
- * <b>Caution:</b> the argument <b>MUST</b> denote an entire list (i.e. a call
- * to ucx_list_first() on the argument must return the argument itself)
- * 
- * @param list the list to free
- * @see ucx_list_free_contents()
- */
-void ucx_list_free(UcxList *list);
-
-/**
- * Destroys the entire list using a UcxAllocator.
- * 
- * See ucx_list_free() for details.
- * 
- * @param allocator the allocator to use
- * @param list the list to free
- * @see ucx_list_free()
- */
-void ucx_list_free_a(UcxAllocator *allocator, UcxList *list);
-
-/**
- * Destroys the contents of the specified list by calling the specified
- * destructor on each of them.
- * 
- * Note, that the contents are not usable afterwards and the list should be
- * destroyed with ucx_list_free().
- * 
- * @param list the list for which the contents shall be freed
- * @param destr the destructor function (e.g. stdlib free())
- * @see ucx_list_free()
- */
-void ucx_list_free_content(UcxList* list, ucx_destructor destr);
-
-
-/**
- * Inserts an element at the end of the list.
- * 
- * This is generally an O(n) operation, as the end of the list is retrieved with
- * ucx_list_last().
- * 
- * @param list the list where to append the data, or <code>NULL</code> to
- * create a new list
- * @param data the data to insert
- * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
- * the newly created list otherwise
- */
-UcxList *ucx_list_append(UcxList *list, void *data);
-
-/**
- * Inserts an element at the end of the list using a UcxAllocator.
- * 
- * See ucx_list_append() for details.
- * 
- * @param allocator the allocator to use
- * @param list the list where to append the data, or <code>NULL</code> to
- * create a new list
- * @param data the data to insert
- * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
- * the newly created list otherwise
- * @see ucx_list_append()
- */
-UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data);
-
-/**
- * Inserts an element at the beginning of the list.
- * 
- * You <i>should</i> overwrite the old list pointer by calling
- * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. However, you may
- * also perform successive calls of ucx_list_prepend() on the same list pointer,
- * as this function always searchs for the head of the list with
- * ucx_list_first().
- * 
- * @param list the list where to insert the data or <code>NULL</code> to create
- * a new list
- * @param data the data to insert
- * @return a pointer to the new list head
- */
-UcxList *ucx_list_prepend(UcxList *list, void *data);
-
-/**
- * Inserts an element at the beginning of the list using a UcxAllocator.
- * 
- * See ucx_list_prepend() for details.
- * 
- * @param allocator the allocator to use
- * @param list the list where to insert the data or <code>NULL</code> to create
- * a new list
- * @param data the data to insert
- * @return a pointer to the new list head
- * @see ucx_list_prepend()
- */
-UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data);
-
-/**
- * Concatenates two lists.
- * 
- * Either of the two arguments may be <code>NULL</code>.
- * 
- * This function modifies the references to the next/previous element of
- * the last/first element of <code>list1</code>/<code>
- * list2</code>.
- * 
- * @param list1 first list
- * @param list2 second list
- * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is
- * returned, otherwise <code>list1</code> is returned
- */
-UcxList *ucx_list_concat(UcxList *list1, UcxList *list2);
-
-/**
- * Returns the first element of a list.
- * 
- * If the argument is the list pointer, it is directly returned. Otherwise
- * this function traverses to the first element of the list and returns the
- * list pointer.
- * 
- * @param elem one element of the list
- * @return the first element of the list, the specified element is a member of
- */
-UcxList *ucx_list_first(const UcxList *elem);
-
-/**
- * Returns the last element of a list.
- * 
- * If the argument has no successor, it is the last element and therefore
- * directly returned. Otherwise this function traverses to the last element of
- * the list and returns it.
- * 
- * @param elem one element of the list
- * @return the last element of the list, the specified element is a member of
- */
-UcxList *ucx_list_last(const UcxList *elem);
-
-/**
- * Returns the list element at the specified index.
- * 
- * @param list the list to retrieve the element from
- * @param index index of the element to return
- * @return the element at the specified index or <code>NULL</code>, if the
- * index is greater than the list size
- */
-UcxList *ucx_list_get(const UcxList *list, size_t index);
-
-/**
- * Returns the index of an element.
- * 
- * @param list the list where to search for the element
- * @param elem the element to find
- * @return the index of the element or -1 if the list does not contain the
- * element
- */
-ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem);
-
-/**
- * Returns the element count of the list.
- * 
- * @param list the list whose elements are counted
- * @return the element count
- */
-size_t ucx_list_size(const UcxList *list);
-
-/**
- * Returns the index of an element containing the specified data.
- *
- * This function uses a cmp_func() to compare the data of each list element
- * with the specified data. If no cmp_func is provided, the pointers are
- * compared.
- * 
- * If the list contains the data more than once, the index of the first
- * occurrence is returned.
- *  
- * @param list the list where to search for the data
- * @param elem the element data
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return the index of the element containing the specified data or -1 if the
- * data is not found in this list
- */
-ssize_t ucx_list_find(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
-
-/**
- * Checks, if a list contains a specific element.
- * 
- * An element is found, if ucx_list_find() returns a value greater than -1.
- * 
- * @param list the list where to search for the data
- * @param elem the element data
- * @param cmpfnc the compare function
- * @param data additional data for the compare function
- * @return 1, if and only if the list contains the specified element data
- * @see ucx_list_find()
- */
-int ucx_list_contains(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
-
-/**
- * Sorts a UcxList with natural merge sort.
- * 
- * This function uses O(n) additional temporary memory for merge operations
- * that is automatically freed after each merge.
- * 
- * As the head of the list might change, you <b>MUST</b> call this function
- * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>.
- * 
- * @param list the list to sort
- * @param cmpfnc the function that shall be used to compare the element data
- * @param data additional data for the cmp_func()
- * @return the sorted list
- */
-UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data);
-
-/**
- * Removes an element from the list.
- * 
- * If the first element is removed, the list pointer changes. So it is
- * <i>highly recommended</i> to <i>always</i> update the pointer by calling
- * <code>mylist = ucx_list_remove(mylist, myelem);</code>.
- * 
- * @param list the list from which the element shall be removed
- * @param element the element to remove
- * @return returns the updated list pointer or <code>NULL</code>, if the list
- * is now empty
- */
-UcxList *ucx_list_remove(UcxList *list, UcxList *element);
-
-/**
- * Removes an element from the list using a UcxAllocator.
- * 
- * See ucx_list_remove() for details.
- * 
- * @param allocator the allocator to use
- * @param list the list from which the element shall be removed
- * @param element the element to remove
- * @return returns the updated list pointer or <code>NULL</code>, if the list
- * @see ucx_list_remove()
- */
-UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list,
-        UcxList *element);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_LIST_H */
-
--- a/src/ucx/logging.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/logging.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,7 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "logging.h"
+#include "ucx/logging.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
@@ -49,6 +50,8 @@
         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]");
     }
@@ -68,12 +71,15 @@
         const unsigned int line, const char *format, ...) {
     if (level <= logger->level) {
         char msg[UCX_LOGGER_MSGMAX];
-        char *text;
+        const char *text;
         size_t k = 0;
         size_t n;
         
         if ((logger->mask & UCX_LOGGER_LEVEL) > 0) {
-            text = (char*) ucx_map_int_get(logger->levels, level);
+            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);
@@ -91,7 +97,9 @@
             k += sprintf(msg+k, ":%u ", line);
         }
         
-        msg[k++] = '-'; msg[k++] = ' ';
+        if (k > 0) {
+            msg[k++] = '-'; msg[k++] = ' ';
+        }
         
         va_list args;
         va_start (args, format);
--- a/src/ucx/logging.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,238 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- *   1. Redistributions of source code must retain the above copyright
- *      notice, this list of conditions and the following disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above copyright
- *      notice, this list of conditions and the following disclaimer in the
- *      documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-/**
- * Logging API.
- * 
- * @file   logging.h
- * @author Mike Becker, Olaf Wintermann
- */
-#ifndef UCX_LOGGING_H
-#define UCX_LOGGING_H
-
-#include "ucx.h"
-#include "map.h"
-#include "string.h"
-#include <stdio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* leave enough space for custom log levels */
-
-/** Log level for error messages. */
-#define UCX_LOGGER_ERROR        0x00
-    
-/** Log level for warning messages. */
-#define UCX_LOGGER_WARN         0x10
-
-/** Log level for information messages. */
-#define UCX_LOGGER_INFO         0x20
-
-/** Log level for debug messages. */
-#define UCX_LOGGER_DEBUG        0x30
-
-/** Log level for trace messages. */
-#define UCX_LOGGER_TRACE        0x40
-
-/**
- * Output flag for the log level. 
- * If this flag is set, the log message will contain the log level.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_LEVEL        0x01
-
-/**
- * Output flag for the timestmap.
- * If this flag is set, the log message will contain the timestmap.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_TIMESTAMP    0x02
-
-/**
- * Output flag for the source.
- * If this flag is set, the log message will contain the source file and line
- * number.
- * @see UcxLogger.mask
- */
-#define UCX_LOGGER_SOURCE       0x04
-
-/**
- * The UCX Logger object.
- */
-typedef struct {
-    /** The stream this logger writes its messages to.*/
-    void *stream;
-
-    /**
-     * The write function that shall be used.
-     * For standard file or stdout loggers this might be standard fwrite
-     * (default).
-     */
-    write_func writer;
-
-    /**
-     * The date format for timestamp outputs including the delimiter
-     * (default: <code>"%F %T %z "</code>).
-     * @see UCX_LOGGER_TIMESTAMP
-     */
-    char *dateformat;
-
-    /**
-     * The level, this logger operates on.
-     * If a log command is issued, the message will only be logged, if the log
-     * level of the message is less or equal than the log level of the logger.
-     */
-    unsigned int level;
-
-    /**
-     * A configuration mask for automatic output. 
-     * For each flag that is set, the logger automatically outputs some extra
-     * information like the timestamp or the source file and line number.
-     * See the documentation for the flags for details.
-     */
-    unsigned int mask;
-
-    /**
-     * A map of valid log levels for this logger.
-     * 
-     * The keys represent all valid log levels and the values provide string
-     * representations, that are used, if the UCX_LOGGER_LEVEL flag is set.
-     * 
-     * The exact data types are <code>unsigned int</code> for the key and
-     * <code>const char*</code> for the value.
-     * 
-     * @see UCX_LOGGER_LEVEL
-     */
-    UcxMap* levels;
-} UcxLogger;
-
-/**
- * Creates a new logger.
- * @param stream the stream, which the logger shall write to
- * @param level the level on which the logger shall operate
- * @param mask configuration mask (cf. UcxLogger.mask)
- * @return a new logger object
- */
-UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask);
-
-/**
- * Destroys the logger.
- * 
- * The map containing the valid log levels is also automatically destroyed.
- * 
- * @param logger the logger to destroy
- */
-void ucx_logger_free(UcxLogger* logger);
-
-/**
- * Internal log function - use macros instead.
- * 
- * This function uses the <code>format</code> and variadic arguments for a
- * printf()-style output of the log message.
- * 
- * Dependent on the UcxLogger.mask some information is prepended. The complete
- * format is:
- * 
- * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code>
- * 
- * <b>Attention:</b> the message (including automatically generated information)
- * is limited to 4096 characters. The level description is limited to
- * 256 characters and the timestamp string is limited to 128 characters.
- * 
- * @param logger the logger to use
- * @param level the level to log on
- * @param file information about the source file
- * @param line information about the source line number
- * @param format format string
- * @param ... arguments
- * @see ucx_logger_log()
- */
-void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file,
-        const unsigned int line, const char* format, ...);
-
-/**
- * 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/map.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/map.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,11 +26,11 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "ucx/map.h"
+
 #include <stdlib.h>
 #include <string.h>
 
-#include "map.h"
-
 UcxMap *ucx_map_new(size_t size) {
     return ucx_map_new_a(NULL, size);
 }
@@ -86,7 +86,11 @@
     UcxMapIterator iter = ucx_map_iterator(map);
     void *val;
     UCX_MAP_FOREACH(key, val, iter) {
-        destr(val);
+        if (destr) {
+            destr(val);
+        } else {
+            alfree(map->allocator, val);
+        }
     }
 }
 
@@ -99,8 +103,7 @@
     map->count = 0;
 }
 
-int ucx_map_copy(UcxMap *restrict from, UcxMap *restrict to,
-        copy_func fnc, void *data) {
+int ucx_map_copy(UcxMap *from, UcxMap *to, copy_func fnc, void *data) {
     UcxMapIterator i = ucx_map_iterator(from);
     void *value;
     UCX_MAP_FOREACH(key, value, i) {
@@ -151,19 +154,22 @@
     UcxAllocator *allocator = map->allocator;
     
     if (key.hash == 0) {
-        key.hash = ucx_hash((char*)key.data, key.len);
+        key.hash = ucx_hash((const char*)key.data, key.len);
     }
+    
+    struct UcxMapKey mapkey;
+    mapkey.hash = key.hash;
 
-    size_t slot = key.hash%map->size;
-    UcxMapElement *restrict elm = map->map[slot];
-    UcxMapElement *restrict prev = NULL;
+    size_t slot = mapkey.hash%map->size;
+    UcxMapElement *elm = map->map[slot];
+    UcxMapElement *prev = NULL;
 
-    while (elm && elm->key.hash < key.hash) {
+    while (elm && elm->key.hash < mapkey.hash) {
         prev = elm;
         elm = elm->next;
     }
     
-    if (!elm || elm->key.hash != key.hash) {
+    if (!elm || elm->key.hash != mapkey.hash) {
         UcxMapElement *e = (UcxMapElement*)almalloc(
                 allocator, sizeof(UcxMapElement));
         if (!e) {
@@ -185,8 +191,9 @@
             return -1;
         }
         memcpy(kd, key.data, key.len);
-        key.data = kd;
-        elm->key = key;
+        mapkey.data = kd;
+        mapkey.len = key.len;
+        elm->key = mapkey;
         map->count++;
     }
     elm->data = data;
@@ -194,14 +201,14 @@
     return 0;
 }
 
-void* ucx_map_get_and_remove(UcxMap *map, UcxKey key, _Bool remove) {
+static void* ucx_map_get_and_remove(UcxMap *map, UcxKey key, int remove) {
     if(key.hash == 0) {
-        key.hash = ucx_hash((char*)key.data, key.len);
+        key.hash = ucx_hash((const char*)key.data, key.len);
     }
     
     size_t slot = key.hash%map->size;
-    UcxMapElement *restrict elm = map->map[slot];
-    UcxMapElement *restrict pelm = NULL;
+    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;
@@ -236,11 +243,11 @@
     return ucx_map_get_and_remove(map, key, 1);
 }
 
-UcxKey ucx_key(void *data, size_t len) {
+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);
+    key.hash = ucx_hash((const char*)data, len);
     return key;
 }
 
@@ -309,7 +316,9 @@
             if (e->data) {
                 i->cur = e;
                 *elm = e->data;
-                *key = e->key;
+                key->data = e->key.data;
+                key->hash = e->key.hash;
+                key->len = e->key.len;
                 return 1;
             }
 
--- a/src/ucx/map.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,423 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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 for a key of a UcxMap. */
-struct UcxKey {
-    /** 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. */
-    UcxKey        key;
-};
-
-/** Structure for an iterator over a UcxMap. */
-struct UcxMapIterator {
-    /** The map to iterate over. */
-    UcxMap        *map;
-    
-    /** The current map element. */
-    UcxMapElement *cur;
-    
-    /**
-     * The current index of the element list array.
-     * <b>Attention: </b> this is <b>NOT</b> the element index! Do <b>NOT</b>
-     * manually iterate over the map by increasing this index. Use
-     * ucx_map_iter_next().
-     * @see UcxMap.map*/
-    size_t        index;
-};
-
-/**
- * Creates a new hash map with the specified size.
- * @param size the size of the hash map
- * @return a pointer to the new hash map
- */
-UcxMap *ucx_map_new(size_t size);
-
-/**
- * Creates a new hash map with the specified size using a UcxAllocator.
- * @param allocator the allocator to use
- * @param size the size of the hash map
- * @return a pointer to the new hash map
- */
-UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size);
-
-/**
- * Frees a hash map.
- * 
- * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
- * before calling this function to achieve that.
- * 
- * @param map the map to be freed
- * @see ucx_map_free_content()
- */
-void ucx_map_free(UcxMap *map);
-
-/**
- * Frees the contents of a hash map.
- * 
- * This is a convenience function that iterates over the map and passes all
- * values to the specified destructor function (e.g. stdlib free()).
- * 
- * 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 pointer to the 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 *restrict from, UcxMap *restrict to,
-        copy_func fnc, void *data);
-
-/**
- * Clones the map and rehashes if necessary.
- * 
- * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant.
- * This function <i>always</i> ensures a new UcxMap.size of at least
- * 2.5*UcxMap.count.
- * 
- * @param map the map to clone
- * @param fnc the copy function to use or <code>NULL</code> if the new and
- * the old map shall share the data pointers
- * @param data additional data for the copy function
- * @return the cloned map
- * @see ucx_map_copy()
- */
-UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data);
-
-/**
- * Increases size of the hash map, if necessary.
- * 
- * The load value is 0.75*UcxMap.size. If the element count exceeds the load
- * value, the map needs to be rehashed. Otherwise no action is performed and
- * this function simply returns 0.
- * 
- * The rehashing process ensures, that the UcxMap.size is at least
- * 2.5*UcxMap.count. So there is enough room for additional elements without
- * the need of another soon rehashing.
- * 
- * You can use this function to dramatically increase access performance.
- * 
- * @param map the map to rehash
- * @return 1, if a memory allocation error occurred, 0 otherwise
- */
-int ucx_map_rehash(UcxMap *map);
-
-/**
- * Puts a key/value-pair into the map.
- * 
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- */
-int ucx_map_put(UcxMap *map, UcxKey key, void *value);
-
-/**
- * Retrieves a value by using a key.
- * 
- * @param map the map
- * @param key the key
- * @return the value
- */
-void* ucx_map_get(UcxMap *map, UcxKey key);
-
-/**
- * Removes a key/value-pair from the map by using the key.
- * 
- * @param map the map
- * @param key the key
- * @return the removed value
- */
-void* ucx_map_remove(UcxMap *map, UcxKey key);
-
-/**
- * Shorthand for putting data with a sstr_t key into the map.
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- * @see ucx_map_put()
- */
-#define ucx_map_sstr_put(map, key, value) \
-    ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value)
-
-/**
- * Shorthand for putting data with a C string key into the map.
- * @param map the map
- * @param key the key
- * @param value the value
- * @return 0 on success, non-zero value on failure
- * @see ucx_map_put()
- */
-#define ucx_map_cstr_put(map, key, value) \
-    ucx_map_put(map, ucx_key((void*)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((void*)&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((void*)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((void*)&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((void*)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((void*)&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(void *data, size_t len);
-
-/**
- * Computes a murmur hash-2.
- * 
- * @param data the data to hash
- * @param len the length of the data
- * @return the murmur hash-2 of the data
- */
-int ucx_hash(const char *data, size_t len);
-
-/**
- * Creates an iterator for a map.
- * 
- * <b>Note:</b> A UcxMapIterator iterates over all elements in all element
- * lists successively. Therefore the order highly depends on the key hashes and
- * may vary under different map sizes. So generally you may <b>NOT</b> rely on
- * the iteration order.
- * 
- * <b>Note:</b> The iterator is <b>NOT</b> initialized. You need to call
- * ucx_map_iter_next() at least once before accessing any information. However,
- * it is not recommended to access the fields of a UcxMapIterator directly.
- * 
- * @param map the map to create the iterator for
- * @return an iterator initialized on the first element of the
- * first element list
- * @see ucx_map_iter_next()
- */
-UcxMapIterator ucx_map_iterator(UcxMap *map);
-
-/**
- * Proceeds to the next element of the map (if any).
- * 
- * Subsequent calls on the same iterator proceed to the next element and
- * store the key/value-pair into the memory specified as arguments of this
- * function.
- * 
- * If no further elements are found, this function returns zero and leaves the
- * last found key/value-pair in memory.
- * 
- * @param iterator the iterator to use
- * @param key a pointer to the memory where to store the key
- * @param value a pointer to the memory where to store the value
- * @return 1, if another element was found, 0 if all elements has been processed
- * @see ucx_map_iterator()
- */
-int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value);
-
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_MAP_H */
-
--- a/src/ucx/mempool.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/mempool.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,6 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "ucx/mempool.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -34,8 +36,6 @@
 #endif
 #include <inttypes.h>
 
-#include "mempool.h"
-
 /** Capsule for destructible memory chunks. */
 typedef struct {
     /** The destructor for the memory chunk. */
@@ -56,18 +56,26 @@
     void           *ptr;
 } ucx_regdestr;
 
-UCX_EXTERN void ucx_mempool_shared_destr(void* ptr) {
+#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(n * sizeof(void*));
+    pool->data = (void**) malloc(poolsz);
     if (pool->data == NULL) {
         free(pool);
         return NULL;
@@ -93,25 +101,39 @@
 }
 
 int ucx_mempool_chcap(UcxMempool *pool, size_t newcap) {
-    void **data = (void**) realloc(pool->data, newcap*sizeof(void*));
+    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 EXIT_SUCCESS;
+        return 0;
     } else {
-        return EXIT_FAILURE;
+        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) {
-        // The hard coded 16 is documented for this function and ucx_mempool_new
-        if (ucx_mempool_chcap(pool, pool->size + 16) == EXIT_FAILURE) {
+        size_t newcap = pool->size*2;
+        if (newcap < pool->size || ucx_mempool_chcap(pool, newcap)) {
             return NULL;
         }
     }
 
-    ucx_memchunk *mem = (ucx_memchunk*)malloc(sizeof(ucx_destructor) + n);
+    void *p = malloc(sizeof(ucx_destructor) + n);
+    ucx_memchunk *mem = (ucx_memchunk*)p;
     if (!mem) {
         return NULL;
     }
@@ -124,7 +146,12 @@
 }
 
 void *ucx_mempool_calloc(UcxMempool *pool, size_t nelem, size_t elsize) {
-    void *ptr = ucx_mempool_malloc(pool, nelem*elsize);
+    size_t msz;
+    if(ucx_szmul(nelem, elsize, &msz)) {
+        return NULL;
+    }
+    
+    void *ptr = ucx_mempool_malloc(pool, msz);
     if (!ptr) {
         return NULL;
     }
@@ -133,6 +160,10 @@
 }
 
 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) {
@@ -147,7 +178,7 @@
         }
         fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
           (intptr_t)ptr, (intptr_t)pool);
-        exit(EXIT_FAILURE);
+        abort();
     } else {
         return newm + sizeof(ucx_destructor);
     }
@@ -172,7 +203,7 @@
     }
     fprintf(stderr, "FATAL: 0x%08" PRIxPTR" not in mpool 0x%08" PRIxPTR"\n",
             (intptr_t)ptr, (intptr_t)pool);
-    exit(EXIT_FAILURE);
+    abort();
 }
 
 void ucx_mempool_destroy(UcxMempool *pool) {
--- a/src/ucx/mempool.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,210 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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 <stddef.h>
-#include "allocator.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 16 elements, when
- * trying to allocate memory on a full pool, it is recommended that you use
- * a multiple of 16 for the initial size.
- * 
- * @param n initial pool size (should be a multiple of 16)
- * @return a pointer to the new memory pool
- */
-UcxMempool *ucx_mempool_new(size_t n);
-
-/**
- * Resizes a memory pool.
- * 
- * @param pool the pool to resize
- * @param newcap the new capacity
- * @return <code>EXIT_SUCCESS</code> on success or
- * <code>EXIT_FAILURE</code> on failure
- */
-int ucx_mempool_chcap(UcxMempool *pool, size_t newcap);
-
-/**
- * Changes the pool size to the next smallest multiple of 16.
- * 
- * You may use this macro, to reduce the pool size after freeing
- * many pooled memory items.
- * 
- * @param pool the pool to clamp
- * @return <code>EXIT_SUCCESS</code> on success or
- * <code>EXIT_FAILURE</code> on failure
- */
-#define ucx_mempool_clamp(pool) ucx_mempool_chcap(pool, \
-        (pool->ndata & ~0xF)+0x10)
-
-/**
- * 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
- * behavior is undefined.
- * 
- * @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.
- * 
- * 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.
- * 
- * @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/properties.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/properties.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,12 +26,12 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "ucx/properties.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include "properties.h"
-
 UcxProperties *ucx_properties_new() {
     UcxProperties *parser = (UcxProperties*)malloc(
             sizeof(UcxProperties));
--- a/src/ucx/properties.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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. 
- * 
- * @param prop the UcxProperties object
- * @param map the target map
- * @return The UcxProperties.error code (i.e. 0 on success).
- * @see ucx_properties_fill()
- */
-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/stack.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/stack.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,7 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "stack.h"
+#include "ucx/stack.h"
+
 #include <string.h>
 
 static size_t ucx_stack_align(size_t n) {
@@ -119,13 +120,15 @@
         return;
     }
     
-    size_t len = ucx_stack_topsize(stack);
-    if (len > n) {
-        len = n;
+    if (dest) {
+        size_t len = ucx_stack_topsize(stack);
+        if (len > n) {
+            len = n;
+        }
+
+        memcpy(dest, stack->top, len);
     }
     
-    memcpy(dest, stack->top, len);
-    
     ucx_stack_free(stack, stack->top);
 }
 
@@ -141,3 +144,22 @@
         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/stack.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,232 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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
- * @see ucx_allocator_malloc()
- */
-void *ucx_stack_malloc(UcxStack *stack, size_t n);
-
-/**
- * Alias for #ucx_stack_malloc().
- * @param stack a pointer to the stack
- * @param n amount of memory to allocate
- * @return a pointer to the allocated memory
- * @see ucx_stack_malloc
- */
-#define ucx_stack_push(stack, n) ucx_stack_malloc(stack, n)
-
-/**
- * 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);
-
-/**
- * Alias for #ucx_stack_calloc().
- * 
- * @param stack a pointer to the stack
- * @param n amount of elements to allocate
- * @param elsize amount of memory per element
- * @return a pointer to the allocated memory
- * @see ucx_stack_calloc
- */
-#define ucx_stack_pusharr(stack,n,elsize) ucx_stack_calloc(stack,n,elssize)
-
-/**
- * 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>.
- * 
- * In contrast to #ucx_stack_pop() the <code>dest</code> pointer <code>MUST
- * NOT</code> be <code>NULL</code>.
- * 
- * @param stack a pointer to the stack
- * @param dest the location where the contents shall be written to
- * @param n copies at most n elements 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/string.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/string.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,13 +26,19 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include "ucx/string.h"
+
+#include "ucx/allocator.h"
+
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <ctype.h>
 
-#include "string.h"
-#include "allocator.h"
+#ifndef _WIN32
+#include <strings.h> /* for strncasecmp() */
+#endif /* _WIN32 */
 
 sstr_t sstr(char *cstring) {
     sstr_t string;
@@ -48,13 +54,35 @@
     return string;
 }
 
-size_t sstrnlen(size_t n, sstr_t s, ...) {
+scstr_t scstr(const char *cstring) {
+    scstr_t string;
+    string.ptr = cstring;
+    string.length = strlen(cstring);
+    return string;
+}
+
+scstr_t scstrn(const char *cstring, size_t length) {
+    scstr_t string;
+    string.ptr = cstring;
+    string.length = length;
+    return string;
+}
+
+
+size_t scstrnlen(size_t n, ...) {
+    if (n == 0) return 0;
+    
     va_list ap;
-    size_t size = s.length;
-    va_start(ap, s);
+    va_start(ap, n);
+    
+    size_t size = 0;
 
-    for (size_t i = 1 ; i < n ; i++) {
-        sstr_t str = va_arg(ap, sstr_t);
+    for (size_t i = 0 ; i < n ; i++) {
+        scstr_t str = va_arg(ap, scstr_t);
+        if(SIZE_MAX - str.length < size) {
+            size = SIZE_MAX;
+            break;
+        }
         size += str.length;
     }
     va_end(ap);
@@ -65,8 +93,7 @@
 static sstr_t sstrvcat_a(
         UcxAllocator *a,
         size_t count,
-        sstr_t s1,
-        sstr_t s2,
+        scstr_t s1,
         va_list ap) {
     sstr_t str;
     str.ptr = NULL;
@@ -75,7 +102,13 @@
         return str;
     }
     
-    sstr_t *strings = (sstr_t*) calloc(count, sizeof(sstr_t));
+    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;
     }
@@ -83,16 +116,25 @@
     // get all args and overall length
     strings[0] = s1;
     strings[1] = s2;
-    size_t strlen = s1.length + s2.length;
+    size_t slen = s1.length + s2.length;
+    int error = 0;
     for (size_t i=2;i<count;i++) {
-        sstr_t s = va_arg (ap, sstr_t);
+        scstr_t s = va_arg (ap, scstr_t);
         strings[i] = s;
-        strlen += s.length;
+        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, strlen + 1);
-    str.length = strlen;
+    str.ptr = (char*) almalloc(a, slen + 1);
+    str.length = slen;
     if(!str.ptr) {
         free(strings);
         str.length = 0;
@@ -102,7 +144,7 @@
     // concatenate strings
     size_t pos = 0;
     for (size_t i=0;i<count;i++) {
-        sstr_t s = strings[i];
+        scstr_t s = strings[i];
         memcpy(str.ptr + pos, s.ptr, s.length);
         pos += s.length;
     }
@@ -114,20 +156,42 @@
     return str;
 }
 
-sstr_t sstrcat(size_t count, sstr_t s1, sstr_t s2, ...) {
+sstr_t scstrcat(size_t count, scstr_t s1, ...) {
     va_list ap;
-    va_start(ap, s2);
-    sstr_t s = sstrvcat_a(ucx_default_allocator(), count, s1, s2, ap);
+    va_start(ap, s1);
+    sstr_t s = sstrvcat_a(ucx_default_allocator(), count, s1, ap);
+    va_end(ap);
+    return s;
+}
+
+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;
 }
 
-sstr_t sstrcat_a(UcxAllocator *a, size_t count, sstr_t s1, sstr_t s2, ...) {
-    va_list ap;
-    va_start(ap, s2);
-    sstr_t s = sstrvcat_a(a, count, s1, s2, ap);
-    va_end(ap);
-    return s;
+static int ucx_substring(
+        size_t str_length,
+        size_t start,
+        size_t length,
+        size_t *newlen,
+        size_t *newpos)
+{
+    *newlen = 0;
+    *newpos = 0;
+    
+    if(start > str_length) {
+        return 0;
+    }
+    
+    if(length > str_length - start) {
+        length = str_length - start;
+    }
+    *newlen = length;
+    *newpos = start;
+    return 1;
 }
 
 sstr_t sstrsubs(sstr_t s, size_t start) {
@@ -135,132 +199,301 @@
 }
 
 sstr_t sstrsubsl(sstr_t s, size_t start, size_t length) {
-    sstr_t new_sstr;
-    if (start >= s.length) {
-        new_sstr.ptr = NULL;
-        new_sstr.length = 0;
-    } else {
-        if (length > s.length-start) {
-            length = s.length-start;
+    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;
+}
+
+scstr_t scstrsubs(scstr_t string, size_t start) {
+    return scstrsubsl(string, start, string.length-start);
+}
+
+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;
         }
-        new_sstr.ptr = &s.ptr[start];
-        new_sstr.length = length;
     }
-    return new_sstr;
+    return 0;
+}
+
+static int ucx_strrchr(const char *str, size_t length, int chr, size_t *pos) {
+    if(length > 0) {
+        for(size_t i=length ; i>0 ; i--) {
+            if(str[i-1] == chr) {
+                *pos = i-1;
+                return 1;
+            }
+        }
+    }
+    return 0;
 }
 
 sstr_t sstrchr(sstr_t s, int c) {
-    for(size_t i=0;i<s.length;i++) {
-        if(s.ptr[i] == c) {
-            return sstrsubs(s, i);
-        }
+    size_t pos = 0;
+    if(ucx_strchr(s.ptr, s.length, c, &pos)) {
+        return sstrsubs(s, pos);
     }
-    sstr_t n;
-    n.ptr = NULL;
-    n.length = 0;
-    return n;
+    return sstrn(NULL, 0);
 }
 
 sstr_t sstrrchr(sstr_t s, int c) {
-    if (s.length > 0) {
-        for(size_t i=s.length;i>0;i--) {
-            if(s.ptr[i-1] == c) {
-                return sstrsubs(s, i-1);
-            }
-        }
+    size_t pos = 0;
+    if(ucx_strrchr(s.ptr, s.length, c, &pos)) {
+        return sstrsubs(s, pos);
     }
-    sstr_t n;
-    n.ptr = NULL;
-    n.length = 0;
-    return n;
+    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);
 }
 
-sstr_t sstrstr(sstr_t string, sstr_t match) {
-    if (match.length == 0) {
-        return string;
+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);
+
+
+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;
     }
     
-    for (size_t i = 0 ; i < string.length ; i++) {
-        sstr_t substr = sstrsubs(string, i);
-        if (sstrprefix(substr, match)) {
-            return substr;
+    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
+     */
+    
+    /* static prefix table */
+    static uint8_t s_prefix_table[256];
+    
+    /* check pattern length and use appropriate prefix table */
+    /* if the pattern exceeds static prefix table, allocate on the heap */
+    register int useheap = matchlen > 255;
+    register void* ptable = useheap ?
+        calloc(matchlen+1, sizeof(size_t)): s_prefix_table;
+    
+    /* keep counter in registers */
+    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++; j++;
+        ptable_w(useheap, 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);
+        }
+        i++; j++;
+        if (j-1 == matchlen) {
+            size_t start = i - matchlen;
+            result = str + start;
+            resultlen = length - start;
+            break;
+        }
+    }
+
+    /* if prefix table was allocated on the heap, free it */
+    if (ptable != s_prefix_table) {
+        free(ptable);
     }
     
-    sstr_t emptystr;
-    emptystr.length = 0;
-    emptystr.ptr = NULL;
-    return emptystr;
+    *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;
 }
 
-sstr_t* sstrsplit(sstr_t s, sstr_t d, ssize_t *n) {
-    return sstrsplit_a(ucx_default_allocator(), s, d, n);
+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;
 }
 
-sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t s, sstr_t d, ssize_t *n) {
+#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;
     }
-
-    sstr_t* result;
-    ssize_t nmax = *n;
-    *n = 1;
-
-    /* special case: exact match - no processing needed */
-    if (sstrcmp(s, d) == 0) {
-        *n = 0;
-        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;
+        }
     }
-    sstr_t sv = sstrdup(s);
-    if (sv.length == 0) {
-        *n = -2;
-        return NULL;
-    }
-
-    for (size_t i = 0 ; i < s.length ; i++) {
-        sstr_t substr = sstrsubs(sv, i);
-        if (sstrprefix(substr, d)) {
-            (*n)++;
-            for (size_t j = 0 ; j < d.length ; j++) {
-                sv.ptr[i+j] = 0;
-            }
-            i += d.length - 1; // -1, because the loop will do a i++
-        }
-        if ((*n) == nmax) break;
-    }
-    result = (sstr_t*) almalloc(allocator, sizeof(sstr_t)*(*n));
+    
+    ssize_t nmax = *n;
+    size_t arrlen = 16;
+    sstr_t* result = (sstr_t*) alcalloc(allocator, arrlen, sizeof(sstr_t));
 
     if (result) {
-        char *pptr = sv.ptr;
-        for (ssize_t i = 0 ; i < *n ; i++) {
-            size_t l = strlen(pptr);
-            char* ptr = (char*) almalloc(allocator, l + 1);
-            if (ptr) {
-                memcpy(ptr, pptr, l);
-                ptr[l] = 0;
+        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;
 
-                result[i] = sstrn(ptr, l);
-                pptr += l + d.length;
+                    /* 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 {
-                for (ssize_t j = i-1 ; j >= 0 ; j--) {
-                    alfree(allocator, result[j].ptr);
-                }
-                alfree(allocator, result);
-                *n = -2;
+                /* no more matches, copy last string */
+                result[j-1] = sstrdup_a(allocator, curpos);
                 break;
             }
         }
+        *n = j;
     } else {
         *n = -2;
     }
-    
-    free(sv.ptr);
 
     return result;
 }
 
-int sstrcmp(sstr_t s1, sstr_t s2) {
+int scstrcmp(scstr_t s1, scstr_t s2) {
     if (s1.length == s2.length) {
         return memcmp(s1.ptr, s2.ptr, s1.length);
     } else if (s1.length > s2.length) {
@@ -270,7 +503,7 @@
     }
 }
 
-int sstrcasecmp(sstr_t s1, sstr_t s2) {
+int scstrcasecmp(scstr_t s1, scstr_t s2) {
     if (s1.length == s2.length) {
 #ifdef _WIN32
         return _strnicmp(s1.ptr, s2.ptr, s1.length);
@@ -284,11 +517,11 @@
     }
 }
 
-sstr_t sstrdup(sstr_t s) {
+sstr_t scstrdup(scstr_t s) {
     return sstrdup_a(ucx_default_allocator(), s);
 }
 
-sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t s) {
+sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t s) {
     sstr_t newstring;
     newstring.ptr = (char*)almalloc(allocator, s.length + 1);
     if (newstring.ptr) {
@@ -303,21 +536,38 @@
     return newstring;
 }
 
-sstr_t sstrtrim(sstr_t string) {
-    sstr_t newstr = string;
+
+static size_t ucx_strtrim(const char *s, size_t len, size_t *newlen) {
+    const char *newptr = s;
+    size_t length = len;
     
-    while (newstr.length > 0 && isspace(*newstr.ptr)) {
-        newstr.ptr++;
-        newstr.length--;
+    while(length > 0 && isspace(*newptr)) {
+        newptr++;
+        length--;
     }
-    while (newstr.length > 0 && isspace(newstr.ptr[newstr.length-1])) {
-        newstr.length--;
+    while(length > 0 && isspace(newptr[length-1])) {
+        length--;
     }
     
+    *newlen = length;
+    return newptr - s;
+}
+
+sstr_t sstrtrim(sstr_t string) {
+    sstr_t newstr;
+    newstr.ptr = string.ptr
+                 + ucx_strtrim(string.ptr, string.length, &newstr.length);
     return newstr;
 }
 
-int sstrprefix(sstr_t string, sstr_t prefix) {
+scstr_t scstrtrim(scstr_t string) {
+    scstr_t newstr;
+    newstr.ptr = string.ptr
+                 + ucx_strtrim(string.ptr, string.length, &newstr.length);
+    return newstr;
+}
+
+int scstrprefix(scstr_t string, scstr_t prefix) {
     if (string.length == 0) {
         return prefix.length == 0;
     }
@@ -332,7 +582,7 @@
     }
 }
 
-int sstrsuffix(sstr_t string, sstr_t suffix) {
+int scstrsuffix(scstr_t string, scstr_t suffix) {
     if (string.length == 0) {
         return suffix.length == 0;
     }
@@ -348,7 +598,39 @@
     }
 }
 
-sstr_t sstrlower(sstr_t string) {
+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;
+    }
+    
+    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]);
@@ -356,7 +638,7 @@
     return ret;
 }
 
-sstr_t sstrlower_a(UcxAllocator *allocator, sstr_t string) {
+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]);
@@ -364,7 +646,7 @@
     return ret;
 }
 
-sstr_t sstrupper(sstr_t string) {
+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]);
@@ -372,10 +654,24 @@
     return ret;
 }
 
-sstr_t sstrupper_a(UcxAllocator *allocator, sstr_t string) {
+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;
 }
+
+// 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/string.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,457 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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>
-
-/** Shortcut for a <code>sstr_t struct</code> literal. */
-#define ST(s) { (char*)s, sizeof(s)-1 }
-
-/** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */
-#define S(s) sstrn((char*)s, sizeof(s)-1)
-
-#ifdef	__cplusplus
-extern "C" {
-#endif
-
-/**
- * The UCX string structure.
- */
-typedef struct {
-   /** A reference to the string (<b>not necessarily  <code>NULL</code>
-    * -terminated</b>) */
-    char   *ptr;
-    /** The length of the string */
-    size_t length;
-} sstr_t;
-
-/**
- * Creates a new sstr_t based on a C string.
- * 
- * The length is implicitly inferred by using a call to <code>strlen()</code>.
- *
- * <b>Note:</b> the sstr_t will hold a <i>reference</i> to the C string. If you
- * do want a copy, use sstrdup() on the return value of this function.
- * 
- * @param cstring the C string to wrap
- * @return a new sstr_t containing the C string
- * 
- * @see sstrn()
- */
-sstr_t sstr(char *cstring);
-
-/**
- * Creates a new sstr_t of the specified length based on a C string.
- *
- * <b>Note:</b> the sstr_t will hold a <i>reference</i> to the C string. If you
- * do want a copy, use sstrdup() on the return value of this function.
- * 
- * @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);
-
-
-/**
- * Returns the cumulated length of all specified strings.
- *
- * At least one string must be specified.
- * 
- * <b>Attention:</b> if the count argument does not match the count of the
- * specified strings, the behavior is undefined.
- *
- * @param count    the total number of specified strings (so at least 1)
- * @param string   the first string
- * @param ...      all other strings
- * @return the cumulated length of all strings
- */
-size_t sstrnlen(size_t count, sstr_t string, ...);
-
-/**
- * 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 s2      second string
- * @param ...     all remaining strings
- * @return the concatenated string
- */
-sstr_t sstrcat(size_t count, sstr_t s1, sstr_t s2, ...);
-
-/**
- * Concatenates two or more strings using a UcxAllocator.
- * 
- * See sstrcat() for details.
- *
- * @param a       the allocator to use
- * @param count   the total number of strings to concatenate
- * @param s1      first string
- * @param s2      second string
- * @param ...     all remaining strings
- * @return the concatenated string
- */
-sstr_t sstrcat_a(UcxAllocator *a, size_t count, sstr_t s1, sstr_t s2, ...);
-
-
-/**
- * Returns a substring starting at the specified location.
- * 
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and will <b>NOT</b> be <code>NULL</code>-terminated.
- * Use sstrdup() to get a copy.
- * 
- * @param string input string
- * @param start  start location of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- * 
- * @see sstrsubsl()
- * @see sstrchr()
- */
-sstr_t sstrsubs(sstr_t string, size_t start);
-
-/**
- * Returns a substring with a maximum length starting at the specified location.
- * 
- * <b>Attention:</b> the new string references the same memory area as the
- * input string and will <b>NOT</b> be <code>NULL</code>-terminated.
- * Use sstrdup() to get a copy.
- * 
- * @param string input string
- * @param start  start location of the substring
- * @param length the maximum length of the substring
- * @return a substring of <code>string</code> starting at <code>start</code>
- * with a maximum length of <code>length</code>
- * 
- * @see sstrsubs()
- * @see sstrchr()
- */
-sstr_t sstrsubsl(sstr_t string, size_t start, size_t length);
-
-/**
- * Returns a substring 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 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 sstrstr(sstr_t string, sstr_t 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.
- * 
- * <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()
- */
-sstr_t* sstrsplit(sstr_t string, sstr_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.
- * 
- * <b>Note:</b> the allocator is not used for memory that is freed within the
- * same call of this function (locally scoped variables).
- * 
- * @param allocator the UcxAllocator used for allocating memory
- * @param string the string to split
- * @param delim  the delimiter string
- * @param count  IN: the maximum size of the resulting array (0 = no limit),
- *               OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- *         <code>NULL</code> on error
- * 
- * @see sstrsplit()
- */
-sstr_t* sstrsplit_a(UcxAllocator *allocator, sstr_t string, sstr_t delim,
-        ssize_t *count);
-
-/**
- * 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)
- */
-int sstrcmp(sstr_t s1, sstr_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 difference between the
- * first two differing characters otherwise (i.e. 0 if the strings match and
- * no characters differ)
- */
-int sstrcasecmp(sstr_t s1, sstr_t 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.
- * 
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see sstrdup_a()
- */
-sstr_t sstrdup(sstr_t string);
-
-/**
- * Creates a duplicate of the specified string using a UcxAllocator.
- * 
- * The new sstr_t will contain a copy allocated by the allocators
- * ucx_allocator_malloc function. So it is implementation depended, whether the
- * returned sstr_t.ptr pointer must be passed to the allocators
- * ucx_allocator_free function manually.
- * 
- * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
- * terminated.
- * 
- * @param allocator a valid instance of a UcxAllocator
- * @param string the string to duplicate
- * @return a duplicate of the string
- * @see sstrdup()
- */
-sstr_t sstrdup_a(UcxAllocator *allocator, sstr_t 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);
-
-/**
- * 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 sstrprefix(sstr_t string, sstr_t 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 sstrsuffix(sstr_t string, sstr_t suffix);
-
-/**
- * Returns a lower case version of a string.
- * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of sstrdup() for the implications.
- * 
- * @param string the input string
- * @return the resulting lower case string
- * @see sstrdup()
- */
-sstr_t sstrlower(sstr_t string);
-
-/**
- * Returns a lower case version of a string.
- * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of sstrdup_a() for the implications.
- * 
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting lower case string
- * @see sstrdup_a()
- */
-sstr_t sstrlower_a(UcxAllocator *allocator, sstr_t string);
-
-/**
- * Returns a upper case version of a string.
- * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of sstrdup() for the implications.
- * 
- * @param string the input string
- * @return the resulting upper case string
- * @see sstrdup()
- */
-sstr_t sstrupper(sstr_t string);
-
-/**
- * Returns a upper case version of a string.
- * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of sstrdup_a() for the implications.
- * 
- * @param allocator the allocator used for duplicating the string
- * @param string the input string
- * @return the resulting upper case string
- * @see sstrdup_a()
- */
-sstr_t sstrupper_a(UcxAllocator *allocator, sstr_t string);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_STRING_H */
--- a/src/ucx/test.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/test.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,7 +26,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "test.h"
+#include "ucx/test.h"
 
 UcxTestSuite* ucx_test_suite_new() {
     UcxTestSuite* suite = (UcxTestSuite*) malloc(sizeof(UcxTestSuite));
@@ -86,6 +86,6 @@
         elem->test(suite, output);
     }
     fwrite("\nAll test completed.\n", 1, 21, output);
-    fprintf(output, "  Total:   %d\n  Success: %d\n  Failure: %d\n",
+    fprintf(output, "  Total:   %u\n  Success: %u\n  Failure: %u\n",
             suite->success+suite->failure, suite->success, suite->failure);
 }
--- a/src/ucx/test.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,241 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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.c	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/ucx.c	Tue Aug 25 12:07:56 2020 +0200
@@ -2,14 +2,23 @@
  * @mainpage UAP Common Extensions
  * Library with common and useful functions, macros and data structures.
  * <p>
- * Latest available source:<br/>
+ * 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 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -34,4 +43,20 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "ucx.h"
+#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.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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   0
-
-/** Minor UCX version as integer constant. */
-#define UCX_VERSION_MINOR   10
-
-#include <stdlib.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
-#ifndef _Bool
-#define _Bool bool
-#define restrict
-#endif
-/** Use C naming even when compiling with C++.  */
-#define UCX_EXTERN extern "C"
-extern "C" {
-#else
-/** Pointless in C. */
-#define UCX_EXTERN
-#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)(void*,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)(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*);
-
-#ifdef	__cplusplus
-}
-#endif
-
-#endif	/* UCX_H */
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/allocator.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,206 @@
+/*
+ * 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 */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/array.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,517 @@
+/*
+ * 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.
+ * 
+ * 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 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)
+
+/**
+ * Convenience macro for ucx_array_util_set() which automatically computes
+ * <code>sizeof(data)</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 data the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ * @see ucx_array_util_set()
+ */
+#define UCX_ARRAY_UTIL_SET(array, capacity, idx, data) \
+    ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \
+                         sizeof(data), idx, data)
+
+/**
+ * Sets an element in an arbitrary user defined array.
+ * 
+ * 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 ... 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, ...);
+
+
+/**
+ * Convenience macro for ucx_array_util_set_a() which automatically computes
+ * <code>sizeof(data)</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 data the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ * @see ucx_array_util_set_a()
+ */
+#define UCX_ARRAY_UTIL_SET_A(alloc, array, capacity, idx, data) \
+    ucx_array_util_set_a(alloc, capacity, sizeof(data), idx, data)
+
+/**
+ * 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);
+
+/**
+ * Inserts an element at the end of the array.
+ * 
+ * This is an O(1) operation.
+ * The array will automatically grow, if the capacity is exceeded.
+ * If the type of the argument has a different size than the element size of
+ * this array, the behavior is undefined.
+ * 
+ * @param array a pointer the array where to append the data
+ * @param elem the value to insert
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append_from()
+ * @see ucx_array_set()
+ */
+#define ucx_array_append(array, elem) ucx_array_appendv(array, elem)
+
+/**
+ * For internal use.
+ * Use ucx_array_append()
+ * 
+ * @param array
+ * @param ... 
+ * @return 
+ * @see ucx_array_append()
+ */
+int ucx_array_appendv(UcxArray *array, ...);
+
+
+/**
+ * Inserts an element 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() instead.
+ * 
+ * @param array a pointer the array where to prepend the data
+ * @param elem the value to insert
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append()
+ * @see ucx_array_set_from()
+ * @see ucx_array_prepend_from()
+ */
+#define ucx_array_prepend(array, elem) ucx_array_prependv(array, elem)
+
+/**
+ * For internal use.
+ * Use ucx_array_prepend()
+ * 
+ * @param array
+ * @param ... 
+ * @return 
+ * @see ucx_array_prepend()
+ */
+int ucx_array_prependv(UcxArray *array, ...);
+
+
+/**
+ * Sets an element at the specified index.
+ * 
+ * If the any index is out of bounds, the array automatically grows.
+ * 
+ * @param array a pointer the array where to set the data
+ * @param index the index of the element to set
+ * @param elem the value to set
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append()
+ * @see ucx_array_set_from()
+ */
+#define ucx_array_set(array, index, elem) ucx_array_setv(array, index, elem)
+
+/**
+ * For internal use.
+ * Use ucx_array_set()
+ * 
+ * @param array
+ * @param index
+ * @param ... 
+ * @return 
+ * @see ucx_array_set()
+ */
+int ucx_array_setv(UcxArray *array, size_t index, ...);
+
+/**
+ * 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);
+
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_ARRAY_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/avl.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,353 @@
+/*
+ * 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 */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/buffer.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,339 @@
+/*
+ * 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 */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/list.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,396 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Doubly linked list implementation.
+ * 
+ * @file   list.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_LIST_H
+#define	UCX_LIST_H
+
+#include "ucx.h"
+#include "allocator.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Loop statement for UCX lists.
+ * 
+ * The first argument is the name of the iteration variable. The scope of
+ * this variable is limited to the <code>UCX_FOREACH</code> statement.
+ * 
+ * The second argument is a pointer to the list. In most cases this will be the
+ * pointer to the first element of the list, but it may also be an arbitrary
+ * element of the list. The iteration will then start with that element.
+ * 
+ * @param list The first element of the list
+ * @param elem The variable name of the element
+ */
+#define UCX_FOREACH(elem,list) \
+        for (UcxList* elem = list ; elem != NULL ; elem = elem->next)
+
+/**
+ * UCX list type.
+ * @see UcxList
+ */
+typedef struct UcxList UcxList;
+
+/**
+ * UCX list structure.
+ */
+struct UcxList {
+    /**
+     * List element payload.
+     */
+    void    *data;
+    /**
+     * Pointer to the next list element or <code>NULL</code>, if this is the
+     * last element.
+     */
+    UcxList *next;
+    /**
+     * Pointer to the previous list element or <code>NULL</code>, if this is
+     * the first element.
+     */
+    UcxList *prev;
+};
+
+/**
+ * Creates an element-wise copy of a list.
+ * 
+ * This function clones the specified list by creating new list elements and
+ * copying the data with the specified copy_func(). If no copy_func() is
+ * specified, a shallow copy is created and the new list will reference the
+ * same data as the source list.
+ * 
+ * @param list the list to copy
+ * @param cpyfnc a pointer to the function that shall copy an element (may be
+ * <code>NULL</code>)
+ * @param data additional data for the copy_func()
+ * @return a pointer to the copy
+ */
+UcxList *ucx_list_clone(UcxList *list, copy_func cpyfnc, void* data);
+
+/**
+ * Creates an element-wise copy of a list using a UcxAllocator.
+ * 
+ * See ucx_list_clone() for details.
+ * 
+ * You might want to pass the allocator via the <code>data</code> parameter,
+ * to access it within the copy function for making deep copies.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list to copy
+ * @param cpyfnc a pointer to the function that shall copy an element (may be
+ * <code>NULL</code>)
+ * @param data additional data for the copy_func()
+ * @return a pointer to the copy
+ * @see ucx_list_clone()
+ */
+UcxList *ucx_list_clone_a(UcxAllocator *allocator, UcxList *list,
+        copy_func cpyfnc, void* data);
+
+/**
+ * Compares two UCX lists element-wise by using a compare function.
+ * 
+ * Each element of the two specified lists are compared by using the specified
+ * compare function and the additional data. The type and content of this
+ * additional data depends on the cmp_func() used.
+ * 
+ * If the list pointers denote elements within a list, the lists are compared
+ * starting with the denoted elements. Thus any previous elements are not taken
+ * into account. This might be useful to check, if certain list tails match
+ * each other.
+ * 
+ * @param list1 the first list
+ * @param list2 the second list
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the two lists equal element-wise, 0 otherwise
+ */
+int ucx_list_equals(const UcxList *list1, const UcxList *list2,
+        cmp_func cmpfnc, void* data);
+
+/**
+ * Destroys the entire list.
+ * 
+ * The members of the list are not automatically freed, so ensure they are
+ * otherwise referenced or destroyed by ucx_list_free_contents().
+ * Otherwise, a memory leak is likely to occur.
+ * 
+ * <b>Caution:</b> the argument <b>MUST</b> denote an entire list (i.e. a call
+ * to ucx_list_first() on the argument must return the argument itself)
+ * 
+ * @param list the list to free
+ * @see ucx_list_free_contents()
+ */
+void ucx_list_free(UcxList *list);
+
+/**
+ * Destroys the entire list using a UcxAllocator.
+ * 
+ * See ucx_list_free() for details.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list to free
+ * @see ucx_list_free()
+ */
+void ucx_list_free_a(UcxAllocator *allocator, UcxList *list);
+
+/**
+ * Destroys the contents of the specified list by calling the specified
+ * destructor on each of them.
+ * 
+ * Note, that the contents are not usable afterwards and the list should be
+ * destroyed with ucx_list_free().
+ *
+ * If no destructor is specified (<code>NULL</code>), stdlib's free() is used.
+ * 
+ * @param list the list for which the contents shall be freed
+ * @param destr optional destructor function
+ * @see ucx_list_free()
+ */
+void ucx_list_free_content(UcxList* list, ucx_destructor destr);
+
+
+/**
+ * Inserts an element at the end of the list.
+ * 
+ * This is generally an O(n) operation, as the end of the list is retrieved with
+ * ucx_list_last().
+ * 
+ * @param list the list where to append the data, or <code>NULL</code> to
+ * create a new list
+ * @param data the data to insert
+ * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
+ * the newly created list otherwise
+ */
+UcxList *ucx_list_append(UcxList *list, void *data);
+
+/**
+ * Inserts an element at the end of the list using a UcxAllocator.
+ * 
+ * See ucx_list_append() for details.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list where to append the data, or <code>NULL</code> to
+ * create a new list
+ * @param data the data to insert
+ * @return <code>list</code>, if it is not <code>NULL</code> or a pointer to
+ * the newly created list otherwise
+ * @see ucx_list_append()
+ */
+UcxList *ucx_list_append_a(UcxAllocator *allocator, UcxList *list, void *data);
+
+
+/**
+ * Inserts an element at the beginning of the list.
+ * 
+ * You <i>should</i> overwrite the old list pointer by calling
+ * <code>mylist = ucx_list_prepend(mylist, mydata);</code>. However, you may
+ * also perform successive calls of ucx_list_prepend() on the same list pointer,
+ * as this function always searchs for the head of the list with
+ * ucx_list_first().
+ * 
+ * @param list the list where to insert the data or <code>NULL</code> to create
+ * a new list
+ * @param data the data to insert
+ * @return a pointer to the new list head
+ */
+UcxList *ucx_list_prepend(UcxList *list, void *data);
+
+/**
+ * Inserts an element at the beginning of the list using a UcxAllocator.
+ * 
+ * See ucx_list_prepend() for details.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list where to insert the data or <code>NULL</code> to create
+ * a new list
+ * @param data the data to insert
+ * @return a pointer to the new list head
+ * @see ucx_list_prepend()
+ */
+UcxList *ucx_list_prepend_a(UcxAllocator *allocator, UcxList *list, void *data);
+
+/**
+ * Concatenates two lists.
+ * 
+ * Either of the two arguments may be <code>NULL</code>.
+ * 
+ * This function modifies the references to the next/previous element of
+ * the last/first element of <code>list1</code>/<code>
+ * list2</code>.
+ * 
+ * @param list1 first list
+ * @param list2 second list
+ * @return if <code>list1</code> is <code>NULL</code>, <code>list2</code> is
+ * returned, otherwise <code>list1</code> is returned
+ */
+UcxList *ucx_list_concat(UcxList *list1, UcxList *list2);
+
+/**
+ * Returns the first element of a list.
+ * 
+ * If the argument is the list pointer, it is directly returned. Otherwise
+ * this function traverses to the first element of the list and returns the
+ * list pointer.
+ * 
+ * @param elem one element of the list
+ * @return the first element of the list, the specified element is a member of
+ */
+UcxList *ucx_list_first(const UcxList *elem);
+
+/**
+ * Returns the last element of a list.
+ * 
+ * If the argument has no successor, it is the last element and therefore
+ * directly returned. Otherwise this function traverses to the last element of
+ * the list and returns it.
+ * 
+ * @param elem one element of the list
+ * @return the last element of the list, the specified element is a member of
+ */
+UcxList *ucx_list_last(const UcxList *elem);
+
+/**
+ * Returns the list element at the specified index.
+ * 
+ * @param list the list to retrieve the element from
+ * @param index index of the element to return
+ * @return the element at the specified index or <code>NULL</code>, if the
+ * index is greater than the list size
+ */
+UcxList *ucx_list_get(const UcxList *list, size_t index);
+
+/**
+ * Returns the index of an element.
+ * 
+ * @param list the list where to search for the element
+ * @param elem the element to find
+ * @return the index of the element or -1 if the list does not contain the
+ * element
+ */
+ssize_t ucx_list_indexof(const UcxList *list, const UcxList *elem);
+
+/**
+ * Returns the element count of the list.
+ * 
+ * @param list the list whose elements are counted
+ * @return the element count
+ */
+size_t ucx_list_size(const UcxList *list);
+
+/**
+ * Returns the index of an element containing the specified data.
+ *
+ * This function uses a cmp_func() to compare the data of each list element
+ * with the specified data. If no cmp_func is provided, the pointers are
+ * compared.
+ * 
+ * If the list contains the data more than once, the index of the first
+ * occurrence is returned.
+ *  
+ * @param list the list where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return the index of the element containing the specified data or -1 if the
+ * data is not found in this list
+ */
+ssize_t ucx_list_find(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
+
+/**
+ * Checks, if a list contains a specific element.
+ * 
+ * An element is found, if ucx_list_find() returns a value greater than -1.
+ * 
+ * @param list the list where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the list contains the specified element data
+ * @see ucx_list_find()
+ */
+int ucx_list_contains(UcxList *list, void *elem, cmp_func cmpfnc, void *data);
+
+/**
+ * Sorts a UcxList with natural merge sort.
+ * 
+ * This function uses O(n) additional temporary memory for merge operations
+ * that is automatically freed after each merge.
+ * 
+ * As the head of the list might change, you <b>MUST</b> call this function
+ * as follows: <code>mylist = ucx_list_sort(mylist, mycmpfnc, mydata);</code>.
+ * 
+ * @param list the list to sort
+ * @param cmpfnc the function that shall be used to compare the element data
+ * @param data additional data for the cmp_func()
+ * @return the sorted list
+ */
+UcxList *ucx_list_sort(UcxList *list, cmp_func cmpfnc, void *data);
+
+/**
+ * Removes an element from the list.
+ * 
+ * If the first element is removed, the list pointer changes. So it is
+ * <i>highly recommended</i> to <i>always</i> update the pointer by calling
+ * <code>mylist = ucx_list_remove(mylist, myelem);</code>.
+ * 
+ * @param list the list from which the element shall be removed
+ * @param element the element to remove
+ * @return returns the updated list pointer or <code>NULL</code>, if the list
+ * is now empty
+ */
+UcxList *ucx_list_remove(UcxList *list, UcxList *element);
+
+/**
+ * Removes an element from the list using a UcxAllocator.
+ * 
+ * See ucx_list_remove() for details.
+ * 
+ * @param allocator the allocator to use
+ * @param list the list from which the element shall be removed
+ * @param element the element to remove
+ * @return returns the updated list pointer or <code>NULL</code>, if the list
+ * @see ucx_list_remove()
+ */
+UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list,
+        UcxList *element);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_LIST_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/logging.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,250 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Logging API.
+ * 
+ * @file   logging.h
+ * @author Mike Becker, Olaf Wintermann
+ */
+#ifndef UCX_LOGGING_H
+#define UCX_LOGGING_H
+
+#include "ucx.h"
+#include "map.h"
+#include "string.h"
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* leave enough space for custom log levels */
+
+/** Log level for error messages. */
+#define UCX_LOGGER_ERROR        0x00
+    
+/** Log level for warning messages. */
+#define UCX_LOGGER_WARN         0x10
+
+/** Log level for information messages. */
+#define UCX_LOGGER_INFO         0x20
+
+/** Log level for debug messages. */
+#define UCX_LOGGER_DEBUG        0x30
+
+/** Log level for trace messages. */
+#define UCX_LOGGER_TRACE        0x40
+
+/**
+ * Output flag for the log level. 
+ * If this flag is set, the log message will contain the log level.
+ * @see UcxLogger.mask
+ */
+#define UCX_LOGGER_LEVEL        0x01
+
+/**
+ * Output flag for the timestmap.
+ * If this flag is set, the log message will contain the timestmap.
+ * @see UcxLogger.mask
+ */
+#define UCX_LOGGER_TIMESTAMP    0x02
+
+/**
+ * Output flag for the source.
+ * If this flag is set, the log message will contain the source file and line
+ * number.
+ * @see UcxLogger.mask
+ */
+#define UCX_LOGGER_SOURCE       0x04
+
+/**
+ * The UCX Logger object.
+ */
+typedef struct {
+    /** The stream this logger writes its messages to.*/
+    void *stream;
+
+    /**
+     * The write function that shall be used.
+     * For standard file or stdout loggers this might be standard fwrite
+     * (default).
+     */
+    write_func writer;
+
+    /**
+     * The date format for timestamp outputs including the delimiter
+     * (default: <code>"%F %T %z "</code>).
+     * @see UCX_LOGGER_TIMESTAMP
+     */
+    char *dateformat;
+
+    /**
+     * The level, this logger operates on.
+     * If a log command is issued, the message will only be logged, if the log
+     * level of the message is less or equal than the log level of the logger.
+     */
+    unsigned int level;
+
+    /**
+     * A configuration mask for automatic output. 
+     * For each flag that is set, the logger automatically outputs some extra
+     * information like the timestamp or the source file and line number.
+     * See the documentation for the flags for details.
+     */
+    unsigned int mask;
+
+    /**
+     * A map of valid log levels for this logger.
+     * 
+     * The keys represent all valid log levels and the values provide string
+     * representations, that are used, if the UCX_LOGGER_LEVEL flag is set.
+     * 
+     * The exact data types are <code>unsigned int</code> for the key and
+     * <code>const char*</code> for the value.
+     * 
+     * @see UCX_LOGGER_LEVEL
+     */
+    UcxMap* levels;
+} UcxLogger;
+
+/**
+ * Creates a new logger.
+ * @param stream the stream, which the logger shall write to
+ * @param level the level on which the logger shall operate
+ * @param mask configuration mask (cf. UcxLogger.mask)
+ * @return a new logger object
+ */
+UcxLogger *ucx_logger_new(void *stream, unsigned int level, unsigned int mask);
+
+/**
+ * Destroys the logger.
+ * 
+ * The map containing the valid log levels is also automatically destroyed.
+ * 
+ * @param logger the logger to destroy
+ */
+void ucx_logger_free(UcxLogger* logger);
+
+/**
+ * Internal log function - use macros instead.
+ * 
+ * This function uses the <code>format</code> and variadic arguments for a
+ * printf()-style output of the log message.
+ * 
+ * Dependent on the UcxLogger.mask some information is prepended. The complete
+ * format is:
+ * 
+ * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code>
+ * 
+ * <b>Attention:</b> the message (including automatically generated information)
+ * is limited to 4096 characters. The level description is limited to
+ * 256 characters and the timestamp string is limited to 128 characters.
+ * 
+ * @param logger the logger to use
+ * @param level the level to log on
+ * @param file information about the source file
+ * @param line information about the source line number
+ * @param format format string
+ * @param ... arguments
+ * @see ucx_logger_log()
+ */
+void ucx_logger_logf(UcxLogger *logger, unsigned int level, const char* file,
+        const unsigned int line, const char* format, ...);
+
+/**
+ * Registers a custom log level.
+ * @param logger the logger
+ * @param level the log level as unsigned integer
+ * @param name a string literal describing the level
+ */
+#define ucx_logger_register_level(logger, level, name) {\
+        unsigned int l; \
+            l = level; \
+            ucx_map_int_put(logger->levels, l, (void*) "[" name "]"); \
+        } while (0);
+
+/**
+ * Logs a message at the specified level.
+ * @param logger the logger to use
+ * @param level the level to log the message on
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_log(logger, level, ...) \
+    ucx_logger_logf(logger, level, __FILE__, __LINE__, __VA_ARGS__)
+
+/**
+ * Shortcut for logging an error message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_error(logger, ...) \
+    ucx_logger_log(logger, UCX_LOGGER_ERROR, __VA_ARGS__)
+
+/**
+ * Shortcut for logging an information message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_info(logger, ...) \
+    ucx_logger_log(logger, UCX_LOGGER_INFO, __VA_ARGS__)
+
+/**
+ * Shortcut for logging a warning message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_warn(logger, ...) \
+    ucx_logger_log(logger, UCX_LOGGER_WARN, __VA_ARGS__)
+
+/**
+ * Shortcut for logging a debug message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_debug(logger, ...) \
+    ucx_logger_log(logger, UCX_LOGGER_DEBUG, __VA_ARGS__)
+
+/**
+ * Shortcut for logging a trace message.
+ * @param logger the logger to use
+ * @param ... format string and arguments
+ * @see ucx_logger_logf()
+ */
+#define ucx_logger_trace(logger, ...) \
+    ucx_logger_log(logger, UCX_LOGGER_TRACE, __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UCX_LOGGING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/map.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,480 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file map.h
+ * 
+ * Hash map implementation.
+ * 
+ * This implementation uses murmur hash 2 and separate chaining with linked
+ * lists.
+ * 
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_MAP_H
+#define	UCX_MAP_H
+
+#include "ucx.h"
+#include "string.h"
+#include "allocator.h"
+#include <stdio.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * Loop statement for UCX maps.
+ * 
+ * The <code>key</code> variable is implicitly defined, but the
+ * <code>value</code> variable must be already declared as type information
+ * cannot be inferred.
+ * 
+ * @param key the variable name for the key
+ * @param value the variable name for the value
+ * @param iter a UcxMapIterator
+ * @see ucx_map_iterator()
+ */
+#define UCX_MAP_FOREACH(key,value,iter) \
+        for(UcxKey key;ucx_map_iter_next(&iter,&key, (void**)&value);)
+
+/** Type for the UCX map. @see UcxMap */
+typedef struct UcxMap          UcxMap;
+
+/** Type for a key of a UcxMap. @see UcxKey */
+typedef struct UcxKey          UcxKey;
+
+/** Type for an element of a UcxMap. @see UcxMapElement */
+typedef struct UcxMapElement   UcxMapElement;
+
+/** Type for an iterator over a UcxMap. @see UcxMapIterator */
+typedef struct UcxMapIterator  UcxMapIterator;
+
+/** Structure for the UCX map. */
+struct UcxMap {
+    /** An allocator that is used for the map elements. */
+    UcxAllocator  *allocator;
+    /** The array of map element lists. */
+    UcxMapElement **map;
+    /** The size of the map is the length of the element list array. */
+    size_t        size;
+    /** The count of elements currently stored in this map. */
+    size_t        count;
+};
+
+/** Structure to publicly denote a key of a UcxMap. */
+struct UcxKey {
+    /** The key data. */
+    const void *data;
+    /** The length of the key data. */
+    size_t     len;
+    /** A cache for the hash value of the key data. */
+    int        hash;
+};
+
+/** Internal structure for a key of a UcxMap. */
+struct UcxMapKey {
+    /** The key data. */
+    void    *data;
+    /** The length of the key data. */
+    size_t  len;
+    /** The hash value of the key data. */
+    int     hash;
+};
+
+/** Structure for an element of a UcxMap. */
+struct UcxMapElement {
+    /** The value data. */
+    void              *data;
+    
+    /** A pointer to the next element in the current list. */
+    UcxMapElement     *next;
+    
+    /** The corresponding key. */
+    struct UcxMapKey  key;
+};
+
+/** Structure for an iterator over a UcxMap. */
+struct UcxMapIterator {
+    /** The map to iterate over. */
+    UcxMap        *map;
+    
+    /** The current map element. */
+    UcxMapElement *cur;
+    
+    /**
+     * The current index of the element list array.
+     * <b>Attention: </b> this is <b>NOT</b> the element index! Do <b>NOT</b>
+     * manually iterate over the map by increasing this index. Use
+     * ucx_map_iter_next().
+     * @see UcxMap.map*/
+    size_t        index;
+};
+
+/**
+ * Creates a new hash map with the specified size.
+ * @param size the size of the hash map
+ * @return a pointer to the new hash map
+ */
+UcxMap *ucx_map_new(size_t size);
+
+/**
+ * Creates a new hash map with the specified size using a UcxAllocator.
+ * @param allocator the allocator to use
+ * @param size the size of the hash map
+ * @return a pointer to the new hash map
+ */
+UcxMap *ucx_map_new_a(UcxAllocator *allocator, size_t size);
+
+/**
+ * Frees a hash map.
+ * 
+ * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
+ * before calling this function to achieve that.
+ * 
+ * @param map the map to be freed
+ * @see ucx_map_free_content()
+ */
+void ucx_map_free(UcxMap *map);
+
+/**
+ * Frees the contents of a hash map.
+ * 
+ * This is a convenience function that iterates over the map and passes all
+ * values to the specified destructor function.
+ * 
+ * If no destructor is specified (<code>NULL</code>), the free() function of
+ * the map's own allocator is used.
+ * 
+ * You must ensure, that it is valid to pass each value in the map to the same
+ * destructor function.
+ * 
+ * You should free or clear the map afterwards, as the contents will be invalid.
+ * 
+ * @param map for which the contents shall be freed
+ * @param destr optional pointer to a destructor function
+ * @see ucx_map_free()
+ * @see ucx_map_clear()
+ */
+void ucx_map_free_content(UcxMap *map, ucx_destructor destr);
+
+/**
+ * Clears a hash map.
+ * 
+ * <b>Note:</b> the contents are <b>not</b> freed, use ucx_map_free_content()
+ * before calling this function to achieve that.
+ * 
+ * @param map the map to be cleared
+ * @see ucx_map_free_content()
+ */
+void ucx_map_clear(UcxMap *map);
+
+
+/**
+ * Copies contents from a map to another map using a copy function.
+ * 
+ * <b>Note:</b> The destination map does not need to be empty. However, if it
+ * contains data with keys that are also present in the source map, the contents
+ * are overwritten.
+ * 
+ * @param from the source map
+ * @param to the destination map
+ * @param fnc the copy function or <code>NULL</code> if the pointer address
+ * shall be copied
+ * @param data additional data for the copy function
+ * @return 0 on success or a non-zero value on memory allocation errors
+ */
+int ucx_map_copy(UcxMap *from, UcxMap *to, copy_func fnc, void *data);
+
+/**
+ * Clones the map and rehashes if necessary.
+ * 
+ * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant.
+ * This function <i>always</i> ensures a new UcxMap.size of at least
+ * 2.5*UcxMap.count.
+ * 
+ * @param map the map to clone
+ * @param fnc the copy function to use or <code>NULL</code> if the new and
+ * the old map shall share the data pointers
+ * @param data additional data for the copy function
+ * @return the cloned map
+ * @see ucx_map_copy()
+ */
+UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data);
+
+/**
+ * Increases size of the hash map, if necessary.
+ * 
+ * The load value is 0.75*UcxMap.size. If the element count exceeds the load
+ * value, the map needs to be rehashed. Otherwise no action is performed and
+ * this function simply returns 0.
+ * 
+ * The rehashing process ensures, that the UcxMap.size is at least
+ * 2.5*UcxMap.count. So there is enough room for additional elements without
+ * the need of another soon rehashing.
+ * 
+ * You can use this function to dramatically increase access performance.
+ * 
+ * @param map the map to rehash
+ * @return 1, if a memory allocation error occurred, 0 otherwise
+ */
+int ucx_map_rehash(UcxMap *map);
+
+/**
+ * Puts a key/value-pair into the map.
+ * 
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ */
+int ucx_map_put(UcxMap *map, UcxKey key, void *value);
+
+/**
+ * Retrieves a value by using a key.
+ * 
+ * @param map the map
+ * @param key the key
+ * @return the value
+ */
+void* ucx_map_get(UcxMap *map, UcxKey key);
+
+/**
+ * Removes a key/value-pair from the map by using the key.
+ * 
+ * @param map the map
+ * @param key the key
+ * @return the removed value
+ */
+void* ucx_map_remove(UcxMap *map, UcxKey key);
+
+/**
+ * Creates a new map containing all elements from the maps a and b.
+ * 
+ * If a key exists in both maps, the value is taken from map a.
+ * 
+ * @param a map a
+ * @param b map b
+ * @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 new union map
+ * @see ucx_map_copy()
+ */
+UcxMap *ucx_map_union(UcxMap *a, UcxMap *b, copy_func fnc, void *data);
+
+/**
+ * intersection
+ * 
+ * If a key exists in both maps, the value is taken from map a.
+ * 
+ * @param a map a
+ * @param b map b
+ * @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 new intersection map
+ * @see ucx_map_copy()
+ */
+UcxMap *ucx_map_intersection(UcxMap *a, UcxMap *b, copy_func fnc, void *data);
+
+/**
+ * relative complement
+ * 
+ * If a key exists in both maps, the value is taken from map a.
+ * 
+ * @param a map a
+ * @param b map b
+ * @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 new intersection map
+ * @see ucx_map_copy()
+ */
+UcxMap *ucx_map_relcomplement(UcxMap *a, UcxMap *b, copy_func fnc, void *data);
+
+/**
+ * Shorthand for putting data with a sstr_t key into the map.
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ * @see ucx_map_put()
+ */
+#define ucx_map_sstr_put(map, key, value) \
+    ucx_map_put(map, ucx_key(key.ptr, key.length), (void*)value)
+
+/**
+ * Shorthand for putting data with a C string key into the map.
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ * @see ucx_map_put()
+ */
+#define ucx_map_cstr_put(map, key, value) \
+    ucx_map_put(map, ucx_key(key, strlen(key)), (void*)value)
+
+/**
+ * Shorthand for putting data with an integer key into the map.
+ * @param map the map
+ * @param key the key
+ * @param value the value
+ * @return 0 on success, non-zero value on failure
+ * @see ucx_map_put()
+ */
+#define ucx_map_int_put(map, key, value) \
+    ucx_map_put(map, ucx_key(&key, sizeof(key)), (void*)value)
+
+/**
+ * Shorthand for getting data from the map with a sstr_t key.
+ * @param map the map
+ * @param key the key
+ * @return the value
+ * @see ucx_map_get()
+ */
+#define ucx_map_sstr_get(map, key) \
+    ucx_map_get(map, ucx_key(key.ptr, key.length))
+
+/**
+ * Shorthand for getting data from the map with a C string key.
+ * @param map the map
+ * @param key the key
+ * @return the value
+ * @see ucx_map_get()
+ */
+#define ucx_map_cstr_get(map, key) \
+    ucx_map_get(map, ucx_key(key, strlen(key)))
+
+/**
+ * Shorthand for getting data from the map with an integer key.
+ * @param map the map
+ * @param key the key
+ * @return the value
+ * @see ucx_map_get()
+ */
+#define ucx_map_int_get(map, key) \
+    ucx_map_get(map, ucx_key(&key, sizeof(int)))
+
+/**
+ * Shorthand for removing data from the map with a sstr_t key.
+ * @param map the map
+ * @param key the key
+ * @return the removed value
+ * @see ucx_map_remove()
+ */
+#define ucx_map_sstr_remove(map, key) \
+    ucx_map_remove(map, ucx_key(key.ptr, key.length))
+
+/**
+ * Shorthand for removing data from the map with a C string key.
+ * @param map the map
+ * @param key the key
+ * @return the removed value
+ * @see ucx_map_remove()
+ */
+#define ucx_map_cstr_remove(map, key) \
+    ucx_map_remove(map, ucx_key(key, strlen(key)))
+
+/**
+ * Shorthand for removing data from the map with an integer key.
+ * @param map the map
+ * @param key the key
+ * @return the removed value
+ * @see ucx_map_remove()
+ */
+#define ucx_map_int_remove(map, key) \
+    ucx_map_remove(map, ucx_key(&key, sizeof(key)))
+
+/**
+ * Creates a UcxKey based on the given data.
+ * 
+ * This function implicitly computes the hash.
+ * 
+ * @param data the data for the key
+ * @param len the length of the data
+ * @return a UcxKey with implicitly computed hash
+ * @see ucx_hash()
+ */
+UcxKey ucx_key(const void *data, size_t len);
+
+/**
+ * Computes a murmur hash-2.
+ * 
+ * @param data the data to hash
+ * @param len the length of the data
+ * @return the murmur hash-2 of the data
+ */
+int ucx_hash(const char *data, size_t len);
+
+/**
+ * Creates an iterator for a map.
+ * 
+ * <b>Note:</b> A UcxMapIterator iterates over all elements in all element
+ * lists successively. Therefore the order highly depends on the key hashes and
+ * may vary under different map sizes. So generally you may <b>NOT</b> rely on
+ * the iteration order.
+ * 
+ * <b>Note:</b> The iterator is <b>NOT</b> initialized. You need to call
+ * ucx_map_iter_next() at least once before accessing any information. However,
+ * it is not recommended to access the fields of a UcxMapIterator directly.
+ * 
+ * @param map the map to create the iterator for
+ * @return an iterator initialized on the first element of the
+ * first element list
+ * @see ucx_map_iter_next()
+ */
+UcxMapIterator ucx_map_iterator(UcxMap *map);
+
+/**
+ * Proceeds to the next element of the map (if any).
+ * 
+ * Subsequent calls on the same iterator proceed to the next element and
+ * store the key/value-pair into the memory specified as arguments of this
+ * function.
+ * 
+ * If no further elements are found, this function returns zero and leaves the
+ * last found key/value-pair in memory.
+ * 
+ * @param iterator the iterator to use
+ * @param key a pointer to the memory where to store the key
+ * @param value a pointer to the memory where to store the value
+ * @return 1, if another element was found, 0 if all elements has been processed
+ * @see ucx_map_iterator()
+ */
+int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value);
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_MAP_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/mempool.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,209 @@
+/*
+ * 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 */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/properties.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,221 @@
+/*
+ * 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 */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/stack.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,240 @@
+/*
+ * 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 */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/string.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,1079 @@
+/*
+ * 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)
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_STRING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/test.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,241 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+ 
+/**
+ * @file: test.h
+ * 
+ * UCX Test Framework.
+ * 
+ * Usage of this test framework:
+ *
+ * **** IN HEADER FILE: ****
+ *
+ * <pre>
+ * UCX_TEST(function_name);
+ * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
+ * </pre>
+ *
+ * **** IN SOURCE FILE: ****
+ * <pre>
+ * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
+ *   // tests with UCX_TEST_ASSERT()
+ * }
+ * 
+ * UCX_TEST(function_name) {
+ *   // memory allocation and other stuff here
+ *   #UCX_TEST_BEGIN
+ *   // tests with UCX_TEST_ASSERT() and/or
+ *   // calls with UCX_TEST_CALL_SUBROUTINE() here
+ *   #UCX_TEST_END
+ *   // cleanup of memory here
+ * }
+ * </pre>
+ *
+ * <b>Note:</b> if a test fails, a longjump is performed
+ * back to the #UCX_TEST_BEGIN macro!
+ * 
+ * <b>Attention:</b> Do not call own functions within a test, that use
+ * UCX_TEST_ASSERT() macros and are not defined by using UCX_TEST_SUBROUTINE().
+ * 
+ *
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ *
+ */
+
+#ifndef UCX_TEST_H
+#define	UCX_TEST_H
+
+#include "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 */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/ucx.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ * 
+ * This is a custom implementation in case there is no compiler builtin
+ * available.
+ * 
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t, where the result should
+ * be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
+#define ucx_szmul(a, b, result) ucx_szmul_impl(a, b, result)
+
+int ucx_szmul_impl(size_t a, size_t b, size_t *result);
+
+#endif
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_H */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/ucx/utils.h	Tue Aug 25 12:07:56 2020 +0200
@@ -0,0 +1,508 @@
+/*
+ * 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	Fri Jul 24 08:00:11 2020 +0200
+++ b/src/ucx/utils.c	Tue Aug 25 12:07:56 2020 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2016 Olaf Wintermann. All rights reserved.
+ * 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:
@@ -26,22 +26,23 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "utils.h"
+#include "ucx/utils.h"
+
 #include <math.h>
 #include <stdio.h>
 #include <limits.h>
 #include <errno.h>
 
 /* COPY FUCNTIONS */
-void* ucx_strcpy(void* s, void* data) {
-    char *str = (char*) s;
+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(void* m, void* n) {
+void* ucx_memcpy(const void* m, void* n) {
     size_t k = *((size_t*)n);
     void *cpy = malloc(k);
     memcpy(cpy, m, k);
@@ -87,17 +88,103 @@
 
 /* COMPARE FUNCTIONS */
 
-int ucx_strcmp(void *s1, void *s2, void *data) {
-    return strcmp((char*)s1, (char*)s2);
+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_strncmp(void *s1, void *s2, void *n) {
-    return strncmp((char*)s1, (char*)s2, *((size_t*) n));
+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_intcmp(void *i1, void *i2, void *data) {
-   int a = *((int*) i1);
-   int b = *((int*) i2);
+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 {
@@ -105,9 +192,111 @@
    }
 }
 
-int ucx_floatcmp(void *f1, void *f2, void *epsilon) {
-   float a = *((float*) f1);
-   float b = *((float*) f2);
+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;
@@ -116,9 +305,9 @@
    }
 }
 
-int ucx_doublecmp(void *d1, void *d2, void *epsilon) {
-   double a = *((float*) d1);
-   double b = *((float*) d2);
+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;
@@ -127,9 +316,9 @@
    }
 }
 
-int ucx_ptrcmp(void *ptr1, void *ptr2, void *data) {
-    intptr_t p1 = (intptr_t) ptr1;
-    intptr_t p2 = (intptr_t) ptr2;
+int ucx_cmp_ptr(const void *ptr1, const void *ptr2, void *data) {
+    const intptr_t p1 = (const intptr_t) ptr1;
+    const intptr_t p2 = (const intptr_t) ptr2;
     if (p1 == p2) {
         return 0;
     } else {
@@ -137,7 +326,7 @@
     }
 }
 
-int ucx_memcmp(void *ptr1, void *ptr2, void *n) {
+int ucx_cmp_mem(const void *ptr1, const void *ptr2, void *n) {
     return memcmp(ptr1, ptr2, *((size_t*)n));
 }
 
--- a/src/ucx/utils.h	Fri Jul 24 08:00:11 2020 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,281 +0,0 @@
-/*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Copyright 2016 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
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include "ucx.h"
-#include "string.h"
-#include "allocator.h"
-#include <inttypes.h>
-#include <string.h>
-#include <stdarg.h>
-
-/**
- * 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(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(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_strcmp(void *s1, 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_strncmp(void *s1, void *s2, void *n);
-
-/**
- * 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_intcmp(void *i1, 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_floatcmp(void *f1, 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_doublecmp(void *d1, 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_ptrcmp(void *ptr1, 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_memcmp(void *ptr1, 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/templates/config/server.template	Fri Jul 24 08:00:11 2020 +0200
+++ b/templates/config/server.template	Tue Aug 25 12:07:56 2020 +0200
@@ -2,46 +2,54 @@
 # server.conf
 #
 
-<Runtime>
+Runtime
+{
 	Temp        /tmp/webserver-rw6pgl8b/
 	User        webservd
 	MimeFile    mime.types
-</Runtime>
+}
 
-<LogFile>
+LogFile
+{
 	File        logs/errors
 	Level       INFO
-</LogFile>
+}
 
-<AccessLog>
+AccessLog
+{
 	File        logs/access
-</AccessLog>
+}
 
-<EventHandler>
+EventHandler
+{
     Name        default
     Threads     1
     Default     true
-</EventHandler>
+}
 
-<Threadpool>
+Threadpool
+{
     Name        default
     MinThreads  4
     MaxThreads  32
-</Threadpool>
+}
 
-<Listener>
+Listener
+{
     Name        http-listener-1
     Port        9090
     DefaultVS   %%WS_HOST%%
-</Listener>
+}
 
-<Listener>
+Listener
+{
     Name        http-listener-2
     Port        9091
     DefaultVS   %%WS_HOST%%
-</Listener>
+}
 
-<VirtualServer>
+VirtualServer
+{
 	Name        %%WS_HOST%%
 	Host        %%WS_HOST%%
     Listener    http-listener-1
@@ -50,6 +58,9 @@
     ACLFile     acl.conf
     DAVFile     dav.conf
     DocRoot     docs/ 
-</VirtualServer>
+}
 
 
+
+
+

mercurial