ucx update

Sat, 05 Dec 2020 11:54:58 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 05 Dec 2020 11:54:58 +0100
changeset 162
18892c0a9adc
parent 161
b1eac0878ce7
child 163
b70e2a77dea0

ucx update

application/main.c file | annotate | diff | comparison | revisions
ucx/Makefile file | annotate | diff | comparison | revisions
ucx/array.c file | annotate | diff | comparison | revisions
ucx/list.c file | annotate | diff | comparison | revisions
ucx/logging.c file | annotate | diff | comparison | revisions
ucx/map.c file | annotate | diff | comparison | revisions
ucx/string.c file | annotate | diff | comparison | revisions
ucx/ucx/array.h file | annotate | diff | comparison | revisions
ucx/ucx/list.h file | annotate | diff | comparison | revisions
ucx/ucx/logging.h file | annotate | diff | comparison | revisions
ucx/ucx/map.h file | annotate | diff | comparison | revisions
ucx/ucx/string.h file | annotate | diff | comparison | revisions
ucx/ucx/ucx.h file | annotate | diff | comparison | revisions
ucx/ucx/utils.h file | annotate | diff | comparison | revisions
ucx/utils.c file | annotate | diff | comparison | revisions
ui/common/context.h file | annotate | diff | comparison | revisions
--- a/application/main.c	Sat Dec 05 10:34:10 2020 +0100
+++ b/application/main.c	Sat Dec 05 11:54:58 2020 +0100
@@ -33,164 +33,14 @@
 #include <ucx/buffer.h>
 #include <ucx/utils.h>
 
-typedef struct Document {
-    UiText *text;
-    UiString *t1;
-    UiString *t2;
-    UiString *t3;
-    UiInteger *i;
-    UiDouble *d;
-    UiRange *r;
-    UiList *list;
-    UiDouble *progress;
-} Document;
-
-typedef struct Entry {
-    char *name;
-    char *desc;
-} Entry;
-
-Document *d1;
-Document *d2;
-int n = 1;
-
-UiImage *img;
-
-Document* create_doc(char *str);
-
-Document* next_doc(void) {
-    Document *doc = d1;
-    if(n == 1) {
-        doc = d2;
-        printf("doc2\n");
-    } else {
-        printf("doc1\n");
-    }
-    n++;
-    n = n%2;
-    return doc;
-}
-
-void action_menu(UiEvent *event, void *data) {
-    printf("action_menu\n");
-    
-    Document *doc = event->document;
-    char *s = doc->text->get(doc->text);
-    printf("text: {\n%s\n}\n", s);
-    //int i = doc->i->get(doc->i);
-    //printf("i: %d\n", i);
+void action_menu(UiEvent *event, void *userdata) {
     
-    fflush(stdout);
-}
-
-void list_data_get(UiEvent *event, UiSelection *selection, UiList *l, int i) {
-    ui_selection_settext(selection, "Hello World!", -1);
-}
-
-void list_drop(UiEvent *event, UiSelection *selection, UiList *l, int i) {
-    char *text = ui_selection_gettext(selection);
-    printf("dnd drop: {%s}\n", text);
-    free(text);
-}
-
-Document* create_doc(char *str) {
-    Document *doc = ui_document_new(sizeof(Document));
-    UiContext *ctx = ui_document_context(doc);
-    
-    doc->text = ui_text_new(ctx, "text");
-    doc->t1 = ui_string_new(ctx, "t1");
-    doc->t2 = ui_string_new(ctx, "t2");
-    doc->t3 = ui_string_new(ctx, "t3");
-    
-    doc->i = ui_int_new(ctx, "int");
-    doc->d = ui_double_new(ctx, "d");
-    doc->r = ui_range_new(ctx, "r");
-    
-    doc->progress = ui_double_new(ctx, "progress");
-    
-    doc->list = ui_list_new(ctx, "list");
-    Entry *e1 = calloc(1, sizeof(Entry));
-    e1->name = "test";
-    e1->desc = "test file";
-    Entry *e2 = calloc(1, sizeof(Entry));
-    e2->name = str;
-    e2->desc = "important document";
-    ui_list_append(doc->list, e1);
-    ui_list_append(doc->list, e2);
-    
-    return doc;
-}
-
-void* model_get(Entry *e, int col) {
-    if(col == 0) {
-        return img;
-    } else if(col == 1) {
-        return e->name;
-    } else if(col == 2) {
-        return e->desc;
-    }
-    return NULL;
-}
-
-void action_newdoc(UiEvent *event, void *data) {
-    printf("new doc;\n    ");
-    
-    Document *newd = next_doc();
-    ui_set_document(event->obj, newd);
-}
-
-void observ(UiEvent *event, void *data) {
-    printf("observ: %s\n", (char*)data);
-}
-
-void doublechanged(UiEvent *event, void *data) {
-    UiDouble *d = event->eventdata;
-    printf("d: %f\n", (float)d->get(d));
 }
 
 void application_startup(UiEvent *event, void *data) {
-    UiIcon *icon = ui_icon("folder", 16);
-    img = ui_icon_image(icon);
-    if(!img) {
-        fprintf(stderr, "Cannot load folder icon\n");
-    }
-    
-    //Document *doc = create_doc();
-    d1 = create_doc("doc1");
-    d2 = create_doc("doc2");
     
     UiObject *obj = ui_window("Test", NULL);
-    ui_set_document(obj, d1);
     
-    ui_textarea_nv(obj, "text");
-    //ui_radiobutton_nv(obj, "1", "int");
-    //ui_radiobutton_nv(obj, "2", "int");
-    //ui_radiobutton_nv(obj, "3", "int");
-    
-    ui_textfield_nv(obj, "t1");
-    //ui_textarea_nv(obj, "text");
-    //d1->t1->observers = ui_add_observer(d1->t1->observers, observ, "t1");
-    //d1->text->observers = ui_add_observer(d1->text->observers, observ, "text");
-    
-    UiModel *model = ui_model(obj->ctx, UI_ICON_TEXT, "name", UI_STRING, "desc", -1);
-    model->getvalue = (ui_getvaluefunc)model_get;
-    model->drop = list_drop;
-    model->data_get = list_data_get;
-    UiListCallbacks cb = {NULL, NULL, NULL};
-    //UIWIDGET table = ui_table_nv(obj, "list", model, cb);
-    //ui_table_dragsource(table, 0, "text/plain", NULL);
-    
-    //ui_spinner_setrange(ui_spinnerf_nv(obj, 1, 0, "d"), 0, 1000);
-    //ui_spinnerr_nv(obj, "r");
-    //d1->r->setrange(d1->r, 0, 10);
-    //d1->r->setextent(d1->r, 1);
-    //d1->d->observers = ui_add_observer(d1->d->observers, doublechanged, NULL);
-    
-    //ui_progressbar_nv(obj, "progress");
-    
-    ui_textfield(obj, NULL);
-    
-    ui_button(obj, "Switch Document", action_newdoc, NULL);
     
     ui_show(obj);
 }
--- a/ucx/Makefile	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/Makefile	Sat Dec 05 11:54:58 2020 +0100
@@ -42,6 +42,8 @@
 SRC += logging.c
 SRC += buffer.c
 SRC += stack.c
+SRC += ucx.c
+SRC += array.c
 
 OBJ   = $(SRC:%.c=../build/ucx/%.$(OBJ_EXT))
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/array.c	Sat Dec 05 11:54:58 2020 +0100
@@ -0,0 +1,467 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE /* we want to use qsort_r(), if available */
+#define __STDC_WANT_LIB_EXT1__ 1 /* use qsort_s, if available */
+
+
+#include "ucx/array.h"
+#include "ucx/utils.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef UCX_ARRAY_DISABLE_QSORT
+#ifdef __GLIBC__
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
+#define ucx_array_sort_impl qsort_r
+#endif /* glibc version >= 2.8 */
+#elif /* not  __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__)
+#define ucx_array_sort_impl ucx_qsort_r
+#define USE_UCX_QSORT_R
+#elif /* not (__APPLE || __FreeBSD__) */ defined(__sun)
+#if __STDC_VERSION__ >= 201112L
+#define ucx_array_sort_impl qsort_s
+#endif
+#endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */
+#endif /* UCX_ARRAY_DISABLE_QSORT */
+
+#ifndef ucx_array_sort_impl
+#define ucx_array_sort_impl ucx_mergesort
+#endif
+
+static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) {
+    size_t required_capacity = array->capacity;
+    while (reqcap > required_capacity) {
+        if (required_capacity * 2 < required_capacity)
+            return 1;
+        required_capacity <<= 1;
+    }
+    if (ucx_array_reserve(array, required_capacity)) {
+        return 1;
+    }
+    return 0;
+}
+
+int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
+    size_t elmsize, size_t index, void* data) {
+    
+    if(!alloc || !capacity || !array) {
+        errno = EINVAL;
+        return 1;
+    }
+    
+    size_t newcapacity = *capacity;
+    while(index >= newcapacity) {
+        if(ucx_szmul(newcapacity, 2, &newcapacity)) {
+            errno = EOVERFLOW;
+            return 1;
+        }        
+    }
+
+    size_t memlen, offset;
+    if(ucx_szmul(newcapacity, elmsize, &memlen)) {
+        errno = EOVERFLOW;
+        return 1;
+    }
+    /* we don't need to check index*elmsize - it is smaller than memlen */
+    
+    
+    void* newptr = alrealloc(alloc, *array, memlen);
+    if(newptr == NULL) {
+        errno = ENOMEM; /* we cannot assume that every allocator sets this */
+        return 1;
+    }
+    *array = newptr;
+    *capacity = newcapacity;
+    
+    
+    char* dest = *array;
+    dest += elmsize*index;
+    memcpy(dest, data, elmsize);
+    
+    return 0;
+}
+
+int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
+    size_t index, void* data) {
+    
+    return ucx_array_util_set_a(alloc, array, capacity, sizeof(void*),
+            index, &data);
+}
+
+UcxArray* ucx_array_new(size_t capacity, size_t elemsize) {
+    return ucx_array_new_a(capacity, elemsize, ucx_default_allocator());
+}
+
+UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
+        UcxAllocator* allocator) {
+    UcxArray* array = almalloc(allocator, sizeof(UcxArray));
+    if(array) {
+        ucx_array_init_a(array, capacity, elemsize, allocator);
+    }
+    return array;
+}
+
+void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize) {
+    ucx_array_init_a(array, capacity, elemsize, ucx_default_allocator());
+}
+
+void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
+        UcxAllocator* allocator) {
+    
+    array->allocator = allocator;
+    array->elemsize = elemsize;
+    array->size = 0;
+    array->data = alcalloc(allocator, capacity, elemsize);
+    
+    if (array->data) {
+        array->capacity = capacity;
+    } else {
+        array->capacity = 0;
+    }
+}
+
+int ucx_array_clone(UcxArray* dest, UcxArray const* src) {
+    if (ucx_array_ensurecap(dest, src->capacity)) {
+        return 1;
+    }
+    
+    dest->elemsize = src->elemsize;
+    dest->size = src->size;
+    
+    if (dest->data) {
+        memcpy(dest->data, src->data, src->size*src->elemsize);
+    }
+    
+    return 0;
+}
+
+int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
+        cmp_func cmpfnc, void* data) {
+    
+    if (array1->size != array2->size || array1->elemsize != array2->elemsize) {
+        return 0;
+    } else {
+        if (array1->size == 0)
+            return 1;
+        
+        size_t elemsize;
+        if (cmpfnc == NULL) {
+            cmpfnc = ucx_cmp_mem;
+            elemsize = array1->elemsize;
+            data = &elemsize;
+        }
+        
+        for (size_t i = 0 ; i < array1->size ; i++) {
+            int r = cmpfnc(
+                    ucx_array_at(array1, i),
+                    ucx_array_at(array2, i),
+                    data);
+            if (r != 0)
+                return 0;
+        }
+        return 1;
+    }
+}
+
+void ucx_array_destroy(UcxArray *array) {
+    if(array->data)
+        alfree(array->allocator, array->data);
+    array->data = NULL;
+    array->capacity = array->size = 0;
+}
+
+void ucx_array_free(UcxArray *array) {
+    ucx_array_destroy(array);
+    alfree(array->allocator, array);
+}
+
+int ucx_array_append_from(UcxArray *array, void *data, size_t count) {
+    if (ucx_array_ensurecap(array, array->size + count))
+        return 1;
+    
+    void* dest = ucx_array_at(array, array->size);
+    if (data) {
+        memcpy(dest, data, array->elemsize*count);
+    } else {
+        memset(dest, 0, array->elemsize*count);
+    }
+    array->size += count;
+    
+    return 0;
+}
+
+int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) {
+    if (ucx_array_ensurecap(array, array->size + count))
+        return 1;
+    
+    if (array->size > 0) {
+        void *dest = ucx_array_at(array, count);
+        memmove(dest, array->data, array->elemsize*array->size);
+    }
+    
+    if (data) {
+        memcpy(array->data, data, array->elemsize*count);
+    } else {
+        memset(array->data, 0, array->elemsize*count);
+    }
+    array->size += count;
+        
+    return 0;
+}
+
+int ucx_array_set_from(UcxArray *array, size_t index,
+        void *data, size_t count) {
+    if (ucx_array_ensurecap(array, index + count))
+        return 1;
+    
+    if (index+count > array->size) {
+        array->size = index+count;
+    }
+    
+    void *dest = ucx_array_at(array, index);
+    if (data) {
+        memcpy(dest, data, array->elemsize*count);
+    } else {
+        memset(dest, 0, array->elemsize*count);
+    }
+    
+    return 0;
+}
+
+int ucx_array_concat(UcxArray *array1, const UcxArray *array2) {
+    
+    if (array1->elemsize != array2->elemsize)
+        return 1;
+    
+    size_t capacity = array1->capacity+array2->capacity;
+        
+    if (array1->capacity < capacity) {
+        if (ucx_array_reserve(array1, capacity)) {
+            return 1;
+        }
+    }
+    
+    void* dest = ucx_array_at(array1, array1->size);
+    memcpy(dest, array2->data, array2->size*array2->elemsize);
+    
+    array1->size += array2->size;
+    
+    return 0;
+}
+
+void *ucx_array_at(UcxArray const *array, size_t index) {
+    char* memory = array->data;
+    char* loc = memory + index*array->elemsize;
+    return loc;
+}
+
+size_t ucx_array_find(UcxArray const *array, void *elem,
+        cmp_func cmpfnc, void *data) {
+    
+    size_t elemsize;
+    if (cmpfnc == NULL) {
+        cmpfnc = ucx_cmp_mem;
+        elemsize = array->elemsize;
+        data = &elemsize;
+    }
+
+    if (array->size > 0) {
+        for (size_t i = 0 ; i < array->size ; i++) {
+            void* ptr = ucx_array_at(array, i);
+            if (cmpfnc(ptr, elem, data) == 0) {
+                return i;
+            }
+        }
+        return array->size;
+    } else {
+        return 0;
+    }
+}
+
+int ucx_array_contains(UcxArray const *array, void *elem,
+        cmp_func cmpfnc, void *data) {
+    return ucx_array_find(array, elem, cmpfnc, data) != array->size;
+}
+
+static void ucx_mergesort_merge(void *arrdata,size_t elemsize,
+        cmp_func cmpfnc, void *data,
+        size_t start, size_t mid, size_t end) { 
+    
+    char* array = arrdata;
+    
+    size_t rightstart = mid + 1; 
+  
+    if (cmpfnc(array + mid*elemsize,
+            array + rightstart*elemsize, data) <= 0) {
+        /* already sorted */
+        return;
+    }
+  
+    /* we need memory for one element */
+    void *value = malloc(elemsize);
+    
+    while (start <= mid && rightstart <= end) { 
+        if (cmpfnc(array + start*elemsize,
+                array + rightstart*elemsize, data) <= 0) { 
+            start++; 
+        } else {
+            /* save the value from the right */
+            memcpy(value, array + rightstart*elemsize, elemsize);
+                        
+            /* shift all left elements one element to the right */
+            size_t shiftcount = rightstart-start;
+            void *startptr = array + start*elemsize;
+            void *dest = array + (start+1)*elemsize;
+            memmove(dest, startptr, shiftcount*elemsize);
+            
+            /* bring the first value from the right to the left */
+            memcpy(startptr, value, elemsize);
+  
+            start++; 
+            mid++; 
+            rightstart++; 
+        }
+    }
+    
+    /* free the temporary memory */
+    free(value);
+} 
+  
+static void ucx_mergesort_impl(void *arrdata, size_t elemsize,
+        cmp_func cmpfnc, void *data, size_t l, size_t r) { 
+    if (l < r) {
+        size_t m = l + (r - l) / 2; 
+  
+        ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m); 
+        ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r); 
+        ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r);
+    } 
+}
+
+static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize,
+        cmp_func cmpfnc, void *data) {
+    
+    ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1);
+}
+
+#ifdef USE_UCX_QSORT_R
+struct cmpfnc_swapargs_info {
+    cmp_func func;
+    void *data;
+};
+
+static int cmp_func_swap_args(void *data, const void *x, const void *y) {
+    struct cmpfnc_swapargs_info* info = data;
+    return info->func(x, y, info->data);
+}
+
+static void ucx_qsort_r(void *array, size_t count, size_t elemsize,
+		     cmp_func cmpfnc, void *data) {
+    struct cmpfnc_swapargs_info info;
+    info.func = cmpfnc;
+    info.data = data;
+    qsort_r(array, count, elemsize, &info, cmp_func_swap_args);
+}
+#endif /* USE_UCX_QSORT_R */
+
+void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data) {
+    ucx_array_sort_impl(array->data, array->size, array->elemsize,
+            cmpfnc, data);
+}
+
+void ucx_array_remove(UcxArray *array, size_t index) {
+    array->size--;
+    if (index < array->size) {
+        void* dest = ucx_array_at(array, index);
+        void* src = ucx_array_at(array, index+1);
+        memmove(dest, src, (array->size - index)*array->elemsize);
+    }
+}
+
+void ucx_array_remove_fast(UcxArray *array, size_t index) {
+    array->size--;
+    if (index < array->size) {       
+        void* dest = ucx_array_at(array, index);
+        void* src = ucx_array_at(array, array->size);
+        memcpy(dest, src, array->elemsize);
+    }
+}
+
+int ucx_array_shrink(UcxArray* array) {
+    void* newptr = alrealloc(array->allocator, array->data,
+                array->size*array->elemsize);
+    if (newptr) {
+        array->data = newptr;
+        array->capacity = array->size;
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+int ucx_array_resize(UcxArray* array, size_t capacity) {
+    if (array->capacity >= capacity) {
+        void* newptr = alrealloc(array->allocator, array->data,
+                capacity*array->elemsize);
+        if (newptr) {
+            array->data = newptr;
+            array->capacity = capacity;
+            if (array->size > array->capacity) {
+                array->size = array->capacity;
+            }
+            return 0;
+        } else {
+            return 1;
+        }
+    } else {
+        return ucx_array_reserve(array, capacity);
+    }
+}
+
+int ucx_array_reserve(UcxArray* array, size_t capacity) {
+    if (array->capacity > capacity) {
+        return 0;
+    } else {
+        void* newptr = alrealloc(array->allocator, array->data,
+                capacity*array->elemsize);
+        if (newptr) {
+            array->data = newptr;
+            array->capacity = capacity;
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+}
+
+int ucx_array_grow(UcxArray* array, size_t count) {
+    return ucx_array_reserve(array, array->size+count);
+}
--- a/ucx/list.c	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/list.c	Sat Dec 05 11:54:58 2020 +0100
@@ -28,11 +28,11 @@
 
 #include "ucx/list.h"
 
-UcxList *ucx_list_clone(UcxList *l, copy_func fnc, void *data) {
+UcxList *ucx_list_clone(const UcxList *l, copy_func fnc, void *data) {
     return ucx_list_clone_a(ucx_default_allocator(), l, fnc, data);
 }
 
-UcxList *ucx_list_clone_a(UcxAllocator *alloc, UcxList *l,
+UcxList *ucx_list_clone_a(UcxAllocator *alloc, const UcxList *l,
         copy_func fnc, void *data) {
     UcxList *ret = NULL;
     while (l) {
@@ -172,7 +172,8 @@
     return (UcxList*)(index == 0 ? e : NULL);
 }
 
-ssize_t ucx_list_find(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) {
+ssize_t ucx_list_find(const UcxList *l, void *elem,
+        cmp_func fnc, void *cmpdata) {
     ssize_t index = 0;
     UCX_FOREACH(e, l) {
         if (fnc) {
@@ -189,7 +190,8 @@
     return -1;
 }
 
-int ucx_list_contains(UcxList *l, void *elem, cmp_func fnc, void *cmpdata) {
+int ucx_list_contains(const UcxList *l, void *elem,
+        cmp_func fnc, void *cmpdata) {
     return ucx_list_find(l, elem, fnc, cmpdata) > -1;
 }
 
@@ -206,7 +208,7 @@
     return s;
 }
 
-static UcxList *ucx_list_sort_merge(int length,
+static UcxList *ucx_list_sort_merge(size_t length,
         UcxList* ls, UcxList* le, UcxList* re,
         cmp_func fnc, void* data) {
 
@@ -214,7 +216,7 @@
     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;
@@ -255,7 +257,7 @@
     }
 
     UcxList *lc;
-    int ln = 1;
+    size_t ln = 1;
 
     UcxList *ls = l, *le, *re;
     
@@ -271,7 +273,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) {
@@ -334,3 +336,93 @@
     alfree(alloc, e);
     return l;
 }
+
+
+static UcxList* ucx_list_setoperation_a(UcxAllocator *allocator,
+        UcxList const *left, UcxList const *right,
+        cmp_func cmpfnc, void* cmpdata,
+        copy_func cpfnc, void* cpdata,
+        int op) {
+    
+    UcxList *res = NULL;
+    UcxList *cur = NULL;
+    const UcxList *src = left;
+    
+    do {
+        UCX_FOREACH(node, src) {
+            void* elem = node->data;
+            if (
+                (op == 0 && !ucx_list_contains(res, elem, cmpfnc, cmpdata)) ||
+                (op == 1 && ucx_list_contains(right, elem, cmpfnc, cmpdata)) ||
+                (op == 2 && !ucx_list_contains(right, elem, cmpfnc, cmpdata))) {
+                UcxList *nl = almalloc(allocator, sizeof(UcxList));
+                nl->prev = cur;
+                nl->next = NULL;
+                if (cpfnc) {
+                    nl->data = cpfnc(elem, cpdata);
+                } else {
+                    nl->data = elem;
+                }
+                if (cur != NULL)
+                    cur->next = nl;
+                cur = nl;
+                if (res == NULL)
+                    res = cur;
+            }
+        }
+        if (op == 0 && src == left)
+            src = right;
+        else
+            src = NULL;
+    } while (src != NULL);
+    
+    return res;
+}
+
+UcxList* ucx_list_union(UcxList const *left, UcxList const *right,
+        cmp_func cmpfnc, void* cmpdata,
+        copy_func cpfnc, void* cpdata) {
+    return ucx_list_union_a(ucx_default_allocator(),
+            left, right, cmpfnc, cmpdata, cpfnc, cpdata);
+}
+
+UcxList* ucx_list_union_a(UcxAllocator *allocator,
+        UcxList const *left, UcxList const *right,
+        cmp_func cmpfnc, void* cmpdata,
+        copy_func cpfnc, void* cpdata) {
+    
+    return ucx_list_setoperation_a(allocator, left, right,
+            cmpfnc, cmpdata, cpfnc, cpdata, 0);
+}
+
+UcxList* ucx_list_intersection(UcxList const *left, UcxList const *right,
+        cmp_func cmpfnc, void* cmpdata,
+        copy_func cpfnc, void* cpdata) {
+    return ucx_list_intersection_a(ucx_default_allocator(), left, right,
+            cmpfnc, cmpdata, cpfnc, cpdata);
+}
+
+UcxList* ucx_list_intersection_a(UcxAllocator *allocator,
+        UcxList const *left, UcxList const *right,
+        cmp_func cmpfnc, void* cmpdata,
+        copy_func cpfnc, void* cpdata) {
+    
+    return ucx_list_setoperation_a(allocator, left, right,
+            cmpfnc, cmpdata, cpfnc, cpdata, 1);
+}
+
+UcxList* ucx_list_difference(UcxList const *left, UcxList const *right,
+        cmp_func cmpfnc, void* cmpdata,
+        copy_func cpfnc, void* cpdata) {
+    return ucx_list_difference_a(ucx_default_allocator(), left, right,
+            cmpfnc, cmpdata, cpfnc, cpdata);
+}
+
+UcxList* ucx_list_difference_a(UcxAllocator *allocator,
+        UcxList const *left, UcxList const *right,
+        cmp_func cmpfnc, void* cmpdata,
+        copy_func cpfnc, void* cpdata) {
+    
+    return ucx_list_setoperation_a(allocator, left, right,
+            cmpfnc, cmpdata, cpfnc, cpdata, 2);
+}
--- a/ucx/logging.c	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/logging.c	Sat Dec 05 11:54:58 2020 +0100
@@ -91,6 +91,10 @@
             k += strftime(msg+k, 128, logger->dateformat, localtime(&now));
         }
         if ((logger->mask & UCX_LOGGER_SOURCE) > 0) {
+            char *fpart = strrchr(file, '/');
+            if (fpart) file = fpart+1;
+            fpart = strrchr(file, '\\');
+            if (fpart) file = fpart+1;
             n = strlen(file);
             memcpy(msg+k, file, n);
             k += n;
--- a/ucx/map.c	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/map.c	Sat Dec 05 11:54:58 2020 +0100
@@ -103,7 +103,7 @@
     map->count = 0;
 }
 
-int ucx_map_copy(UcxMap *from, UcxMap *to, copy_func fnc, void *data) {
+int ucx_map_copy(UcxMap const *from, UcxMap *to, copy_func fnc, void *data) {
     UcxMapIterator i = ucx_map_iterator(from);
     void *value;
     UCX_MAP_FOREACH(key, value, i) {
@@ -114,9 +114,14 @@
     return 0;
 }
 
-UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data) {
+UcxMap *ucx_map_clone(UcxMap const *map, copy_func fnc, void *data) {
+    return ucx_map_clone_a(ucx_default_allocator(), map, fnc, data);
+}
+
+UcxMap *ucx_map_clone_a(UcxAllocator *allocator,
+        UcxMap const *map, copy_func fnc, void *data) {
     size_t bs = (map->count * 5) >> 1;
-    UcxMap *newmap = ucx_map_new(bs > map->size ? bs : map->size);
+    UcxMap *newmap = ucx_map_new_a(allocator, bs > map->size ? bs : map->size);
     if (!newmap) {
         return NULL;
     }
@@ -235,8 +240,8 @@
     return NULL;
 }
 
-void *ucx_map_get(UcxMap *map, UcxKey key) {
-    return ucx_map_get_and_remove(map, key, 0);
+void *ucx_map_get(UcxMap const *map, UcxKey key) {
+    return ucx_map_get_and_remove((UcxMap *)map, key, 0);
 }
 
 void *ucx_map_remove(UcxMap *map, UcxKey key) {
@@ -294,7 +299,7 @@
     return h;
 }
 
-UcxMapIterator ucx_map_iterator(UcxMap *map) {
+UcxMapIterator ucx_map_iterator(UcxMap const *map) {
     UcxMapIterator i;
     i.map = map;
     i.cur = NULL;
@@ -335,3 +340,63 @@
     return 0;
 }
 
+UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second,
+                      copy_func cpfnc, void* cpdata) {
+    return ucx_map_union_a(ucx_default_allocator(),
+            first, second, cpfnc, cpdata);
+}
+
+UcxMap* ucx_map_union_a(UcxAllocator *allocator,
+                        const UcxMap *first, const UcxMap *second,
+                        copy_func cpfnc, void* cpdata) {
+    UcxMap* result = ucx_map_clone_a(allocator, first, cpfnc, cpdata);
+    ucx_map_copy(second, result, cpfnc, cpdata);
+    return result;
+}
+
+UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second,
+                             copy_func cpfnc, void* cpdata) {
+    return ucx_map_intersection_a(ucx_default_allocator(),
+            first, second, cpfnc, cpdata);
+}
+
+UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
+                               const UcxMap *first, const UcxMap *second,
+                               copy_func cpfnc, void* cpdata) {
+    UcxMap *result = ucx_map_new_a(allocator, first->size < second->size ?
+            first->size : second->size);
+
+    UcxMapIterator iter = ucx_map_iterator(first);
+    void* value;
+    UCX_MAP_FOREACH(key, value, iter) {
+        if (ucx_map_get(second, key)) {
+            ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
+        }
+    }
+
+    return result;
+}
+
+UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second,
+                           copy_func cpfnc, void* cpdata) {
+    return ucx_map_difference_a(ucx_default_allocator(),
+            first, second, cpfnc, cpdata);
+}
+
+UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
+                             const UcxMap *first, const UcxMap *second,
+                             copy_func cpfnc, void* cpdata) {
+
+    UcxMap *result = ucx_map_new_a(allocator, first->size - second->count);
+
+    UcxMapIterator iter = ucx_map_iterator(first);
+    void* value;
+    UCX_MAP_FOREACH(key, value, iter) {
+        if (!ucx_map_get(second, key)) {
+            ucx_map_put(result, key, cpfnc ? cpfnc(value, cpdata) : value);
+        }
+    }
+
+    ucx_map_rehash(result);
+    return result;
+}
\ No newline at end of file
--- a/ucx/string.c	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/string.c	Sat Dec 05 11:54:58 2020 +0100
@@ -36,6 +36,10 @@
 #include <stdint.h>
 #include <ctype.h>
 
+#ifndef _WIN32
+#include <strings.h> /* for strncasecmp() */
+#endif /* _WIN32 */
+
 sstr_t sstr(char *cstring) {
     sstr_t string;
     string.ptr = cstring;
@@ -66,6 +70,8 @@
 
 
 size_t scstrnlen(size_t n, ...) {
+    if (n == 0) return 0;
+    
     va_list ap;
     va_start(ap, n);
     
@@ -592,6 +598,38 @@
     }
 }
 
+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++) {
@@ -624,6 +662,136 @@
     return ret;
 }
 
+#define REPLACE_INDEX_BUFFER_MAX 100
+
+struct scstrreplace_ibuf {
+    size_t* buf;
+    unsigned int len; /* small indices */
+    struct scstrreplace_ibuf* next;
+};
+
+static void scstrrepl_free_ibuf(struct scstrreplace_ibuf *buf) {
+    while (buf) {
+        struct scstrreplace_ibuf *next = buf->next;
+        free(buf->buf);
+        free(buf);
+        buf = next;
+    }
+}
+
+sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str,
+                     scstr_t pattern, scstr_t replacement, size_t replmax) {
+
+    if (pattern.length == 0 || pattern.length > str.length || replmax == 0)
+        return sstrdup(str);
+
+    /* Compute expected buffer length */
+    size_t ibufmax = str.length / pattern.length;
+    size_t ibuflen = replmax < ibufmax ? replmax : ibufmax;
+    if (ibuflen > REPLACE_INDEX_BUFFER_MAX) {
+        ibuflen = REPLACE_INDEX_BUFFER_MAX;
+    }
+
+    /* Allocate first index buffer */
+    struct scstrreplace_ibuf *firstbuf, *curbuf;
+    firstbuf = curbuf = calloc(1, sizeof(struct scstrreplace_ibuf));
+    if (!firstbuf) return sstrn(NULL, 0);
+    firstbuf->buf = calloc(ibuflen, sizeof(size_t));
+    if (!firstbuf->buf) {
+        free(firstbuf);
+        return sstrn(NULL, 0);
+    }
+
+    /* Search occurrences */
+    scstr_t searchstr = str;
+    size_t found = 0;
+    do {
+        scstr_t match = scstrscstr(searchstr, pattern);
+        if (match.length > 0) {
+            /* Allocate next buffer in chain, if required */
+            if (curbuf->len == ibuflen) {
+                struct scstrreplace_ibuf *nextbuf =
+                        calloc(1, sizeof(struct scstrreplace_ibuf));
+                if (!nextbuf) {
+                    scstrrepl_free_ibuf(firstbuf);
+                    return sstrn(NULL, 0);
+                }
+                nextbuf->buf = calloc(ibuflen, sizeof(size_t));
+                if (!nextbuf->buf) {
+                    free(nextbuf);
+                    scstrrepl_free_ibuf(firstbuf);
+                    return sstrn(NULL, 0);
+                }
+                curbuf->next = nextbuf;
+                curbuf = nextbuf;
+            }
+
+            /* Record match index */
+            found++;
+            size_t idx = match.ptr - str.ptr;
+            curbuf->buf[curbuf->len++] = idx;
+            searchstr.ptr = match.ptr + pattern.length;
+            searchstr.length = str.length - idx - pattern.length;
+        } else {
+            break;
+        }
+    } while (searchstr.length > 0 && found < replmax);
+
+    /* Allocate result string */
+    sstr_t result;
+    {
+        ssize_t adjlen = (ssize_t) replacement.length - (ssize_t) pattern.length;
+        size_t rcount = 0;
+        curbuf = firstbuf;
+        do {
+            rcount += curbuf->len;
+            curbuf = curbuf->next;
+        } while (curbuf);
+        result.length = str.length + rcount * adjlen;
+        result.ptr = almalloc(allocator, result.length);
+        if (!result.ptr) {
+            scstrrepl_free_ibuf(firstbuf);
+            return sstrn(NULL, 0);
+        }
+    }
+
+    /* Build result string */
+    curbuf = firstbuf;
+    size_t srcidx = 0;
+    char* destptr = result.ptr;
+    do {
+        for (size_t i = 0; i < curbuf->len; i++) {
+            /* Copy source part up to next match*/
+            size_t idx = curbuf->buf[i];
+            size_t srclen = idx - srcidx;
+            if (srclen > 0) {
+                memcpy(destptr, str.ptr+srcidx, srclen);
+                destptr += srclen;
+                srcidx += srclen;
+            }
+
+            /* Copy the replacement and skip the source pattern */
+            srcidx += pattern.length;
+            memcpy(destptr, replacement.ptr, replacement.length);
+            destptr += replacement.length;
+        }
+        curbuf = curbuf->next;
+    } while (curbuf);
+    memcpy(destptr, str.ptr+srcidx, str.length-srcidx);
+
+    /* Free index buffer */
+    scstrrepl_free_ibuf(firstbuf);
+
+    return result;
+}
+
+sstr_t scstrreplacen(scstr_t str, scstr_t pattern,
+        scstr_t replacement, size_t replmax) {
+    return scstrreplacen_a(ucx_default_allocator(),
+            str, pattern, replacement, replmax);
+}
+
+
 // type adjustment functions
 scstr_t ucx_sc2sc(scstr_t str) {
     return str;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ucx/ucx/array.h	Sat Dec 05 11:54:58 2020 +0100
@@ -0,0 +1,460 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Dynamically allocated array implementation.
+ * 
+ * @file   array.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_ARRAY_H
+#define	UCX_ARRAY_H
+
+#include "ucx.h"
+#include "allocator.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * UCX array type.
+ */
+typedef struct {
+    /**
+     * The current capacity of the array.
+     */
+    size_t capacity;
+    /**
+     * The actual number of elements in the array.
+     */
+    size_t size;
+    /**
+     * The size of an individual element in bytes.
+     */
+    size_t elemsize;
+    /**
+     * A pointer to the data.
+     */
+    void* data;
+    /**
+     * The allocator used for the data.
+     */
+    UcxAllocator* allocator;
+} UcxArray;
+
+/**
+ * Sets an element in an arbitrary user defined array.
+ * The data is copied from the specified data location.
+ * 
+ * If the capacity is insufficient, the array is automatically reallocated and
+ * the possibly new pointer is stored in the <code>array</code> argument.
+ * 
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>.
+ *  
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param elmsize the size of each element
+ * @param idx the index of the element to set
+ * @param data a pointer to the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+#define ucx_array_util_set(array, capacity, elmsize, idx, data) \
+    ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \
+                         elmsize, idx, data)
+
+/**
+ * Sets an element in an arbitrary user defined array.
+ * The data is copied from the specified data location.
+ * 
+ * If the capacity is insufficient, the array is automatically reallocated
+ * using the specified allocator and the possibly new pointer is stored in
+ * the <code>array</code> argument.
+ * 
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>. 
+ * 
+ * @param alloc the allocator that shall be used to reallocate the array
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param elmsize the size of each element
+ * @param idx the index of the element to set
+ * @param data a pointer to the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
+    size_t elmsize, size_t idx, void* data);
+
+/**
+ * Stores a pointer in an arbitrary user defined array.
+ * The element size of the array must be sizeof(void*).
+ * 
+ * If the capacity is insufficient, the array is automatically reallocated and
+ * the possibly new pointer is stored in the <code>array</code> argument.
+ * 
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>.
+ *  
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param idx the index of the element to set
+ * @param ptr the pointer to store
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+#define ucx_array_util_setptr(array, capacity, idx, ptr) \
+    ucx_array_util_setptr_a(ucx_default_allocator(), (void**)(array), \
+                            capacity, idx, ptr)
+
+/**
+ * Stores a pointer in an arbitrary user defined array.
+ * The element size of the array must be sizeof(void*).
+ * 
+ * If the capacity is insufficient, the array is automatically reallocated
+ * using the specified allocator and the possibly new pointer is stored in
+ * the <code>array</code> argument.
+ * 
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>. 
+ * 
+ * @param alloc the allocator that shall be used to reallocate the array
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param idx the index of the element to set
+ * @param ptr the pointer to store
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
+    size_t idx, void* ptr);
+
+
+/**
+ * Creates a new UCX array with the given capacity and element size.
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @return a pointer to a new UCX array structure
+ */
+UcxArray* ucx_array_new(size_t capacity, size_t elemsize);
+
+/**
+ * Creates a new UCX array using the specified allocator.
+ * 
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @param allocator the allocator to use
+ * @return a pointer to new UCX array structure
+ */
+UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
+        UcxAllocator* allocator);
+
+/**
+ * Initializes a UCX array structure with the given capacity and element size.
+ * The structure must be uninitialized as the data pointer will be overwritten.
+ * 
+ * @param array the structure to initialize
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ */
+void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize);
+
+/**
+ * Initializes a UCX array structure using the specified allocator.
+ * The structure must be uninitialized as the data pointer will be overwritten.
+ * 
+ * @param array the structure to initialize
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @param allocator the allocator to use
+ */
+void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
+        UcxAllocator* allocator);
+
+/**
+ * Creates an shallow copy of an array.
+ * 
+ * This function clones the specified array by using memcpy().
+ * If the destination capacity is insufficient, an automatic reallocation is
+ * attempted.
+ * 
+ * Note: if the destination array is uninitialized, the behavior is undefined.
+ * 
+ * @param dest the array to copy to
+ * @param src the array to copy from
+ * @return zero on success, non-zero on reallocation failure.
+ */
+int ucx_array_clone(UcxArray* dest, UcxArray const* src);
+
+
+/**
+ * Compares two UCX arrays element-wise by using a compare function.
+ *
+ * Elements of the two specified arrays are compared by using the specified
+ * compare function and the additional data. The type and content of this
+ * additional data depends on the cmp_func() used.
+ * 
+ * This function always returns zero, if the element sizes of the arrays do
+ * not match and performs no comparisons in this case.
+ * 
+ * @param array1 the first array
+ * @param array2 the second array
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the two arrays equal element-wise, 0 otherwise
+ */
+int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
+        cmp_func cmpfnc, void* data);
+
+/**
+ * Destroys the array.
+ * 
+ * The data is freed and both capacity and count are reset to zero.
+ * If the array structure itself has been dynamically allocated, it has to be
+ * freed separately.
+ * 
+ * @param array the array to destroy
+ */
+void ucx_array_destroy(UcxArray *array);
+
+/**
+ * Destroys and frees the array.
+ * 
+ * @param array the array to free
+ */
+void ucx_array_free(UcxArray *array);
+
+/**
+ * Inserts elements at the end of the array.
+ * 
+ * This is an O(1) operation.
+ * The array will automatically grow, if the capacity is exceeded.
+ * If a pointer to data is provided, the data is copied into the array with
+ * memcpy(). Otherwise the new elements are completely zeroed.
+ * 
+ * @param array a pointer the array where to append the data
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, zeroed elements are appended)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_set_from()
+ * @see ucx_array_append()
+ */
+int ucx_array_append_from(UcxArray *array, void *data, size_t count);
+
+
+/**
+ * Inserts elements at the beginning of the array.
+ * 
+ * This is an expensive operation, because the contents must be moved.
+ * If there is no particular reason to prepend data, you should use
+ * ucx_array_append_from() instead.
+ * 
+ * @param array a pointer the array where to prepend the data
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, zeroed elements are inserted)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append_from()
+ * @see ucx_array_set_from()
+ * @see ucx_array_prepend()
+ */
+int ucx_array_prepend_from(UcxArray *array, void *data, size_t count);
+
+
+/**
+ * Sets elements starting at the specified index.
+ * 
+ * If the any index is out of bounds, the array automatically grows.
+ * The pointer to the data may be NULL, in which case the elements are zeroed. 
+ * 
+ * @param array a pointer the array where to set the data
+ * @param index the index of the element to set
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, the memory in the array is zeroed)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append_from()
+ * @see ucx_array_set()
+ */
+int ucx_array_set_from(UcxArray *array, size_t index, void *data, size_t count);
+
+/**
+ * Concatenates two arrays.
+ * 
+ * The contents of the second array are appended to the first array in one
+ * single operation. The second array is otherwise left untouched.
+ * 
+ * The first array may grow automatically. If this fails, both arrays remain
+ * unmodified.
+ * 
+ * @param array1 first array
+ * @param array2 second array
+ * @return zero on success, non-zero if reallocation was necessary but failed 
+ * or the element size does not match
+ */
+int ucx_array_concat(UcxArray *array1, const UcxArray *array2);
+
+/**
+ * Returns a pointer to the array element at the specified index.
+ * 
+ * @param array the array to retrieve the element from
+ * @param index index of the element to return
+ * @return a pointer to the element at the specified index or <code>NULL</code>,
+ * if the index is greater than the array size
+ */
+void *ucx_array_at(UcxArray const* array, size_t index);
+
+/**
+ * Returns the index of an element containing the specified data.
+ *
+ * This function uses a cmp_func() to compare the data of each list element
+ * with the specified data. If no cmp_func is provided, memcmp() is used.
+ * 
+ * If the array contains the data more than once, the index of the first
+ * occurrence is returned.
+ * If the array does not contain the data, the size of array is returned.
+ *  
+ * @param array the array where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return the index of the element containing the specified data or the size of
+ * the array, if the data is not found in this array
+ */
+size_t ucx_array_find(UcxArray const *array, void *elem,
+    cmp_func cmpfnc, void *data);
+
+/**
+ * Checks, if an array contains a specific element.
+ * 
+ * An element is found, if ucx_array_find() returns a value less than the size.
+ * 
+ * @param array the array where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the array contains the specified element data
+ * @see ucx_array_find()
+ */
+int ucx_array_contains(UcxArray const *array, void *elem,
+    cmp_func cmpfnc, void *data);
+
+/**
+ * Sorts a UcxArray with the best available sort algorithm.
+ * 
+ * The qsort_r() function is used, if available (glibc, FreeBSD or MacOS).
+ * The order of arguments is automatically adjusted for the FreeBSD and MacOS
+ * version of qsort_r().
+ * 
+ * If qsort_r() is not available, a merge sort algorithm is used, which is
+ * guaranteed to use no more additional memory than for exactly one element.
+ * 
+ * @param array the array to sort
+ * @param cmpfnc the function that shall be used to compare the element data
+ * @param data additional data for the cmp_func() or <code>NULL</code>
+ */
+void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data);
+
+/**
+ * Removes an element from the array.
+ * 
+ * This is in general an expensive operation, because several elements may
+ * be moved. If the order of the elements is not relevant, use
+ * ucx_array_remove_fast() instead.
+ * 
+ * @param array pointer to the array from which the element shall be removed
+ * @param index the index of the element to remove
+ */
+void ucx_array_remove(UcxArray *array, size_t index);
+
+/**
+ * Removes an element from the array.
+ * 
+ * This is an O(1) operation, but does not maintain the order of the elements.
+ * The last element in the array is moved to the location of the removed
+ * element.
+ * 
+ * @param array pointer to the array from which the element shall be removed
+ * @param index the index of the element to remove
+ */
+void ucx_array_remove_fast(UcxArray *array, size_t index);
+
+/**
+ * Shrinks the memory to exactly fit the contents.
+ * 
+ * After this operation, the capacity equals the size.
+ * 
+ * @param array a pointer to the array
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_shrink(UcxArray* array);
+
+/**
+ * Sets the capacity of the array.
+ * 
+ * If the new capacity is smaller than the size of the array, the elements
+ * are removed and the size is adjusted accordingly.
+ * 
+ * @param array a pointer to the array
+ * @param capacity the new capacity
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_resize(UcxArray* array, size_t capacity);
+
+/**
+ * Resizes the array only, if the capacity is insufficient.
+ * 
+ * If the requested capacity is smaller than the current capacity, this
+ * function does nothing.
+ * 
+ * @param array a pointer to the array
+ * @param capacity the guaranteed capacity
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_reserve(UcxArray* array, size_t capacity);
+
+/**
+ * Resizes the capacity, if the specified number of elements would not fit.
+ * 
+ * A call to ucx_array_grow(array, count) is effectively the same as
+ * ucx_array_reserve(array, array->size+count).
+ * 
+ * @param array a pointer to the array
+ * @param count the number of elements that should additionally fit
+ * into the array
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_grow(UcxArray* array, size_t count);
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_ARRAY_H */
+
--- a/ucx/ucx/list.h	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/ucx/list.h	Sat Dec 05 11:54:58 2020 +0100
@@ -57,7 +57,7 @@
  * @param elem The variable name of the element
  */
 #define UCX_FOREACH(elem,list) \
-        for (UcxList* elem = list ; elem != NULL ; elem = elem->next)
+        for (UcxList* elem = (UcxList*) list ; elem != NULL ; elem = elem->next)
 
 /**
  * UCX list type.
@@ -99,7 +99,7 @@
  * @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);
+UcxList *ucx_list_clone(const UcxList *list, copy_func cpyfnc, void* data);
 
 /**
  * Creates an element-wise copy of a list using a UcxAllocator.
@@ -117,7 +117,7 @@
  * @return a pointer to the copy
  * @see ucx_list_clone()
  */
-UcxList *ucx_list_clone_a(UcxAllocator *allocator, UcxList *list,
+UcxList *ucx_list_clone_a(UcxAllocator *allocator, const UcxList *list,
         copy_func cpyfnc, void* data);
 
 /**
@@ -328,7 +328,8 @@
  * @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);
+ssize_t ucx_list_find(const UcxList *list, void *elem,
+    cmp_func cmpfnc, void *data);
 
 /**
  * Checks, if a list contains a specific element.
@@ -342,7 +343,8 @@
  * @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);
+int ucx_list_contains(const UcxList *list, void *elem,
+    cmp_func cmpfnc, void *data);
 
 /**
  * Sorts a UcxList with natural merge sort.
@@ -388,6 +390,120 @@
 UcxList *ucx_list_remove_a(UcxAllocator *allocator, UcxList *list,
         UcxList *element);
 
+/**
+ * Returns the union of two lists.
+ * 
+ * The union is a list of unique elements regarding cmpfnc obtained from
+ * both source lists.
+ * 
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the union
+ */
+UcxList* ucx_list_union(const UcxList *left, const UcxList *right,
+    cmp_func cmpfnc, void* cmpdata,
+    copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the union of two lists.
+ * 
+ * The union is a list of unique elements regarding cmpfnc obtained from
+ * both source lists.
+ * 
+ * @param allocator allocates the new list elements
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the union
+ */
+UcxList* ucx_list_union_a(UcxAllocator *allocator,
+    const UcxList *left, const UcxList *right,
+    cmp_func cmpfnc, void* cmpdata,
+    copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the intersection of two lists.
+ * 
+ * The intersection contains all elements of the left list
+ * (including duplicates) that can be found in the right list.
+ * 
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the intersection
+ */
+UcxList* ucx_list_intersection(const UcxList *left, const UcxList *right,
+    cmp_func cmpfnc, void* cmpdata,
+    copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the intersection of two lists.
+ * 
+ * The intersection contains all elements of the left list
+ * (including duplicates) that can be found in the right list.
+ * 
+ * @param allocator allocates the new list elements
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the intersection
+ */
+UcxList* ucx_list_intersection_a(UcxAllocator *allocator,
+    const UcxList *left, const UcxList *right,
+    cmp_func cmpfnc, void* cmpdata,
+    copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the difference of two lists.
+ * 
+ * The difference contains all elements of the left list
+ * (including duplicates) that are not equal to any element of the right list.
+ * 
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the difference
+ */
+UcxList* ucx_list_difference(const UcxList *left, const UcxList *right,
+    cmp_func cmpfnc, void* cmpdata,
+    copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the difference of two lists.
+ * 
+ * The difference contains all elements of the left list
+ * (including duplicates) that are not equal to any element of the right list.
+ * 
+ * @param allocator allocates the new list elements
+ * @param left the left source list
+ * @param right the right source list
+ * @param cmpfnc a function to compare elements
+ * @param cmpdata additional data for the compare function
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the difference
+ */
+UcxList* ucx_list_difference_a(UcxAllocator *allocator,
+    const UcxList *left, const UcxList *right,
+    cmp_func cmpfnc, void* cmpdata,
+    copy_func cpfnc, void* cpdata);
+
 #ifdef	__cplusplus
 }
 #endif
--- a/ucx/ucx/logging.h	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/ucx/logging.h	Sat Dec 05 11:54:58 2020 +0100
@@ -160,7 +160,10 @@
  * format is:
  * 
  * <code>[LEVEL] [TIMESTAMP] [SOURCEFILE]:[LINENO] message</code>
- * 
+ *
+ * The source file name is reduced to the actual file name. This is necessary to
+ * get consistent behavior over different definitions of the __FILE__ macro.
+ *
  * <b>Attention:</b> the message (including automatically generated information)
  * is limited to 4096 characters. The level description is limited to
  * 256 characters and the timestamp string is limited to 128 characters.
--- a/ucx/ucx/map.h	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/ucx/map.h	Sat Dec 05 11:54:58 2020 +0100
@@ -124,7 +124,7 @@
 /** Structure for an iterator over a UcxMap. */
 struct UcxMapIterator {
     /** The map to iterate over. */
-    UcxMap        *map;
+    UcxMap const  *map;
     
     /** The current map element. */
     UcxMapElement *cur;
@@ -211,7 +211,7 @@
  * @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);
+int ucx_map_copy(UcxMap const *from, UcxMap *to, copy_func fnc, void *data);
 
 /**
  * Clones the map and rehashes if necessary.
@@ -227,7 +227,25 @@
  * @return the cloned map
  * @see ucx_map_copy()
  */
-UcxMap *ucx_map_clone(UcxMap *map, copy_func fnc, void *data);
+UcxMap *ucx_map_clone(UcxMap const *map, copy_func fnc, void *data);
+
+/**
+ * Clones the map and rehashes if necessary.
+ *
+ * <b>Note:</b> In contrast to ucx_map_rehash() the load factor is irrelevant.
+ * This function <i>always</i> ensures a new UcxMap.size of at least
+ * 2.5*UcxMap.count.
+ *
+ * @param allocator the allocator to use for the cloned map
+ * @param map the map to clone
+ * @param fnc the copy function to use or <code>NULL</code> if the new and
+ * the old map shall share the data pointers
+ * @param data additional data for the copy function
+ * @return the cloned map
+ * @see ucx_map_copy()
+ */
+UcxMap *ucx_map_clone_a(UcxAllocator *allocator,
+                        UcxMap const *map, copy_func fnc, void *data);
 
 /**
  * Increases size of the hash map, if necessary.
@@ -264,7 +282,7 @@
  * @param key the key
  * @return the value
  */
-void* ucx_map_get(UcxMap *map, UcxKey key);
+void* ucx_map_get(UcxMap const *map, UcxKey key);
 
 /**
  * Removes a key/value-pair from the map by using the key.
@@ -406,7 +424,7 @@
  * first element list
  * @see ucx_map_iter_next()
  */
-UcxMapIterator ucx_map_iterator(UcxMap *map);
+UcxMapIterator ucx_map_iterator(UcxMap const *map);
 
 /**
  * Proceeds to the next element of the map (if any).
@@ -426,6 +444,102 @@
  */
 int ucx_map_iter_next(UcxMapIterator *iterator, UcxKey *key, void **value);
 
+/**
+ * Returns the union of two maps.
+ *
+ * The union is a fresh map which is filled by two successive calls of
+ * ucx_map_copy() on the two input maps.
+ *
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new map containing the union
+ */
+UcxMap* ucx_map_union(const UcxMap *first, const UcxMap *second,
+                      copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the union of two maps.
+ *
+ * The union is a fresh map which is filled by two successive calls of
+ * ucx_map_copy() on the two input maps.
+ *
+ * @param allocator the allocator that shall be used by the new map
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new map containing the union
+ */
+UcxMap* ucx_map_union_a(UcxAllocator *allocator,
+                        const UcxMap *first, const UcxMap *second,
+                        copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the intersection of two maps.
+ *
+ * The intersection is defined as a copy of the first map with every element
+ * removed that has no valid key in the second map.
+ *
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new map containing the intersection
+ */
+UcxMap* ucx_map_intersection(const UcxMap *first, const UcxMap *second,
+                             copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the intersection of two maps.
+ *
+ * The intersection is defined as a copy of the first map with every element
+ * removed that has no valid key in the second map.
+ *
+ * @param allocator the allocator that shall be used by the new map
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new map containing the intersection
+ */
+UcxMap* ucx_map_intersection_a(UcxAllocator *allocator,
+                               const UcxMap *first, const UcxMap *second,
+                               copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the difference of two maps.
+ *
+ * The difference contains a copy of all elements of the first map
+ * for which the corresponding keys cannot be found in the second map.
+ *
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the difference
+ */
+UcxMap* ucx_map_difference(const UcxMap *first, const UcxMap *second,
+                           copy_func cpfnc, void* cpdata);
+
+/**
+ * Returns the difference of two maps.
+ *
+ * The difference contains a copy of all elements of the first map
+ * for which the corresponding keys cannot be found in the second map.
+ *
+ * @param allocator the allocator that shall be used by the new map
+ * @param first the first source map
+ * @param second the second source map
+ * @param cpfnc a function to copy the elements
+ * @param cpdata additional data for the copy function
+ * @return a new list containing the difference
+ */
+UcxMap* ucx_map_difference_a(UcxAllocator *allocator,
+                             const UcxMap *first, const UcxMap *second,
+                             copy_func cpfnc, void* cpdata);
+
 
 #ifdef	__cplusplus
 }
--- a/ucx/ucx/string.h	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/ucx/string.h	Sat Dec 05 11:54:58 2020 +0100
@@ -83,6 +83,7 @@
 #ifdef	__cplusplus
 extern "C" {
 #endif
+  
 /**
  * The UCX string structure.
  */
@@ -112,7 +113,7 @@
 
 #ifdef __cplusplus
 /**
- * One of two type adjustment functions that return a scstr_t.
+ * 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.
  * 
@@ -129,7 +130,7 @@
 }
 
 /**
- * One of two type adjustment functions that return a scstr_t.
+ * 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
@@ -147,13 +148,13 @@
 /**
  * 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
+ * @return an immutable version of the provided string
  */
 #define SCSTR(s) s2scstr(s)
 #else
 
 /**
- * One of two type adjustment functions that return a scstr_t.
+ * 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
@@ -167,7 +168,7 @@
 scstr_t ucx_sc2sc(scstr_t str);
 
 /**
- * One of two type adjustment functions that return a scstr_t.
+ * 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.
  * 
@@ -182,7 +183,7 @@
 /**
  * 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
+ * @return an immutable version of the provided string
  */
 #define SCSTR(str) _Generic(str, sstr_t: ucx_ss2sc, scstr_t: ucx_sc2sc)(str)
 
@@ -191,7 +192,7 @@
 /**
  * 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
+ * @return an immutable version of the provided string
  */
 #define SCSTR(str) __builtin_choose_expr( \
         __builtin_types_compatible_p(typeof(str), sstr_t), \
@@ -244,8 +245,8 @@
  * 
  * 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.
+ * <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().
  * 
@@ -259,8 +260,8 @@
 /**
  * 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.
+ * <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().
  * 
@@ -278,8 +279,8 @@
  * 
  * The length is implicitly inferred by using a call to <code>strlen()</code>.
  *
- * <b>Note:</b> the scstr_t will hold a <i>reference</i> to the C string. If you
- * do want a copy, use scstrdup() on the return value of this function.
+ * <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
@@ -292,9 +293,8 @@
 /**
  * Creates a new scstr_t of the specified length based on a constant C string.
  *
- * <b>Note:</b> the scstr_t will hold a <i>reference</i> to the C string. If you
- * do want a copy, use scstrdup() on the return value of this function.
- * 
+ * <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
@@ -305,21 +305,24 @@
 scstr_t scstrn(const char *cstring, size_t length);
 
 /**
- * Returns the cumulated length of all specified strings.
+ * Returns the accumulated length of all specified strings.
  * 
- * <b>Attention:</b> if the count argument does not match the count of the
+ * <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 (so at least 1)
+ * @param count    the total number of specified strings
  * @param ...      all strings
- * @return the cumulated length of all strings
+ * @return the accumulated length of all strings
  */
 size_t scstrnlen(size_t count, ...);
 
 /**
- * Alias for scstrnlen() which automatically converts the arguments.
+ * Returns the accumulated length of all specified strings.
  * 
- * @param count    the total number of specified strings (so at least 1)
+ * <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
  */
@@ -342,7 +345,13 @@
 sstr_t scstrcat(size_t count, scstr_t s1, ...);
 
 /**
- * Alias for scstrcat() which automatically converts the arguments.
+ * 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
@@ -354,35 +363,47 @@
 /**
  * Concatenates two or more strings using a UcxAllocator.
  * 
- * See scstrcat() for details.
+ * 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 a       the allocator to use
+ * @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 *a, size_t count, scstr_t s1, ...);
+sstr_t scstrcat_a(UcxAllocator *alloc, size_t count, scstr_t s1, ...);
 
 /**
- * Alias for scstrcat_a() which automatically converts the arguments.
+ * Concatenates two or more strings using a UcxAllocator.
+ * 
+ * The resulting string must be freed by the allocators <code>free()</code>
+ * implementation.
  * 
- * See sstrcat() for details.
+ * The sstr_t.ptr of the return value will <i>always</i> be <code>NULL</code>-
+ * terminated.
  *
- * @param a       the allocator to use
+ * @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(a, count, s1, ...) \
-    scstrcat_a(a, count, SCSTR(s1), __VA_ARGS__)
+#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 will <b>NOT</b> be <code>NULL</code>-terminated.
+ * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
  * Use sstrdup() to get a copy.
  * 
  * @param string input string
@@ -395,10 +416,10 @@
 sstr_t sstrsubs(sstr_t string, size_t start);
 
 /**
- * Returns a substring with a maximum length starting at the specified location.
+ * 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 will <b>NOT</b> be <code>NULL</code>-terminated.
+ * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
  * Use sstrdup() to get a copy.
  * 
  * @param string input string
@@ -417,7 +438,7 @@
  * 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.
+* input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
  * Use scstrdup() to get a copy.
  * 
  * @param string input string
@@ -434,7 +455,7 @@
  * 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.
+ * input string and is <b>NOT</b> required to be <code>NULL</code>-terminated.
  * Use scstrdup() to get a copy.
  * 
  * @param string input string
@@ -522,7 +543,13 @@
 sstr_t scstrsstr(sstr_t string, scstr_t match);
 
 /**
- * Alias for scstrsstr() which automatically converts the match string.
+ * 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
@@ -550,7 +577,13 @@
 scstr_t scstrscstr(scstr_t string, scstr_t match);
 
 /**
- * Alias for scstrscstr() which automatically converts the match string.
+ * 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
@@ -595,6 +628,55 @@
  * 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.
  *
@@ -605,20 +687,6 @@
  * @return a sstr_t array containing the split strings or
  * <code>NULL</code> on error
  * 
- * @see scstrsplit_a()
- */
-sstr_t* scstrsplit(scstr_t string, scstr_t delim, ssize_t *count);
-
-/**
- * Alias for scstrsplit() which automatically converts the arguments.
- * 
- * @param string the string to split
- * @param delim  the delimiter string
- * @param count  IN: the maximum size of the resulting array (0 = no limit),
- *               OUT: the actual size of the array
- * @return a sstr_t array containing the split strings or
- * <code>NULL</code> on error
- * 
  * @see sstrsplit_a()
  */
 #define sstrsplit(string, delim, count) \
@@ -633,9 +701,6 @@
  * 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
@@ -650,7 +715,13 @@
         ssize_t *count);
 
 /**
- * Alias for scstrsplit_a() which automatically converts the arguments.
+ * 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
@@ -680,7 +751,10 @@
 int scstrcmp(scstr_t s1, scstr_t s2);
 
 /**
- * Alias for scstrcmp() which automatically converts its arguments.
+ * 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
@@ -706,7 +780,11 @@
 int scstrcasecmp(scstr_t s1, scstr_t s2);
 
 /**
- * Alias for scstrcasecmp() which automatically converts the arguments.
+ * 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
@@ -733,7 +811,14 @@
 sstr_t scstrdup(scstr_t string);
 
 /**
- * Alias for scstrdup() which automatically converts the argument.
+ * 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
@@ -760,7 +845,15 @@
 sstr_t scstrdup_a(UcxAllocator *allocator, scstr_t string);
 
 /**
- * Alias for scstrdup_a() which automatically converts the argument.
+ * 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
@@ -810,6 +903,7 @@
 
 /**
  * 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
@@ -817,7 +911,7 @@
 int scstrprefix(scstr_t string, scstr_t prefix);
 
 /**
- * Alias for scstrprefix() which automatically converts the arguments.
+ * Checks, if a string has a specific prefix.
  * 
  * @param string the string to check
  * @param prefix the prefix the string should have
@@ -827,6 +921,7 @@
 
 /**
  * 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
@@ -834,7 +929,7 @@
 int scstrsuffix(scstr_t string, scstr_t suffix);
 
 /**
- * Alias for scstrsuffix() which automatically converts the arguments.
+ * Checks, if a string has a specific suffix.
  *
  * @param string the string to check
  * @param suffix the suffix the string should have
@@ -843,10 +938,48 @@
 #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 the
- * documentation of scstrdup() for the implications.
+ * This function creates a duplicate of the input string, first
+ * (see scstrdup()).
  * 
  * @param string the input string
  * @return the resulting lower case string
@@ -855,7 +988,10 @@
 sstr_t scstrlower(scstr_t string);
 
 /**
- * Alias for scstrlower() which automatically converts the argument.
+ * 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
@@ -865,8 +1001,8 @@
 /**
  * Returns a lower case version of a string.
  * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of scstrdup_a() for the implications.
+  * 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
@@ -877,7 +1013,10 @@
 
 
 /**
- * Alias for scstrlower_a() which automatically converts the argument.
+ * 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
@@ -888,8 +1027,8 @@
 /**
  * Returns a upper case version of a string.
  * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of scstrdup() for the implications.
+ * This function creates a duplicate of the input string, first
+ * (see scstrdup()).
  * 
  * @param string the input string
  * @return the resulting upper case string
@@ -898,7 +1037,10 @@
 sstr_t scstrupper(scstr_t string);
 
 /**
- * Alias for scstrupper() which automatically converts the argument.
+ * 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
@@ -908,8 +1050,8 @@
 /**
  * Returns a upper case version of a string.
  * 
- * This function creates a duplicate of the input string, first. See the
- * documentation of scstrdup_a() for the implications.
+ * 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
@@ -919,7 +1061,10 @@
 sstr_t scstrupper_a(UcxAllocator *allocator, scstr_t string);
 
 /**
- * Alias for scstrupper_a() which automatically converts the argument.
+ * 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
@@ -927,6 +1072,128 @@
  */
 #define sstrupper_a(allocator, string) scstrupper_a(allocator, string)
 
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The resulting string is allocated by the specified allocator. I.e. it
+ * depends on the used allocator, whether the sstr_t.ptr must be freed
+ * manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+sstr_t scstrreplacen_a(UcxAllocator *allocator, scstr_t str,
+        scstr_t pattern, scstr_t replacement, size_t replmax);
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The sstr_t.ptr of the resulting string must be freed manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+sstr_t scstrreplacen(scstr_t str, scstr_t pattern,
+        scstr_t replacement, size_t replmax);
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The resulting string is allocated by the specified allocator. I.e. it
+ * depends on the used allocator, whether the sstr_t.ptr must be freed
+ * manually.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+#define sstrreplacen_a(allocator, str, pattern, replacement, replmax) \
+        scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \
+            SCSTR(replacement), replmax)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The sstr_t.ptr of the resulting string must be freed manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @param replmax maximum number of replacements
+ * @return the resulting string after applying the replacements
+ */
+#define sstrreplacen(str, pattern, replacement, replmax) \
+        scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), replmax)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The resulting string is allocated by the specified allocator. I.e. it
+ * depends on the used allocator, whether the sstr_t.ptr must be freed
+ * manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param allocator the allocator to use
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @return the resulting string after applying the replacements
+ */
+#define sstrreplace_a(allocator, str, pattern, replacement) \
+        scstrreplacen_a(allocator, SCSTR(str), SCSTR(pattern), \
+            SCSTR(replacement), SIZE_MAX)
+
+/**
+ * Replaces a pattern in a string with another string.
+ *
+ * The pattern is taken literally and is no regular expression.
+ * Replaces at most <code>replmax</code> occurrences.
+ *
+ * The sstr_t.ptr of the resulting string must be freed manually.
+ *
+ * If allocation fails, the sstr_t.ptr of the return value is NULL.
+ *
+ * @param str the string where replacements should be applied
+ * @param pattern the pattern to search for
+ * @param replacement the replacement string
+ * @return the resulting string after applying the replacements
+ */
+#define sstrreplace(str, pattern, replacement) \
+        scstrreplacen(SCSTR(str), SCSTR(pattern), SCSTR(replacement), SIZE_MAX)
+
 #ifdef	__cplusplus
 }
 #endif
--- a/ucx/ucx/ucx.h	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/ucx/ucx.h	Sat Dec 05 11:54:58 2020 +0100
@@ -40,7 +40,7 @@
 #define UCX_VERSION_MAJOR   2
 
 /** Minor UCX version as integer constant. */
-#define UCX_VERSION_MINOR   0
+#define UCX_VERSION_MINOR   1
 
 /** Version constant which ensures to increase monotonically. */
 #define UCX_VERSION (((UCX_VERSION_MAJOR)<<16)|UCX_VERSION_MINOR)
@@ -170,10 +170,7 @@
 
 /**
  * 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
@@ -183,6 +180,18 @@
  */
 #define ucx_szmul(a, b, result) ucx_szmul_impl(a, b, result)
 
+/**
+ * Performs a multiplication of size_t values and checks for overflow.
+ *
+ * This is a custom implementation in case there is no compiler builtin
+ * available.
+ *
+ * @param a first operand
+ * @param b second operand
+ * @param result a pointer to a size_t where the result should be stored
+ * @return zero, if no overflow occurred and the result is correct, non-zero
+ * otherwise
+ */
 int ucx_szmul_impl(size_t a, size_t b, size_t *result);
 
 #endif
--- a/ucx/ucx/utils.h	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/ucx/utils.h	Sat Dec 05 11:54:58 2020 +0100
@@ -184,6 +184,105 @@
  */
 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.
@@ -204,6 +303,96 @@
 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
--- a/ucx/utils.c	Sat Dec 05 10:34:10 2020 +0100
+++ b/ucx/utils.c	Sat Dec 05 11:54:58 2020 +0100
@@ -113,8 +113,108 @@
 }
 
 int ucx_cmp_longint(const void *i1, const void *i2, void *data) {
-   int a = *((const long int*) i1);
-   int b = *((const long int*) i2);
+   long int a = *((const long int*) i1);
+   long int b = *((const long int*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_longlong(const void *i1, const void *i2, void *data) {
+   long long a = *((const long long*) i1);
+   long long b = *((const long long*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_int16(const void *i1, const void *i2, void *data) {
+   int16_t a = *((const int16_t*) i1);
+   int16_t b = *((const int16_t*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_int32(const void *i1, const void *i2, void *data) {
+   int32_t a = *((const int32_t*) i1);
+   int32_t b = *((const int32_t*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_int64(const void *i1, const void *i2, void *data) {
+   int64_t a = *((const int64_t*) i1);
+   int64_t b = *((const int64_t*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_uint(const void *i1, const void *i2, void *data) {
+   unsigned int a = *((const unsigned int*) i1);
+   unsigned int b = *((const unsigned int*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_ulongint(const void *i1, const void *i2, void *data) {
+   unsigned long int a = *((const unsigned long int*) i1);
+   unsigned long int b = *((const unsigned long int*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_ulonglong(const void *i1, const void *i2, void *data) {
+   unsigned long long a = *((const unsigned long long*) i1);
+   unsigned long long b = *((const unsigned long long*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_uint16(const void *i1, const void *i2, void *data) {
+   uint16_t a = *((const uint16_t*) i1);
+   uint16_t b = *((const uint16_t*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_uint32(const void *i1, const void *i2, void *data) {
+   uint32_t a = *((const uint32_t*) i1);
+   uint32_t b = *((const uint32_t*) i2);
+   if (a == b) {
+       return 0;
+   } else {
+       return a < b ? -1 : 1;
+   }
+}
+
+int ucx_cmp_uint64(const void *i1, const void *i2, void *data) {
+   uint64_t a = *((const uint64_t*) i1);
+   uint64_t b = *((const uint64_t*) i2);
    if (a == b) {
        return 0;
    } else {
@@ -134,6 +234,66 @@
    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);
--- a/ui/common/context.h	Sat Dec 05 10:34:10 2020 +0100
+++ b/ui/common/context.h	Sat Dec 05 11:54:58 2020 +0100
@@ -59,14 +59,19 @@
     UiContext     *parent;
     UiObject      *obj;
     UcxMempool    *mempool;
-    UcxMap        *bound; // key: char*  value: UiVar*
-    UcxMap        *vars; // key: char*  value: UiVar*
-    void          *document;
+    
+    UcxMap        *bound; // key: char*  value: UiVar*       deprecated
+    UcxMap        *vars; // key: char*  value: UiVar*        deprecated
+    void          *document; //                              deprecated
+    
+    UcxMap        *vars2; // manually created context vars
+    UcxMap        *vars_unbound; // unbound vars created by widgets
+    
     UcxList       *groups; // int list
     UcxList       *group_widgets; // UiGroupWidget* list
     
-    void (*set_document)(UiContext *ctx, void *document);
-    void (*detach_document)(UiContext *ctx);
+    void (*set_document)(UiContext *ctx, void *document); // deprecated
+    void (*detach_document)(UiContext *ctx);              // deprecated    
     
     char          *title;
     
@@ -78,6 +83,7 @@
     void          *close_data;
 };
 
+// deprecated
 struct UiVar {
     void      *value;
     void      *orig_val;
@@ -85,6 +91,13 @@
     UiVar     *from;
 };
 
+// UiVar replacement, rename it to UiVar when finished
+struct UiVar2 {
+    void      *value;
+    UiVarType type;
+    UiContext *bound_from; /* bound by this context or NULL if unbound */
+};
+
 struct UiGroupWidget {
     UIWIDGET widget;
     int      *groups;
@@ -94,8 +107,8 @@
 
 UiContext* uic_context(UiObject *toplevel, UcxMempool *mp);
 UiContext* uic_root_context(UiContext *ctx);
-void uic_context_set_document(UiContext *ctx, void *document);
-void uic_context_detach_document(UiContext *ctx);
+void uic_context_set_document(UiContext *ctx, void *document); // deprecated
+void uic_context_detach_document(UiContext *ctx); // deprecated
 
 //UiVar* uic_get_var(UiContext *ctx, char *name);
 UiVar* uic_create_var(UiContext *ctx, char *name, UiVarType type);

mercurial