merges the UcxArray implementation

Sat, 05 Oct 2019 16:58:16 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 05 Oct 2019 16:58:16 +0200
changeset 360
fed2ead878ea
parent 351
87c22ec6a0fd (current diff)
parent 359
9f86bc73f96b (diff)
child 361
8ee9e23adbd2

merges the UcxArray implementation

--- a/configure.ac	Sat Aug 10 08:46:38 2019 +0200
+++ b/configure.ac	Sat Oct 05 16:58:16 2019 +0200
@@ -1,7 +1,7 @@
 #
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 #
-# Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+# Copyright 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:
@@ -28,8 +28,8 @@
 
 # the package version must match the macros in ucx.h
 # the lib version must follow the libtool versioning convention
-AC_INIT([ucx], [2.0.0], [olaf.wintermann@gmail.com])
-AC_SUBST([UCX_LIB_VERSION], [3:0:0])
+AC_INIT([ucx], [2.1.0], [olaf.wintermann@gmail.com])
+AC_SUBST([UCX_LIB_VERSION], [4:0:1])
 
 # don't place everything in the project root
 AC_CONFIG_AUX_DIR([build-aux])
--- a/docs/src/header.html	Sat Aug 10 08:46:38 2019 +0200
+++ b/docs/src/header.html	Sat Oct 05 16:58:16 2019 +0200
@@ -21,6 +21,7 @@
                     <li><a href="modules.html">Modules</a>
                     <ul>
                         <li><a href="modules.html#allocator">Allocator</a></li>
+                        <li><a href="modules.html#array">Array</a></li>
                         <li><a href="modules.html#avl-tree">AVL Tree</a></li>
                         <li><a href="modules.html#buffer">Buffer</a></li>
                         <li><a href="modules.html#list">List</a></li>
--- a/docs/src/modules.md	Sat Aug 10 08:46:38 2019 +0200
+++ b/docs/src/modules.md	Sat Oct 05 16:58:16 2019 +0200
@@ -15,11 +15,12 @@
 
 <div id="modules" align="center">
     
------------------------ ----------------------      ----------------------------     -------------------------
-[Allocator](#allocator) [AVL&nbsp;Tree](#avl-tree)  [Buffer](#buffer)                [List](#list)
-[Logging](#logging)     [Map](#map)                 [Memory&nbsp;Pool](#memory-pool) [Properties](#properties)
-[Stack](#stack)         [String](#string)           [Testing](#testing)              [Utilities](#utilities)
------------------------ ----------------------      ----------------------------     -------------------------
+----------------------- ----------------------  --------------------------------  ---------------------------
+[String](#string)       [Buffer](#buffer)
+[Allocator](#allocator) [Stack](#stack)         [Memory&nbsp;Pool](#memory-pool)     
+[Array](#array)         [List](#list)           [Map](#map)                       [AVL&nbsp;Tree](#avl-tree)
+[Logging](#logging)     [Testing](#testing)     [Utilities](#utilities)           [Properties](#properties)                         
+----------------------- ----------------------  --------------------------------  ---------------------------
 
 </div>
 
@@ -41,6 +42,61 @@
 can be provided to the memory management functions. One example is the
 [UCX Memory Pool](#memory-pool).
 
+## Array
+
+*Header file:* [array.h](api/array_8h.html)  
+*Required modules:* [Allocator](#allocator)
+
+The UCX Array is an implementation of a dynamic array with automatic
+reallocation. The array structure contains a capacity, the current size,
+the size of each element, the raw pointer to the memory area and an allocator.
+Arrays are in most cases much faster than linked list.
+One can decide, whether to create a new array on the heap with `ucx_array_new()`
+or to save one indirection by initializing a `UcxArray` structure on the stack
+with `ucx_array_init()`.
+
+### Remove duplicates from an array of strings
+
+The following example shows, how a `UcxArray` can be built with
+a standard dynamic C array (pointer+length) as basis.
+
+```C
+#include <stdio.h>
+#include <ucx/array.h>
+#include <ucx/string.h>
+#include <ucx/utils.h>
+
+UcxArray remove_duplicates(sstr_t* array, size_t arrlen) {
+    // worst case is no duplicates, hence the capacity is set to arrlen
+    UcxArray result = ucx_array_new(arrlen, sizeof(sstr_t));
+    // only append elements, if they are not already present in the array
+    for (size_t i = 0 ; i < arrlen ; ++i) {
+        if (!ucx_array_contains(result, array+i, ucx_cmp_sstr, NULL)) {
+            ucx_array_append(&result, array+i);
+        }
+    }
+    // make the array as small as possible
+    ucx_array_shrink(&result);
+    return result;
+}
+
+/* ... */
+
+sstr_t* array = /* some standard array of strings */
+size_t arrlen = /* the length of the array */
+
+UcxArray result = remove_duplicates(array,arrlen);
+
+/* Iterate over the array and print the elements */
+for (size_t i = 0 ; i < result.size ; i++) {
+    sstr_t s = ucx_array_at_typed(sstr_t, result, i);
+    printf("%" PRIsstr "\n", SFMT(s));
+}
+
+/* Free the array. */
+ucx_array_free(&result);
+```
+
 ## AVL Tree
 
 *Header file:* [avl.h](api/avl_8h.html)  
@@ -195,6 +251,8 @@
 
 Assume you are given an array of `sstr_t` and want to create a list of these
 strings without duplicates.
+This is a similar example to the one [above](#array), but here we are
+using a `UcxList`.
 ```C
 #include <stdio.h>
 #include <ucx/list.h>
--- a/src/Makefile.am	Sat Aug 10 08:46:38 2019 +0200
+++ b/src/Makefile.am	Sat Oct 05 16:58:16 2019 +0200
@@ -29,6 +29,7 @@
 lib_LTLIBRARIES = libucx.la
 libucx_la_LDFLAGS = -version-info $(UCX_LIB_VERSION)
 libucx_la_SOURCES = utils.c
+libucx_la_SOURCES += array.c
 libucx_la_SOURCES += list.c
 libucx_la_SOURCES += map.c
 libucx_la_SOURCES += avl.c
@@ -44,6 +45,7 @@
 
 ucxdir = $(includedir)/ucx
 ucx_HEADERS = ucx/allocator.h
+ucx_HEADERS += ucx/array.h
 ucx_HEADERS += ucx/avl.h
 ucx_HEADERS += ucx/buffer.h
 ucx_HEADERS += ucx/list.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/array.c	Sat Oct 05 16:58:16 2019 +0200
@@ -0,0 +1,488 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE /* we want to use qsort_r(), if available */
+#define __STDC_WANT_LIB_EXT1__ 1 /* use qsort_s, if available */
+
+
+#include "ucx/array.h"
+#include "ucx/utils.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef UCX_ARRAY_DISABLE_QSORT
+#ifdef __GLIBC__
+#if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
+#define ucx_array_sort_impl qsort_r
+#endif /* glibc version >= 2.8 */
+#elif /* not  __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__)
+#define ucx_array_sort_impl ucx_qsort_r
+#define USE_UCX_QSORT_R
+#elif /* not (__APPLE || __FreeBSD__) */ defined(__sun)
+#if __STDC_VERSION__ >= 201112L
+#define ucx_array_sort_impl qsort_s
+#endif
+#endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */
+#endif /* UCX_ARRAY_DISABLE_QSORT */
+
+#ifndef ucx_array_sort_impl
+#define ucx_array_sort_impl ucx_mergesort
+#endif
+
+static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) {
+    size_t required_capacity = array->capacity;
+    while (reqcap > required_capacity) {
+        if (required_capacity * 2 < required_capacity)
+            return 1;
+        required_capacity <<= 1;
+    }
+    if (ucx_array_reserve(array, required_capacity)) {
+        return 1;
+    }
+    return 0;
+}
+
+int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
+    size_t elmsize, size_t index, ...) {
+    
+    if(!alloc || !capacity || !array) {
+        errno = EINVAL;
+        return 1;
+    }
+    
+    size_t newcapacity = *capacity;
+    while(index >= newcapacity) {
+        if(ucx_szmul(newcapacity, 2, &newcapacity)) {
+            errno = EOVERFLOW;
+            return 1;
+        }        
+    }
+
+    size_t memlen, offset;
+    if(ucx_szmul(newcapacity, elmsize, &memlen)) {
+        errno = EOVERFLOW;
+        return 1;
+    }
+    /* we don't need to check index*elmsize - it is smaller than memlen */
+    
+    
+    void* newptr = alrealloc(alloc, *array, memlen);
+    if(newptr == NULL) {
+        errno = ENOMEM; /* we cannot assume that every allocator sets this */
+        return 1;
+    }
+    *array = newptr;
+    *capacity = newcapacity;
+    
+    
+    char* dest = *array;
+    dest += elmsize*index;
+
+    va_list ap;
+    va_start(ap, index);
+    int elem = va_arg(ap, int);    
+    memcpy(dest, &elem, elmsize);
+    va_end(ap);
+    
+    return 0;
+}
+
+UcxArray* ucx_array_new(size_t capacity, size_t elemsize) {
+    return ucx_array_new_a(capacity, elemsize, ucx_default_allocator());
+}
+
+UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
+        UcxAllocator* allocator) {
+    UcxArray* array = almalloc(allocator, sizeof(UcxArray));
+    if(array) {
+        ucx_array_init_a(array, capacity, elemsize, allocator);
+    }
+    return array;
+}
+
+void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize) {
+    ucx_array_init_a(array, capacity, elemsize, ucx_default_allocator());
+}
+
+void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
+        UcxAllocator* allocator) {
+    
+    array->allocator = allocator;
+    array->elemsize = elemsize;
+    array->size = 0;
+    array->data = alcalloc(allocator, capacity, elemsize);
+    
+    if (array->data) {
+        array->capacity = capacity;
+    } else {
+        array->capacity = 0;
+    }
+}
+
+int ucx_array_clone(UcxArray* dest, UcxArray const* src) {
+    if (ucx_array_ensurecap(dest, src->capacity)) {
+        return 1;
+    }
+    
+    dest->elemsize = src->elemsize;
+    dest->size = src->size;
+    
+    if (dest->data) {
+        memcpy(dest->data, src->data, src->size*src->elemsize);
+    }
+    
+    return 0;
+}
+
+int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
+        cmp_func cmpfnc, void* data) {
+    
+    if (array1->size != array2->size || array1->elemsize != array2->elemsize) {
+        return 0;
+    } else {
+        if (array1->size == 0)
+            return 1;
+        
+        size_t elemsize;
+        if (cmpfnc == NULL) {
+            cmpfnc = ucx_cmp_mem;
+            elemsize = array1->elemsize;
+            data = &elemsize;
+        }
+        
+        for (size_t i = 0 ; i < array1->size ; i++) {
+            int r = cmpfnc(
+                    ucx_array_at(array1, i),
+                    ucx_array_at(array2, i),
+                    data);
+            if (r != 0)
+                return 0;
+        }
+        return 1;
+    }
+}
+
+void ucx_array_destroy(UcxArray *array) {
+    if(array->data)
+        alfree(array->allocator, array->data);
+    array->data = NULL;
+    array->capacity = array->size = 0;
+}
+
+void ucx_array_free(UcxArray *array) {
+    ucx_array_destroy(array);
+    alfree(array->allocator, array);
+}
+
+int ucx_array_append_from(UcxArray *array, void *data, size_t count) {
+    if (ucx_array_ensurecap(array, array->size + count))
+        return 1;
+    
+    void* dest = ucx_array_at(array, array->size);
+    if (data) {
+        memcpy(dest, data, array->elemsize*count);
+    } else {
+        memset(dest, 0, array->elemsize*count);
+    }
+    array->size += count;
+    
+    return 0;
+}
+
+int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) {
+    if (ucx_array_ensurecap(array, array->size + count))
+        return 1;
+    
+    if (array->size > 0) {
+        void *dest = ucx_array_at(array, count);
+        memmove(dest, array->data, array->elemsize*array->size);
+    }
+    
+    if (data) {
+        memcpy(array->data, data, array->elemsize*count);
+    } else {
+        memset(array->data, 0, array->elemsize*count);
+    }
+    array->size += count;
+        
+    return 0;
+}
+
+int ucx_array_set_from(UcxArray *array, size_t index,
+        void *data, size_t count) {
+    if (ucx_array_ensurecap(array, index + count))
+        return 1;
+    
+    if (index+count > array->size) {
+        array->size = index+count;
+    }
+    
+    void *dest = ucx_array_at(array, index);
+    if (data) {
+        memcpy(dest, data, array->elemsize*count);
+    } else {
+        memset(dest, 0, array->elemsize*count);
+    }
+    
+    return 0;
+}
+
+int ucx_array_appendv(UcxArray *array, ...) {
+    va_list ap;
+    va_start(ap, array);
+    int elem = va_arg(ap, int);
+    int ret = ucx_array_append_from(array, &elem, 1);
+    va_end(ap);
+    return ret;
+}
+
+int ucx_array_prependv(UcxArray *array, ...) {
+    va_list ap;
+    va_start(ap, array);
+    int elem = va_arg(ap, int);
+    int ret = ucx_array_prepend_from(array, &elem, 1);
+    va_end(ap);
+    return ret;
+}
+
+int ucx_array_setv(UcxArray *array, size_t index, ...) {
+    va_list ap;
+    va_start(ap, index);
+    int elem = va_arg(ap, int);
+    int ret = ucx_array_set_from(array, index, &elem, 1);
+    va_end(ap);
+    return ret;
+}
+
+int ucx_array_concat(UcxArray *array1, const UcxArray *array2) {
+    
+    if (array1->elemsize != array2->elemsize)
+        return 1;
+    
+    size_t capacity = array1->capacity+array2->capacity;
+        
+    if (array1->capacity < capacity) {
+        if (ucx_array_reserve(array1, capacity)) {
+            return 1;
+        }
+    }
+    
+    void* dest = ucx_array_at(array1, array1->size);
+    memcpy(dest, array2->data, array2->size*array2->elemsize);
+    
+    array1->size += array2->size;
+    
+    return 0;
+}
+
+void *ucx_array_at(UcxArray const *array, size_t index) {
+    char* memory = array->data;
+    char* loc = memory + index*array->elemsize;
+    return loc;
+}
+
+size_t ucx_array_find(UcxArray const *array, void *elem,
+        cmp_func cmpfnc, void *data) {
+    
+    size_t elemsize;
+    if (cmpfnc == NULL) {
+        cmpfnc = ucx_cmp_mem;
+        elemsize = array->elemsize;
+        data = &elemsize;
+    }
+
+    if (array->size > 0) {
+        for (size_t i = 0 ; i < array->size ; i++) {
+            void* ptr = ucx_array_at(array, i);
+            if (cmpfnc(ptr, elem, data) == 0) {
+                return i;
+            }
+        }
+        return array->size;
+    } else {
+        return 0;
+    }
+}
+
+int ucx_array_contains(UcxArray const *array, void *elem,
+        cmp_func cmpfnc, void *data) {
+    return ucx_array_find(array, elem, cmpfnc, data) != array->size;
+}
+
+static void ucx_mergesort_merge(void *arrdata,size_t elemsize,
+        cmp_func cmpfnc, void *data,
+        size_t start, size_t mid, size_t end) { 
+    
+    char* array = arrdata;
+    
+    size_t rightstart = mid + 1; 
+  
+    if (cmpfnc(array + mid*elemsize,
+            array + rightstart*elemsize, data) <= 0) {
+        /* already sorted */
+        return;
+    }
+  
+    /* we need memory for one element */
+    void *value = malloc(elemsize);
+    
+    while (start <= mid && rightstart <= end) { 
+        if (cmpfnc(array + start*elemsize,
+                array + rightstart*elemsize, data) <= 0) { 
+            start++; 
+        } else {
+            /* save the value from the right */
+            memcpy(value, array + rightstart*elemsize, elemsize);
+                        
+            /* shift all left elements one element to the right */
+            size_t shiftcount = rightstart-start;
+            void *startptr = array + start*elemsize;
+            void *dest = array + (start+1)*elemsize;
+            memmove(dest, startptr, shiftcount*elemsize);
+            
+            /* bring the first value from the right to the left */
+            memcpy(startptr, value, elemsize);
+  
+            start++; 
+            mid++; 
+            rightstart++; 
+        }
+    }
+    
+    /* free the temporary memory */
+    free(value);
+} 
+  
+static void ucx_mergesort_impl(void *arrdata, size_t elemsize,
+        cmp_func cmpfnc, void *data, size_t l, size_t r) { 
+    if (l < r) {
+        size_t m = l + (r - l) / 2; 
+  
+        ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m); 
+        ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r); 
+        ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r);
+    } 
+}
+
+static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize,
+        cmp_func cmpfnc, void *data) {
+    
+    ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1);
+}
+
+#ifdef USE_UCX_QSORT_R
+struct cmpfnc_swapargs_info {
+    cmp_func func;
+    void *data;
+};
+
+static int cmp_func_swap_args(void *data, const void *x, const void *y) {
+    cmpfnc_swapargs_info* info = data;
+    return info->func(x, y, info->data);
+}
+
+static void ucx_qsort_r(void *array, size_t count, size_t elemsize,
+		     cmp_func cmpfnc, void *data) {
+    struct cmpfnc_swapargs_info info;
+    info.func = cmpfnc;
+    info.data = data;
+    qsort_r(array, count, elemsize, &info, cmp_func_swap_args);
+}
+#endif /* USE_UCX_QSORT_R */
+
+void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data) {
+    ucx_array_sort_impl(array->data, array->size, array->elemsize,
+            cmpfnc, data);
+}
+
+void ucx_array_remove(UcxArray *array, size_t index) {
+    array->size--;
+    if (index < array->size) {
+        void* dest = ucx_array_at(array, index);
+        void* src = ucx_array_at(array, index+1);
+        memmove(dest, src, (array->size - index)*array->elemsize);
+    }
+}
+
+void ucx_array_remove_fast(UcxArray *array, size_t index) {
+    array->size--;
+    if (index < array->size) {       
+        void* dest = ucx_array_at(array, index);
+        void* src = ucx_array_at(array, array->size);
+        memcpy(dest, src, array->elemsize);
+    }
+}
+
+int ucx_array_shrink(UcxArray* array) {
+    void* newptr = alrealloc(array->allocator, array->data,
+                array->size*array->elemsize);
+    if (newptr) {
+        array->data = newptr;
+        array->capacity = array->size;
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+int ucx_array_resize(UcxArray* array, size_t capacity) {
+    if (array->capacity >= capacity) {
+        void* newptr = alrealloc(array->allocator, array->data,
+                capacity*array->elemsize);
+        if (newptr) {
+            array->data = newptr;
+            array->capacity = capacity;
+            if (array->size > array->capacity) {
+                array->size = array->capacity;
+            }
+            return 0;
+        } else {
+            return 1;
+        }
+    } else {
+        return ucx_array_reserve(array, capacity);
+    }
+}
+
+int ucx_array_reserve(UcxArray* array, size_t capacity) {
+    if (array->capacity > capacity) {
+        return 0;
+    } else {
+        void* newptr = alrealloc(array->allocator, array->data,
+                capacity*array->elemsize);
+        if (newptr) {
+            array->data = newptr;
+            array->capacity = capacity;
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+}
--- a/src/list.c	Sat Aug 10 08:46:38 2019 +0200
+++ b/src/list.c	Sat Oct 05 16:58:16 2019 +0200
@@ -206,7 +206,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 +214,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 +255,7 @@
     }
 
     UcxList *lc;
-    int ln = 1;
+    size_t ln = 1;
 
     UcxList *ls = l, *le, *re;
     
@@ -271,7 +271,7 @@
         return l; // this list is already sorted :)
     } else {
         UcxList *rc;
-        int rn = 1;
+        size_t rn = 1;
         rc = le;
         // skip already sorted elements
         while (rc->next != NULL && fnc(rc->next->data, rc->data, data) > 0) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ucx/array.h	Sat Oct 05 16:58:16 2019 +0200
@@ -0,0 +1,516 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * Dynamically allocated array implementation.
+ * 
+ * @file   array.h
+ * @author Mike Becker
+ * @author Olaf Wintermann
+ */
+
+#ifndef UCX_ARRAY_H
+#define	UCX_ARRAY_H
+
+#include "ucx.h"
+#include "allocator.h"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+/**
+ * UCX array type.
+ */
+typedef struct {
+    /**
+     * The current capacity of the array.
+     */
+    size_t capacity;
+    /**
+     * The actual number of elements in the array.
+     */
+    size_t size;
+    /**
+     * The size of an individual element in bytes.
+     */
+    size_t elemsize;
+    /**
+     * A pointer to the data.
+     */
+    void* data;
+    /**
+     * The allocator used for the data.
+     */
+    UcxAllocator* allocator;
+} UcxArray;
+
+/**
+ * Sets an element in an arbitrary user defined array.
+ * 
+ * If the capacity is insufficient, the array is automatically reallocated and
+ * the possibly new pointer is stored in the <code>array</code> argument.
+ * 
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>.
+ *  
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param elmsize the size of each element
+ * @param idx the index of the element to set
+ * @param data the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+#define ucx_array_util_set(array, capacity, elmsize, idx, data) \
+    ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \
+                         elmsize, idx, data)
+
+/**
+ * Convenience macro for ucx_array_util_set() which automatically computes
+ * <code>sizeof(data)</code>.
+ * 
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param idx the index of the element to set
+ * @param data the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ * @see ucx_array_util_set()
+ */
+#define UCX_ARRAY_UTIL_SET(array, capacity, idx, data) \
+    ucx_array_util_set_a(ucx_default_allocator(), (void**)(array), capacity, \
+                         sizeof(data), idx, data)
+
+/**
+ * Sets an element in an arbitrary user defined array.
+ * 
+ * If the capacity is insufficient, the array is automatically reallocated
+ * using the specified allocator and the possibly new pointer is stored in
+ * the <code>array</code> argument.
+ * 
+ * On reallocation the capacity of the array is doubled until it is sufficient.
+ * The new capacity is stored back to <code>capacity</code>. 
+ * 
+ * @param alloc the allocator that shall be used to reallocate the array
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param elmsize the size of each element
+ * @param idx the index of the element to set
+ * @param ... the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ */
+int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
+    size_t elmsize, size_t idx, ...);
+
+
+/**
+ * Convenience macro for ucx_array_util_set_a() which automatically computes
+ * <code>sizeof(data)</code>.
+ * 
+ * @param alloc the allocator that shall be used to reallocate the array
+ * @param array a pointer to location of the array pointer
+ * @param capacity a pointer to the capacity
+ * @param idx the index of the element to set
+ * @param data the element data
+ * @return zero on success or non-zero on error (errno will be set)
+ * @see ucx_array_util_set_a()
+ */
+#define UCX_ARRAY_UTIL_SET_A(alloc, array, capacity, idx, data) \
+    ucx_array_util_set_a(alloc, capacity, sizeof(data), idx, data)
+
+/**
+ * Creates a new UCX array with the given capacity and element size.
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @return a pointer to a new UCX array structure
+ */
+UcxArray* ucx_array_new(size_t capacity, size_t elemsize);
+
+/**
+ * Creates a new UCX array using the specified allocator.
+ * 
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @param allocator the allocator to use
+ * @return a pointer to new UCX array structure
+ */
+UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
+        UcxAllocator* allocator);
+
+/**
+ * Initializes a UCX array structure with the given capacity and element size.
+ * The structure must be uninitialized as the data pointer will be overwritten.
+ * 
+ * @param array the structure to initialize
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ */
+void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize);
+
+/**
+ * Initializes a UCX array structure using the specified allocator.
+ * The structure must be uninitialized as the data pointer will be overwritten.
+ * 
+ * @param capacity the initial capacity
+ * @param elemsize the element size
+ * @param allocator the allocator to use
+ */
+void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
+        UcxAllocator* allocator);
+
+/**
+ * Creates an shallow copy of an array.
+ * 
+ * This function clones the specified array by using memcpy().
+ * If the destination capacity is insufficient, an automatic reallocation is
+ * attempted.
+ * 
+ * Note: if the destination array is uninitialized, the behavior is undefined.
+ * 
+ * @param dest the array to copy to
+ * @param src the array to copy from
+ * @return zero on success, non-zero on reallocation failure.
+ */
+int ucx_array_clone(UcxArray* dest, UcxArray const* src);
+
+
+/**
+ * Compares two UCX arrays element-wise by using a compare function.
+ *
+ * Elements of the two specified arrays are compared by using the specified
+ * compare function and the additional data. The type and content of this
+ * additional data depends on the cmp_func() used.
+ * 
+ * This function always returns zero, if the element sizes of the arrays do
+ * not match and performs no comparisons in this case.
+ * 
+ * @param array1 the first array
+ * @param array2 the second array
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the two arrays equal element-wise, 0 otherwise
+ */
+int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
+        cmp_func cmpfnc, void* data);
+
+/**
+ * Destroys the array.
+ * 
+ * The data is freed and both capacity and count are reset to zero.
+ * If the array structure itself has been dynamically allocated, it has to be
+ * freed separately.
+ * 
+ * @param array the array to destroy
+ */
+void ucx_array_destroy(UcxArray *array);
+
+/**
+ * Destroys and frees the array.
+ * 
+ * @param array the array to free
+ */
+void ucx_array_free(UcxArray *array);
+
+/**
+ * Inserts elements at the end of the array.
+ * 
+ * This is an O(1) operation.
+ * The array will automatically grow, if the capacity is exceeded.
+ * If a pointer to data is provided, the data is copied into the array with
+ * memcpy(). Otherwise the new elements are completely zeroed.
+ * 
+ * @param array a pointer the array where to append the data
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, zeroed elements are appended)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_set_from()
+ * @see ucx_array_append()
+ */
+int ucx_array_append_from(UcxArray *array, void *data, size_t count);
+
+
+/**
+ * Inserts elements at the beginning of the array.
+ * 
+ * This is an expensive operation, because the contents must be moved.
+ * If there is no particular reason to prepend data, you should use
+ * ucx_array_append_from() instead.
+ * 
+ * @param array a pointer the array where to prepend the data
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, zeroed elements are inserted)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append_from()
+ * @see ucx_array_set_from()
+ * @see ucx_array_prepend()
+ */
+int ucx_array_prepend_from(UcxArray *array, void *data, size_t count);
+
+
+/**
+ * Sets elements starting at the specified index.
+ * 
+ * If the any index is out of bounds, the array automatically grows.
+ * The pointer to the data may be NULL, in which case the elements are zeroed. 
+ * 
+ * @param array a pointer the array where to set the data
+ * @param index the index of the element to set
+ * @param data a pointer to the data to insert (may be <code>NULL</code>)
+ * @param count number of elements to copy from data (if data is
+ * <code>NULL</code>, the memory in the array is zeroed)
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append_from()
+ * @see ucx_array_set()
+ */
+int ucx_array_set_from(UcxArray *array, size_t index, void *data, size_t count);
+
+/**
+ * Inserts an element at the end of the array.
+ * 
+ * This is an O(1) operation.
+ * The array will automatically grow, if the capacity is exceeded.
+ * If the type of the argument has a different size than the element size of
+ * this array, the behavior is undefined.
+ * 
+ * @param array a pointer the array where to append the data
+ * @param elem the value to insert
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append_from()
+ * @see ucx_array_set()
+ */
+#define ucx_array_append(array, elem) ucx_array_appendv(array, elem)
+
+/**
+ * For internal use.
+ * Use ucx_array_append()
+ * 
+ * @param array
+ * @param ... 
+ * @return 
+ * @see ucx_array_append()
+ */
+int ucx_array_appendv(UcxArray *array, ...);
+
+
+/**
+ * Inserts an element at the beginning of the array.
+ * 
+ * This is an expensive operation, because the contents must be moved.
+ * If there is no particular reason to prepend data, you should use
+ * ucx_array_append() instead.
+ * 
+ * @param array a pointer the array where to prepend the data
+ * @param elem the value to insert
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append()
+ * @see ucx_array_set_from()
+ * @see ucx_array_prepend_from()
+ */
+#define ucx_array_prepend(array, elem) ucx_array_prependv(array, elem)
+
+/**
+ * For internal use.
+ * Use ucx_array_prepend()
+ * 
+ * @param array
+ * @param ... 
+ * @return 
+ * @see ucx_array_prepend()
+ */
+int ucx_array_prependv(UcxArray *array, ...);
+
+
+/**
+ * Sets an element at the specified index.
+ * 
+ * If the any index is out of bounds, the array automatically grows.
+ * 
+ * @param array a pointer the array where to set the data
+ * @param index the index of the element to set
+ * @param elem the value to set
+ * @return zero on success, non-zero if a reallocation was necessary but failed
+ * @see ucx_array_append()
+ * @see ucx_array_set_from()
+ */
+#define ucx_array_set(array, index, elem) ucx_array_setv(array, index, elem)
+
+/**
+ * For internal use.
+ * Use ucx_array_set()
+ * 
+ * @param array
+ * @param index
+ * @param ... 
+ * @return 
+ * @see ucx_array_set()
+ */
+int ucx_array_setv(UcxArray *array, size_t index, ...);
+
+/**
+ * Concatenates two arrays.
+ * 
+ * The contents of the second array are appended to the first array in one
+ * single operation. The second array is otherwise left untouched.
+ * 
+ * The first array may grow automatically. If this fails, both arrays remain
+ * unmodified.
+ * 
+ * @param array1 first array
+ * @param array2 second array
+ * @return zero on success, non-zero if reallocation was necessary but failed 
+ * or the element size does not match
+ */
+int ucx_array_concat(UcxArray *array1, const UcxArray *array2);
+
+/**
+ * Returns a pointer to the array element at the specified index.
+ * 
+ * @param array the array to retrieve the element from
+ * @param index index of the element to return
+ * @return a pointer to the element at the specified index or <code>NULL</code>,
+ * if the index is greater than the array size
+ */
+void *ucx_array_at(UcxArray const* array, size_t index);
+
+/**
+ * Returns the index of an element containing the specified data.
+ *
+ * This function uses a cmp_func() to compare the data of each list element
+ * with the specified data. If no cmp_func is provided, memcmp() is used.
+ * 
+ * If the array contains the data more than once, the index of the first
+ * occurrence is returned.
+ * If the array does not contain the data, the size of array is returned.
+ *  
+ * @param array the array where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return the index of the element containing the specified data or the size of
+ * the array, if the data is not found in this array
+ */
+size_t ucx_array_find(UcxArray const *array, void *elem,
+    cmp_func cmpfnc, void *data);
+
+/**
+ * Checks, if an array contains a specific element.
+ * 
+ * An element is found, if ucx_array_find() returns a value less than the size.
+ * 
+ * @param array the array where to search for the data
+ * @param elem the element data
+ * @param cmpfnc the compare function
+ * @param data additional data for the compare function
+ * @return 1, if and only if the array contains the specified element data
+ * @see ucx_array_find()
+ */
+int ucx_array_contains(UcxArray const *array, void *elem,
+    cmp_func cmpfnc, void *data);
+
+/**
+ * Sorts a UcxArray with the best available sort algorithm.
+ * 
+ * The qsort_r() function is used, if available (glibc, FreeBSD or MacOS).
+ * The order of arguments is automatically adjusted for the FreeBSD and MacOS
+ * version of qsort_r().
+ * 
+ * If qsort_r() is not available, a merge sort algorithm is used, which is
+ * guaranteed to use no more additional memory than for exactly one element.
+ * 
+ * @param array the array to sort
+ * @param cmpfnc the function that shall be used to compare the element data
+ * @param data additional data for the cmp_func() or <code>NULL</code>
+ */
+void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data);
+
+/**
+ * Removes an element from the array.
+ * 
+ * This is in general an expensive operation, because several elements may
+ * be moved. If the order of the elements is not relevant, use
+ * ucx_array_remove_fast() instead.
+ * 
+ * @param array pointer to the array from which the element shall be removed
+ * @param index the index of the element to remove
+ */
+void ucx_array_remove(UcxArray *array, size_t index);
+
+/**
+ * Removes an element from the array.
+ * 
+ * This is an O(1) operation, but does not maintain the order of the elements.
+ * The last element in the array is moved to the location of the removed
+ * element.
+ * 
+ * @param array pointer to the array from which the element shall be removed
+ * @param index the index of the element to remove
+ */
+void ucx_array_remove_fast(UcxArray *array, size_t index);
+
+/**
+ * Shrinks the memory to exactly fit the contents.
+ * 
+ * After this operation, the capacity equals the size.
+ * 
+ * @param array a pointer to the array
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_shrink(UcxArray* array);
+
+/**
+ * Sets the capacity of the array.
+ * 
+ * If the new capacity is smaller than the size of the array, the elements
+ * are removed and the size is adjusted accordingly.
+ * 
+ * @param array a pointer to the array
+ * @param capacity the new capacity
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_resize(UcxArray* array, size_t capacity);
+
+/**
+ * Resizes the array only, if the capacity is insufficient.
+ * 
+ * If the requested capacity is smaller than the current capacity, this
+ * function does nothing.
+ * 
+ * @param array a pointer to the array
+ * @param capacity the guaranteed capacity
+ * @return zero on success, non-zero if reallocation failed
+ */
+int ucx_array_reserve(UcxArray* array, size_t capacity);
+
+
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* UCX_ARRAY_H */
+
--- a/src/ucx/ucx.h	Sat Aug 10 08:46:38 2019 +0200
+++ b/src/ucx/ucx.h	Sat Oct 05 16:58:16 2019 +0200
@@ -1,7 +1,7 @@
 /*
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  *
- * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved.
+ * Copyright 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:
@@ -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)
--- a/src/ucx/utils.h	Sat Aug 10 08:46:38 2019 +0200
+++ b/src/ucx/utils.h	Sat Oct 05 16:58:16 2019 +0200
@@ -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/src/utils.c	Sat Aug 10 08:46:38 2019 +0200
+++ b/src/utils.c	Sat Oct 05 16:58:16 2019 +0200
@@ -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/test/Makefile.am	Sat Aug 10 08:46:38 2019 +0200
+++ b/test/Makefile.am	Sat Oct 05 16:58:16 2019 +0200
@@ -31,6 +31,7 @@
 ucxtest_CFLAGS = -I$(top_srcdir)/src
 ucxtest_SOURCES = main.c
 ucxtest_SOURCES += allocator_tests.c
+ucxtest_SOURCES += array_tests.c
 ucxtest_SOURCES += list_tests.c
 ucxtest_SOURCES += avl_tests.c
 ucxtest_SOURCES += mpool_tests.c
@@ -41,4 +42,5 @@
 ucxtest_SOURCES += logging_tests.c
 ucxtest_SOURCES += buffer_tests.c
 ucxtest_SOURCES += utils_tests.c
+ucxtest_LDFLAGS = -static
 ucxtest_LDADD = ../src/libucx.la
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/array_tests.c	Sat Oct 05 16:58:16 2019 +0200
@@ -0,0 +1,653 @@
+/*
+ * 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.
+ */
+
+#include "array_tests.h"
+#include <ucx/utils.h>
+
+UCX_TEST(test_ucx_array_destroy) {
+    UcxArray array;
+    ucx_array_init(&array, 16, sizeof(int));
+    
+    UCX_TEST_BEGIN
+    ucx_array_destroy(&array);
+    UCX_TEST_ASSERT(array.data == NULL, "data pointer not NULL after destroy");
+    UCX_TEST_ASSERT(array.size == 0, "size not zero after destroy");
+    UCX_TEST_ASSERT(array.capacity == 0, "capacity not zero after destroy");
+    UCX_TEST_ASSERT(array.allocator == ucx_default_allocator(),
+            "allocator corrupted during destroy");
+    UCX_TEST_END
+}
+
+UCX_TEST(test_ucx_array_new) {
+    UcxArray* array = ucx_array_new(16, 47);
+    
+    UCX_TEST_BEGIN
+    UCX_TEST_ASSERT(array->data, "no memory allocated");
+    UCX_TEST_ASSERT(array->size == 0, "size not initially zero");
+    UCX_TEST_ASSERT(array->capacity == 16, "capacity not as requested");
+    UCX_TEST_ASSERT(array->elemsize == 47, "element size not as requested");
+    UCX_TEST_ASSERT(array->allocator == ucx_default_allocator(),
+            "array not using the default allocator");
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_append_from) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    int *elements;
+    
+    int x = 42;
+    ucx_array_append_from(array, &x, 1);
+    UCX_TEST_BEGIN
+    
+    elements = array->data;
+    UCX_TEST_ASSERT(elements[0] == 42, "failed");
+    
+    int y[2] = {13, 37};
+    ucx_array_append_from(array, y, 2);
+    
+    elements = array->data;
+    UCX_TEST_ASSERT(array->size == 3, "incorrect size after append");
+    UCX_TEST_ASSERT(elements[1] == 13, "failed");
+    UCX_TEST_ASSERT(elements[2] == 37, "failed");
+    UCX_TEST_ASSERT(elements[0] == 42,
+            "append corrupted previously inserted data");
+    
+    ucx_array_append_from(array, NULL, 2);
+    
+    elements = array->data;
+    UCX_TEST_ASSERT(array->size == 5, "incorrect size after NULL append");
+    UCX_TEST_ASSERT(elements[3] == 0, "element is not zeroed");
+    UCX_TEST_ASSERT(elements[4] == 0, "element is not zeroed");
+    UCX_TEST_ASSERT(elements[0] == 42,
+            "NULL append corrupted previously inserted data");
+    UCX_TEST_ASSERT(elements[1] == 13,
+            "NULL append corrupted previously inserted data");
+    UCX_TEST_ASSERT(elements[2] == 37,
+            "NULL append corrupted previously inserted data");
+    
+    UCX_TEST_END
+    
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_prepend_from) {
+    int *elems;
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    
+    int x = 42;
+    ucx_array_prepend_from(array, &x, 1);
+    UCX_TEST_BEGIN
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(elems[0] == 42, "failed");
+    
+    int y[2] = {13, 37};
+    ucx_array_prepend_from(array, y, 2);
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(array->size == 3, "incorrect size after prepend");
+    UCX_TEST_ASSERT(elems[0] == 13, "failed");
+    UCX_TEST_ASSERT(elems[1] == 37, "failed");
+    UCX_TEST_ASSERT(elems[2] == 42,
+            "prepend corrupted previously inserted data");
+    
+    ucx_array_prepend_from(array, NULL, 2);
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(array->size == 5, "incorrect size after NULL prepend");
+    UCX_TEST_ASSERT(elems[0] == 0, "element is not zeroed");
+    UCX_TEST_ASSERT(elems[1] == 0, "element is not zeroed");
+    UCX_TEST_ASSERT(elems[2] == 13,
+            "NULL prepend corrupted previously inserted data");
+    UCX_TEST_ASSERT(elems[3] == 37,
+            "NULL prepend corrupted previously inserted data");
+    UCX_TEST_ASSERT(elems[4] == 42,
+            "NULL prepend corrupted previously inserted data");
+    
+    UCX_TEST_END
+    
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_set_from) {
+    int *elems;
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    
+    int x = 42;
+
+    UCX_TEST_BEGIN
+
+    ucx_array_set_from(array, 7, &x, 1);
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(elems[7] == 42, "failed");
+    UCX_TEST_ASSERT(array->size >= 8, "array not resized on set");
+    UCX_TEST_ASSERT(array->capacity == 16, "capacity changed unnecessarily");
+    
+    int y[2] = {13, 37};
+    ucx_array_set_from(array, 27, y, 2);
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(elems[27] == 13, "failed");
+    UCX_TEST_ASSERT(elems[28] == 37, "failed");
+    UCX_TEST_ASSERT(array->size == 29, "array not resized on set");
+    UCX_TEST_ASSERT(array->capacity == 32, "capacity not grown");
+    
+    ucx_array_set_from(array, 7, NULL, 2);
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(elems[7] == 0, "not zeroed on NULL set");
+    UCX_TEST_ASSERT(elems[8] == 0, "not zeroed on NULL set");
+    
+    UCX_TEST_END
+    
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_append) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    int *elements;
+    
+    ucx_array_append(array, 42);
+    UCX_TEST_BEGIN
+    
+    elements = array->data;
+    UCX_TEST_ASSERT(elements[0] == 42, "failed");
+    
+    ucx_array_append(array, 13);
+    ucx_array_append(array, 37);
+    
+    elements = array->data;
+    UCX_TEST_ASSERT(array->size == 3, "incorrect size after append");
+    UCX_TEST_ASSERT(elements[1] == 13, "failed");
+    UCX_TEST_ASSERT(elements[2] == 37, "failed");
+    UCX_TEST_ASSERT(elements[0] == 42,
+            "append corrupted previously inserted data");
+    
+    UCX_TEST_END
+    
+    ucx_array_destroy(array);
+}
+
+UCX_TEST(test_ucx_array_prepend) {
+    int *elems;
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    
+    ucx_array_prepend(array, 42);
+    UCX_TEST_BEGIN
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(elems[0] == 42, "failed");
+    
+    ucx_array_prepend(array, 37);
+    ucx_array_prepend(array, 13);
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(array->size == 3, "incorrect size after prepend");
+    UCX_TEST_ASSERT(elems[0] == 13, "failed");
+    UCX_TEST_ASSERT(elems[1] == 37, "failed");
+    UCX_TEST_ASSERT(elems[2] == 42,
+            "prepend corrupted previously inserted data");
+    
+    UCX_TEST_END
+    
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_set) {
+    int *elems;
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+
+    UCX_TEST_BEGIN
+
+    ucx_array_set(array, 7, 42);
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(elems[7] == 42, "failed");
+    UCX_TEST_ASSERT(array->size == 8, "array not resized on set");
+    UCX_TEST_ASSERT(array->capacity == 16, "capacity changed unnecessarily");
+    
+    ucx_array_set(array, 27, 13);
+    ucx_array_set(array, 28, 37);
+    
+    elems = array->data;
+    UCX_TEST_ASSERT(elems[27] == 13, "failed");
+    UCX_TEST_ASSERT(elems[28] == 37, "failed");
+    UCX_TEST_ASSERT(array->size == 29, "array not resized on set");
+    UCX_TEST_ASSERT(array->capacity == 32, "capacity not grown");
+        
+    UCX_TEST_END
+    
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_equals) {
+    UcxArray *a1 = ucx_array_new(16, sizeof(int32_t));
+    UcxArray *a2 = ucx_array_new(16, sizeof(int32_t));
+    UcxArray *a3 = ucx_array_new(16, sizeof(int64_t));
+    UcxArray *a4 = ucx_array_new(16, sizeof(int32_t));
+    
+    int32_t *intelems;
+    int64_t *longintelems;
+    
+    a1->size = 5;
+    intelems = a1->data;
+    intelems[0] = 47;
+    intelems[1] = 11;
+    intelems[2] = 0;
+    intelems[3] = 8;
+    intelems[4] = 15;
+    a2->size = 5;
+    intelems = a2->data;
+    intelems[0] = 47;
+    intelems[1] = 11;
+    intelems[2] = 0;
+    intelems[3] = 8;
+    intelems[4] = 15;
+    a3->size = 5;
+    longintelems = a3->data;
+    longintelems[0] = 47;
+    longintelems[1] = 11;
+    longintelems[2] = 0;
+    longintelems[3] = 8;
+    longintelems[4] = 15;
+    a4->size = 5;
+    intelems = a4->data;
+    intelems[0] = 47;
+    intelems[1] = 11;
+    intelems[2] = -6;
+    intelems[3] = 8;
+    intelems[4] = 15;
+    
+    UCX_TEST_BEGIN
+    
+    UCX_TEST_ASSERT(ucx_array_equals(a1, a2, ucx_cmp_int32, NULL), "failed");
+    UCX_TEST_ASSERT(!ucx_array_equals(a1, a4, ucx_cmp_int32, NULL), "failed");
+    UCX_TEST_ASSERT(!ucx_array_equals(a4, a1, ucx_cmp_int32, NULL), "failed");
+    UCX_TEST_ASSERT(!ucx_array_equals(a1, a3, ucx_cmp_int64, NULL),
+            "comparing arrays of different element size shall fail");
+    UCX_TEST_ASSERT(!ucx_array_equals(a3, a1, ucx_cmp_int64, NULL),
+            "comparing arrays of different element size shall fail");
+    
+    UCX_TEST_ASSERT(ucx_array_equals(a1, a2, NULL, NULL),
+            "compare using memcmp() failed");
+    UCX_TEST_ASSERT(!ucx_array_equals(a1, a4, NULL, NULL),
+            "compare using memcmp() failed");
+    
+    UCX_TEST_END
+    ucx_array_free(a1);
+    ucx_array_free(a2);
+    ucx_array_free(a3);
+    ucx_array_free(a4);
+}
+
+UCX_TEST(test_ucx_array_concat) {
+    UcxArray *a1 = ucx_array_new(16, sizeof(int));
+    UcxArray *a2 = ucx_array_new(16, sizeof(int));
+    int *elems;
+    
+    a1->size = 2;
+    elems = a1->data;
+    elems[0] = 47;
+    elems[1] = 11;
+    a2->size = 3;
+    elems = a2->data;
+    elems[0] = 0;
+    elems[1] = 8;
+    elems[2] = 15;
+    
+    UCX_TEST_BEGIN
+    
+    UCX_TEST_ASSERT(!ucx_array_concat(a1, a2), "failed");
+    UCX_TEST_ASSERT(a1->size == 5, "failed");
+    elems = a1->data;
+    UCX_TEST_ASSERT(elems[0] == 47, "failed");
+    UCX_TEST_ASSERT(elems[1] == 11, "failed");
+    UCX_TEST_ASSERT(elems[2] == 0, "failed");
+    UCX_TEST_ASSERT(elems[3] == 8, "failed");
+    UCX_TEST_ASSERT(elems[4] == 15, "failed");
+    
+    a1->elemsize *= 2;
+    UCX_TEST_ASSERT(ucx_array_concat(a1, a2),
+            "arrays of different element size must not be concatenated");
+    UCX_TEST_ASSERT(a1->size == 5,
+            "arrays of different element size must not be concatenated");
+    
+    UCX_TEST_END
+    ucx_array_free(a1);
+    ucx_array_free(a2);    
+}
+
+UCX_TEST(test_ucx_array_at) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    
+    int x[3] = {42, 13, 5};
+    ucx_array_append_from(array, x, 3);
+    
+    UCX_TEST_BEGIN
+    
+    UCX_TEST_ASSERT(*(int*)ucx_array_at(array, 1) == 13, "failed");
+    *(int*)ucx_array_at(array, 1) = 80;
+    UCX_TEST_ASSERT(*(int*)ucx_array_at(array, 1) == 80, "assignment failed");
+    
+    UCX_TEST_ASSERT(*(int*)ucx_array_at(array, 0) == 42, "corrupted data");
+    UCX_TEST_ASSERT(*(int*)ucx_array_at(array, 2) == 5, "corrupted data");
+    
+    UCX_TEST_END
+    
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_find) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    int *elems;
+    
+    array->size = 5;
+    elems = array->data;
+    elems[0] = 47;
+    elems[1] = 11;
+    elems[2] = 0;
+    elems[3] = 8;
+    elems[4] = 15;
+    
+    int x = 8;
+    int y = 90;
+    
+    UCX_TEST_BEGIN
+    
+    UCX_TEST_ASSERT(ucx_array_find(array,(void*)&x,ucx_cmp_int,NULL) == 3,
+        "doesn't find element");
+    UCX_TEST_ASSERT(ucx_array_find(array,(void*)&y,ucx_cmp_int,NULL) == 5,
+        "finds non-existing element");
+    
+    UCX_TEST_ASSERT(ucx_array_find(array,(void*)&x,NULL,NULL) == 3,
+        "failed using memcmp()");
+    UCX_TEST_ASSERT(ucx_array_find(array,(void*)&y,NULL,NULL) == 5,
+        "failed using memcmp()");
+    
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_contains) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    int *elems;
+    
+    array->size = 5;
+    elems = array->data;
+    elems[0] = 47;
+    elems[1] = 11;
+    elems[2] = 0;
+    elems[3] = 8;
+    elems[4] = 15;
+    
+    int x = 8;
+    int y = 90;
+    
+    UCX_TEST_BEGIN
+    
+    UCX_TEST_ASSERT(ucx_array_contains(array,(void*)&x,ucx_cmp_int,NULL),
+        "false negative");
+    UCX_TEST_ASSERT(!ucx_array_contains(array,(void*)&y,ucx_cmp_int,NULL),
+        "false positive");
+    
+    UCX_TEST_ASSERT(ucx_array_contains(array,(void*)&x,NULL,NULL),
+        "false negative using memcmp()");
+    UCX_TEST_ASSERT(!ucx_array_contains(array,(void*)&y,NULL,NULL),
+        "false positive using memcmp()");
+    
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_remove) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    int *elems;
+    
+    array->size = 5;
+    elems = array->data;
+    elems[0] = 47;
+    elems[1] = 11;
+    elems[2] = 0;
+    elems[3] = 8;
+    elems[4] = 15;
+        
+    UCX_TEST_BEGIN
+    
+    ucx_array_remove(array, 2);
+    elems = array->data;
+    UCX_TEST_ASSERT(
+            elems[0] == 47 &&
+            elems[1] == 11 &&
+            elems[2] == 8 &&
+            elems[3] == 15,
+            "wrong contents after remove");
+    UCX_TEST_ASSERT(array->size == 4, "wrong size after remove");
+    
+    ucx_array_remove_fast(array, 1);
+    elems = array->data;
+    UCX_TEST_ASSERT(
+            elems[0] == 47 &&
+            elems[1] == 15 &&
+            elems[2] == 8,
+            "wrong contents after fast remove");
+    UCX_TEST_ASSERT(array->size == 3, "wrong size after fast remove");
+    
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_clone) {
+    UcxArray array;
+    UcxArray copy;
+    ucx_array_init(&array, 16, sizeof(int));
+    ucx_array_init(&copy, 4, 2*sizeof(double));
+    int *elems;
+    
+    array.size = 5;
+    elems = array.data;
+    elems[0] = 47;
+    elems[1] = 11;
+    elems[2] = 0;
+    elems[3] = 8;
+    elems[4] = 15;
+    
+    ucx_array_clone(&copy, &array);
+    UCX_TEST_BEGIN
+
+    UCX_TEST_ASSERT(array.data != copy.data, "no true copy");
+    UCX_TEST_ASSERT(array.size == copy.size, "size mismatch");
+    UCX_TEST_ASSERT(array.capacity == copy.capacity, "capacity mismatch");
+    UCX_TEST_ASSERT(array.elemsize == copy.elemsize, "element size mismatch");
+    UCX_TEST_ASSERT(array.allocator == copy.allocator, "allocator mismatch");
+    UCX_TEST_ASSERT(ucx_array_equals(&array, &copy, ucx_cmp_int, NULL),
+            "contents do not match after clone");
+    
+    UCX_TEST_END
+
+    ucx_array_destroy(&array);
+    ucx_array_destroy(&copy);
+}
+
+static int ucx_cmp_int_reverse(const void* x, const void* y, void* data) {
+    return -ucx_cmp_int(x,y,data);
+}
+
+UCX_TEST(test_ucx_array_sort) {
+    int *elems;
+
+    UcxArray *array = ucx_array_new(16, sizeof(int));    
+    array->size = 5;
+    elems = array->data;
+    elems[0] = 47;
+    elems[1] = 11;
+    elems[2] = 0;
+    elems[3] = 8;
+    elems[4] = 15;
+    
+    UcxArray *expected = ucx_array_new(16, sizeof(int));
+    expected->size = 5;
+    elems = expected->data;
+    elems[0] = 0;
+    elems[1] = 8;
+    elems[2] = 11;
+    elems[3] = 15;
+    elems[4] = 47;
+    
+    UcxArray *expectedrev = ucx_array_new(16, sizeof(int));
+    expectedrev->size = 5;
+    elems = expectedrev->data;
+    elems[0] = 47;
+    elems[1] = 15;
+    elems[2] = 11;
+    elems[3] = 8;
+    elems[4] = 0;
+    
+
+    UCX_TEST_BEGIN
+    void* original_ptr = array->data;
+    ucx_array_sort(array, ucx_cmp_int, NULL);
+    UCX_TEST_ASSERT(ucx_array_equals(array, expected, NULL, NULL), "failed");
+    UCX_TEST_ASSERT(array->size == 5, "size corrupted");
+    UCX_TEST_ASSERT(array->data == original_ptr, "shall not reallocate");
+    
+    ucx_array_sort(array, ucx_cmp_int_reverse, NULL);
+    UCX_TEST_ASSERT(ucx_array_equals(array, expectedrev, NULL, NULL), "failed");
+
+    ucx_array_reserve(array, 32);
+    ucx_array_reserve(expected, 32);
+    array->size = expected->size = 32;
+    for (size_t i = 0 ; i < 32 ; i++) {
+        ((int*)array->data)[i]= ((i%2==0)?-1:1) * ((int) i);
+        ((int*)expected->data)[i] = (-30+2*i) - (i > 15 ? 1 : 0);
+    }
+    
+    /* dummy third argument to trigger a possible fallback for qsort_s */
+    ucx_array_sort(array, ucx_cmp_int, array->data);
+    UCX_TEST_ASSERT(ucx_array_equals(array, expected, NULL, NULL),
+            "failed for bigger arrays");
+    UCX_TEST_END
+
+    ucx_array_free(expectedrev);
+    ucx_array_free(expected);
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_autogrow) {
+    int *elems;
+    UcxArray *array = ucx_array_new(4, sizeof(int));
+    array->size = 3;
+    elems = array->data;
+    elems[0] = 47;
+    elems[1] = 11;
+    int x = 5;
+    
+    UCX_TEST_BEGIN
+
+    void* oldptr = array->data;
+    
+    ucx_array_append(array, 5);
+    UCX_TEST_ASSERT(array->capacity == 4 && array->data == oldptr,
+            "array should not grow too early");
+    ucx_array_append(array, 5);
+    elems = array->data;
+    UCX_TEST_ASSERT(array->capacity == 8, "array did not grow");
+    UCX_TEST_ASSERT(array->size == 5, "incorrect size after grow");
+    UCX_TEST_ASSERT(elems[3] == 5 && elems[4] == 5, "corrupt data");
+    
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_shrink) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    array->size = 4;
+    
+    UCX_TEST_BEGIN
+    UCX_TEST_ASSERT(!ucx_array_shrink(array), "failed");
+    UCX_TEST_ASSERT(array->capacity == 4, "incorrect capacity after shrink");
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_resize) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    array->size = 8;
+    
+    UCX_TEST_BEGIN
+
+    UCX_TEST_ASSERT(!ucx_array_resize(array, 32), "failed");
+    UCX_TEST_ASSERT(array->capacity == 32, "incorrect capacity after resize");
+    UCX_TEST_ASSERT(array->size == 8, "incorrect size after resize");
+    
+    UCX_TEST_ASSERT(!ucx_array_resize(array, 4), "failed");
+    UCX_TEST_ASSERT(array->capacity == 4, "incorrect capacity after resize");
+    UCX_TEST_ASSERT(array->size == 4, "incorrect size after resize");
+    
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_reserve) {
+    UcxArray *array = ucx_array_new(16, sizeof(int));
+    
+    UCX_TEST_BEGIN
+
+    UCX_TEST_ASSERT(!ucx_array_reserve(array, 4), "failed");
+    UCX_TEST_ASSERT(array->capacity == 16, "reserve shall not shrink");
+            
+    UCX_TEST_ASSERT(!ucx_array_resize(array, 32), "failed");
+    UCX_TEST_ASSERT(array->capacity == 32, "incorrect capacity after reserve");    
+    
+    UCX_TEST_END
+    ucx_array_free(array);
+}
+
+UCX_TEST(test_ucx_array_util_set) {
+    size_t capacity = 16;
+    int* array = malloc(sizeof(int)*capacity);
+
+    UCX_TEST_BEGIN
+
+    UCX_ARRAY_UTIL_SET(&array, &capacity, 7, 42);
+    
+    UCX_TEST_ASSERT(array[7] == 42, "failed");
+    UCX_TEST_ASSERT(capacity == 16, "capacity changed unnecessarily");
+    
+    UCX_ARRAY_UTIL_SET(&array, &capacity, 37, 13);
+    UCX_ARRAY_UTIL_SET(&array, &capacity, 38, 37);
+    
+    UCX_TEST_ASSERT(array[37] == 13, "failed");
+    UCX_TEST_ASSERT(array[38] == 37, "failed");
+    UCX_TEST_ASSERT(capacity == 64, "capacity not grown");
+        
+    UCX_TEST_END
+    
+    free(array);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/array_tests.h	Sat Oct 05 16:58:16 2019 +0200
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef ARRAY_TESTS_H
+#define	ARRAY_TESTS_H
+
+#include <ucx/array.h>
+#include <ucx/test.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+UCX_TEST(test_ucx_array_destroy);
+UCX_TEST(test_ucx_array_new);
+UCX_TEST(test_ucx_array_at);
+UCX_TEST(test_ucx_array_append_from);
+UCX_TEST(test_ucx_array_prepend_from);
+UCX_TEST(test_ucx_array_set_from);
+UCX_TEST(test_ucx_array_append);
+UCX_TEST(test_ucx_array_prepend);
+UCX_TEST(test_ucx_array_set);
+UCX_TEST(test_ucx_array_autogrow);
+UCX_TEST(test_ucx_array_equals);
+UCX_TEST(test_ucx_array_concat);
+UCX_TEST(test_ucx_array_find);
+UCX_TEST(test_ucx_array_contains);
+UCX_TEST(test_ucx_array_remove);
+UCX_TEST(test_ucx_array_clone);
+UCX_TEST(test_ucx_array_sort);
+UCX_TEST(test_ucx_array_shrink);
+UCX_TEST(test_ucx_array_resize);
+UCX_TEST(test_ucx_array_reserve);
+UCX_TEST(test_ucx_array_util_set);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* ARRAY_TESTS_H */
+
--- a/test/main.c	Sat Aug 10 08:46:38 2019 +0200
+++ b/test/main.c	Sat Oct 05 16:58:16 2019 +0200
@@ -33,6 +33,7 @@
 
 #include "main.h"
 
+#include "array_tests.h"
 #include "allocator_tests.h"
 #include "logging_tests.h"
 #include "list_tests.h"
@@ -141,6 +142,29 @@
         ucx_test_register(suite, test_ucx_logger_new);
         ucx_test_register(suite, test_ucx_logger_log);
         
+        /* UcxArray Tests */
+        ucx_test_register(suite, test_ucx_array_destroy);
+        ucx_test_register(suite, test_ucx_array_new);
+        ucx_test_register(suite, test_ucx_array_at);
+        ucx_test_register(suite, test_ucx_array_append_from);
+        ucx_test_register(suite, test_ucx_array_prepend_from);
+        ucx_test_register(suite, test_ucx_array_set_from);
+        ucx_test_register(suite, test_ucx_array_append);
+        ucx_test_register(suite, test_ucx_array_prepend);
+        ucx_test_register(suite, test_ucx_array_set);
+        ucx_test_register(suite, test_ucx_array_autogrow);
+        ucx_test_register(suite, test_ucx_array_equals);
+        ucx_test_register(suite, test_ucx_array_concat);
+        ucx_test_register(suite, test_ucx_array_find);
+        ucx_test_register(suite, test_ucx_array_contains);
+        ucx_test_register(suite, test_ucx_array_remove);
+        ucx_test_register(suite, test_ucx_array_clone);
+        ucx_test_register(suite, test_ucx_array_sort);
+        ucx_test_register(suite, test_ucx_array_shrink);
+        ucx_test_register(suite, test_ucx_array_resize);
+        ucx_test_register(suite, test_ucx_array_reserve);
+        ucx_test_register(suite, test_ucx_array_util_set);
+        
         /* UcxList Tests */
         ucx_test_register(suite, test_ucx_list_append);
         ucx_test_register(suite, test_ucx_list_prepend);

mercurial